ngpt 3.1.1__tar.gz → 3.3.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.
Files changed (62) hide show
  1. {ngpt-3.1.1 → ngpt-3.3.0}/PKG-INFO +5 -3
  2. {ngpt-3.1.1 → ngpt-3.3.0}/README.md +2 -2
  3. {ngpt-3.1.1 → ngpt-3.3.0}/ngpt/cli/args.py +1 -1
  4. {ngpt-3.1.1 → ngpt-3.3.0}/ngpt/cli/interactive.py +68 -18
  5. {ngpt-3.1.1 → ngpt-3.3.0}/ngpt/cli/modes/chat.py +18 -2
  6. {ngpt-3.1.1 → ngpt-3.3.0}/ngpt/cli/modes/code.py +18 -3
  7. {ngpt-3.1.1 → ngpt-3.3.0}/ngpt/cli/modes/rewrite.py +17 -2
  8. {ngpt-3.1.1 → ngpt-3.3.0}/ngpt/cli/modes/shell.py +19 -3
  9. {ngpt-3.1.1 → ngpt-3.3.0}/ngpt/cli/modes/text.py +18 -2
  10. {ngpt-3.1.1 → ngpt-3.3.0}/ngpt/cli/renderers.py +45 -10
  11. {ngpt-3.1.1 → ngpt-3.3.0}/ngpt/cli/ui.py +17 -5
  12. {ngpt-3.1.1 → ngpt-3.3.0}/ngpt/client.py +0 -12
  13. {ngpt-3.1.1 → ngpt-3.3.0}/ngpt/utils/__init__.py +7 -1
  14. ngpt-3.3.0/ngpt/utils/web_search.py +270 -0
  15. {ngpt-3.1.1 → ngpt-3.3.0}/pyproject.toml +3 -1
  16. ngpt-3.3.0/uv.lock +796 -0
  17. ngpt-3.1.1/uv.lock +0 -251
  18. {ngpt-3.1.1 → ngpt-3.3.0}/.github/workflows/aur-publish.yml +0 -0
  19. {ngpt-3.1.1 → ngpt-3.3.0}/.github/workflows/python-publish.yml +0 -0
  20. {ngpt-3.1.1 → ngpt-3.3.0}/.gitignore +0 -0
  21. {ngpt-3.1.1 → ngpt-3.3.0}/.python-version +0 -0
  22. {ngpt-3.1.1 → ngpt-3.3.0}/COMMIT_GUIDELINES.md +0 -0
  23. {ngpt-3.1.1 → ngpt-3.3.0}/CONTRIBUTING.md +0 -0
  24. {ngpt-3.1.1 → ngpt-3.3.0}/LICENSE +0 -0
  25. {ngpt-3.1.1 → ngpt-3.3.0}/PKGBUILD +0 -0
  26. {ngpt-3.1.1 → ngpt-3.3.0}/docs/CONTRIBUTING.md +0 -0
  27. {ngpt-3.1.1 → ngpt-3.3.0}/docs/LICENSE.md +0 -0
  28. {ngpt-3.1.1 → ngpt-3.3.0}/docs/README.md +0 -0
  29. {ngpt-3.1.1 → ngpt-3.3.0}/docs/_config.yml +0 -0
  30. {ngpt-3.1.1 → ngpt-3.3.0}/docs/api/README.md +0 -0
  31. {ngpt-3.1.1 → ngpt-3.3.0}/docs/api/cli.md +0 -0
  32. {ngpt-3.1.1 → ngpt-3.3.0}/docs/api/cli_config.md +0 -0
  33. {ngpt-3.1.1 → ngpt-3.3.0}/docs/api/client.md +0 -0
  34. {ngpt-3.1.1 → ngpt-3.3.0}/docs/api/config.md +0 -0
  35. {ngpt-3.1.1 → ngpt-3.3.0}/docs/api/logging.md +0 -0
  36. {ngpt-3.1.1 → ngpt-3.3.0}/docs/assets/css/style.scss +0 -0
  37. {ngpt-3.1.1 → ngpt-3.3.0}/docs/configuration.md +0 -0
  38. {ngpt-3.1.1 → ngpt-3.3.0}/docs/examples/README.md +0 -0
  39. {ngpt-3.1.1 → ngpt-3.3.0}/docs/examples/advanced.md +0 -0
  40. {ngpt-3.1.1 → ngpt-3.3.0}/docs/examples/basic.md +0 -0
  41. {ngpt-3.1.1 → ngpt-3.3.0}/docs/examples/cli_components.md +0 -0
  42. {ngpt-3.1.1 → ngpt-3.3.0}/docs/examples/integrations.md +0 -0
  43. {ngpt-3.1.1 → ngpt-3.3.0}/docs/installation.md +0 -0
  44. {ngpt-3.1.1 → ngpt-3.3.0}/docs/overview.md +0 -0
  45. {ngpt-3.1.1 → ngpt-3.3.0}/docs/usage/README.md +0 -0
  46. {ngpt-3.1.1 → ngpt-3.3.0}/docs/usage/cli_config.md +0 -0
  47. {ngpt-3.1.1 → ngpt-3.3.0}/docs/usage/cli_framework.md +0 -0
  48. {ngpt-3.1.1 → ngpt-3.3.0}/docs/usage/cli_usage.md +0 -0
  49. {ngpt-3.1.1 → ngpt-3.3.0}/docs/usage/gitcommsg.md +0 -0
  50. {ngpt-3.1.1 → ngpt-3.3.0}/docs/usage/library_usage.md +0 -0
  51. {ngpt-3.1.1 → ngpt-3.3.0}/ngpt/__init__.py +0 -0
  52. {ngpt-3.1.1 → ngpt-3.3.0}/ngpt/__main__.py +0 -0
  53. {ngpt-3.1.1 → ngpt-3.3.0}/ngpt/cli/__init__.py +0 -0
  54. {ngpt-3.1.1 → ngpt-3.3.0}/ngpt/cli/config_manager.py +0 -0
  55. {ngpt-3.1.1 → ngpt-3.3.0}/ngpt/cli/formatters.py +0 -0
  56. {ngpt-3.1.1 → ngpt-3.3.0}/ngpt/cli/main.py +0 -0
  57. {ngpt-3.1.1 → ngpt-3.3.0}/ngpt/cli/modes/__init__.py +0 -0
  58. {ngpt-3.1.1 → ngpt-3.3.0}/ngpt/cli/modes/gitcommsg.py +0 -0
  59. {ngpt-3.1.1 → ngpt-3.3.0}/ngpt/utils/cli_config.py +0 -0
  60. {ngpt-3.1.1 → ngpt-3.3.0}/ngpt/utils/config.py +0 -0
  61. {ngpt-3.1.1 → ngpt-3.3.0}/ngpt/utils/log.py +0 -0
  62. {ngpt-3.1.1 → ngpt-3.3.0}/wiki.md +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: ngpt
3
- Version: 3.1.1
3
+ Version: 3.3.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
@@ -28,10 +28,12 @@ Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
28
28
  Classifier: Topic :: Software Development :: Libraries :: Python Modules
29
29
  Classifier: Topic :: Utilities
30
30
  Requires-Python: >=3.8
31
+ Requires-Dist: duckduckgo-search>=3.0.0
31
32
  Requires-Dist: prompt-toolkit>=3.0.0
32
33
  Requires-Dist: pyperclip>=1.8.0
33
34
  Requires-Dist: requests>=2.31.0
34
35
  Requires-Dist: rich>=10.0.0
36
+ Requires-Dist: trafilatura>=1.6.0
35
37
  Description-Content-Type: text/markdown
36
38
 
37
39
  # nGPT
@@ -63,7 +65,7 @@ Description-Content-Type: text/markdown
63
65
  - 🔄 **API Flexibility**: Works with OpenAI, Ollama, Groq, Claude, Gemini, and any compatible endpoint
64
66
  - 💬 **Interactive Chat**: Continuous conversation with memory in modern UI
65
67
  - 📊 **Streaming Responses**: Real-time output for better user experience
66
- - 🔍 **Web Search**: Integrated with compatible API endpoints
68
+ - 🔍 **Web Search**: Enhance any model with contextual information from the web
67
69
  - 📥 **Stdin Processing**: Process piped content by using `{}` placeholder in prompts
68
70
  - 🎨 **Markdown Rendering**: Beautiful formatting of markdown and code with syntax highlighting
69
71
  - ⚡ **Real-time Markdown**: Stream responses with live updating syntax highlighting and formatting
@@ -271,7 +273,7 @@ ngpt --list-models --provider Gemini
271
273
  # With custom options
272
274
  ngpt --api-key your-key --base-url http://your-endpoint --model your-model "Hello"
273
275
 
274
- # Enable web search (if your API endpoint supports it)
276
+ # Enable web search capability to enhance prompts with web information
275
277
  ngpt --web-search "What's the latest news about AI?"
276
278
 
277
279
  # Generate and execute shell commands (using -s or --shell flag)
@@ -27,7 +27,7 @@
27
27
  - 🔄 **API Flexibility**: Works with OpenAI, Ollama, Groq, Claude, Gemini, and any compatible endpoint
28
28
  - 💬 **Interactive Chat**: Continuous conversation with memory in modern UI
29
29
  - 📊 **Streaming Responses**: Real-time output for better user experience
30
- - 🔍 **Web Search**: Integrated with compatible API endpoints
30
+ - 🔍 **Web Search**: Enhance any model with contextual information from the web
31
31
  - 📥 **Stdin Processing**: Process piped content by using `{}` placeholder in prompts
32
32
  - 🎨 **Markdown Rendering**: Beautiful formatting of markdown and code with syntax highlighting
33
33
  - ⚡ **Real-time Markdown**: Stream responses with live updating syntax highlighting and formatting
@@ -235,7 +235,7 @@ ngpt --list-models --provider Gemini
235
235
  # With custom options
236
236
  ngpt --api-key your-key --base-url http://your-endpoint --model your-model "Hello"
237
237
 
238
- # Enable web search (if your API endpoint supports it)
238
+ # Enable web search capability to enhance prompts with web information
239
239
  ngpt --web-search "What's the latest news about AI?"
240
240
 
241
241
  # Generate and execute shell commands (using -s or --shell flag)
@@ -68,7 +68,7 @@ def setup_argument_parser():
68
68
  global_group.add_argument('--model',
69
69
  help='Model to use')
70
70
  global_group.add_argument('--web-search', action='store_true',
71
- help='Enable web search capability (Note: Your API endpoint must support this feature)')
71
+ help='Enable web search capability using DuckDuckGo to enhance prompts with relevant information')
72
72
  global_group.add_argument('--temperature', type=float, default=0.7,
73
73
  help='Set temperature (controls randomness, default: 0.7)')
74
74
  global_group.add_argument('--top_p', type=float, default=1.0,
@@ -6,6 +6,7 @@ import sys
6
6
  import time
7
7
  from .formatters import COLORS
8
8
  from .renderers import prettify_markdown, prettify_streaming_markdown
9
+ from ..utils import enhance_prompt_with_web_search
9
10
 
10
11
  # Optional imports for enhanced UI
11
12
  try:
@@ -64,9 +65,20 @@ def interactive_chat_session(client, web_search=False, no_stream=False, temperat
64
65
  if logger:
65
66
  print(f"{COLORS['green']}Logging conversation to: {logger.get_log_path()}{COLORS['reset']}")
66
67
 
68
+ # Display a note about web search if enabled
69
+ if web_search:
70
+ print(f"{COLORS['green']}Web search capability is enabled.{COLORS['reset']}")
71
+
72
+ # Display a note about markdown rendering only once at the beginning
73
+ if prettify and not no_stream and not stream_prettify:
74
+ print(f"{COLORS['yellow']}Note: Using standard markdown rendering (--prettify). For streaming markdown rendering, use --stream-prettify instead.{COLORS['reset']}")
75
+
67
76
  # Custom separator - use the same length for consistency
68
77
  def print_separator():
69
- print(f"\n{separator}\n")
78
+ # Make sure there's exactly one newline before and after
79
+ # Use sys.stdout.write for direct control, avoiding any extra newlines
80
+ sys.stdout.write(f"\n{separator}\n")
81
+ sys.stdout.flush()
70
82
 
71
83
  # Initialize conversation history
72
84
  system_prompt = preprompt if preprompt else "You are a helpful assistant."
@@ -181,20 +193,49 @@ def interactive_chat_session(client, web_search=False, no_stream=False, temperat
181
193
  # Log user message if logging is enabled
182
194
  if logger:
183
195
  logger.log("user", user_input)
196
+
197
+ # Enhance prompt with web search if enabled
198
+ enhanced_prompt = user_input
199
+ if web_search:
200
+ try:
201
+ print(f"{COLORS['cyan']}Searching the web...{COLORS['reset']}")
202
+ enhanced_prompt = enhance_prompt_with_web_search(user_input, logger=logger)
203
+ print(f"{COLORS['green']}Enhanced input with web search results.{COLORS['reset']}")
204
+
205
+ # Update the user message in conversation with enhanced prompt
206
+ for i in range(len(conversation) - 1, -1, -1):
207
+ if conversation[i]["role"] == "user" and conversation[i]["content"] == user_input:
208
+ conversation[i]["content"] = enhanced_prompt
209
+ break
210
+
211
+ # Log the enhanced prompt if logging is enabled
212
+ if logger:
213
+ # Use "web_search" role instead of "system" for clearer logs
214
+ logger.log("web_search", enhanced_prompt.replace(user_input, "").strip())
215
+ except Exception as e:
216
+ print(f"{COLORS['yellow']}Warning: Failed to enhance prompt with web search: {str(e)}{COLORS['reset']}")
217
+ # Continue with the original prompt if web search fails
184
218
 
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)
219
+ # Print assistant indicator with formatting - but only if we're not going to show a rich formatted box
220
+ # With Rich prettify, no header should be printed as the Rich panel already includes it
221
+ should_print_header = True
222
+
223
+ # Determine if we should print a header based on formatting options
224
+ if prettify and renderer != 'glow':
225
+ # Don't print header for Rich prettify
226
+ should_print_header = False
227
+
228
+ # Print the header if needed
229
+ if should_print_header:
230
+ if not no_stream and not stream_prettify:
231
+ print(f"\n{ngpt_header()}: {COLORS['reset']}", end="", flush=True)
232
+ elif not stream_prettify:
233
+ print(f"\n{ngpt_header()}: {COLORS['reset']}", flush=True)
190
234
 
191
- # If prettify is enabled with regular streaming
235
+ # Determine streaming behavior
192
236
  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
237
  should_stream = False
196
238
  else:
197
- # Regular behavior with stream-prettify taking precedence
198
239
  should_stream = not no_stream
199
240
 
200
241
  # Setup for stream-prettify
@@ -205,9 +246,15 @@ def interactive_chat_session(client, web_search=False, no_stream=False, temperat
205
246
  first_content_received = False
206
247
 
207
248
  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)
249
+ # Don't pass the header_text for rich renderer as it already creates its own header,
250
+ # but pass it for other renderers like glow
251
+ if renderer == 'rich' or renderer == 'auto':
252
+ live_display, stream_callback, setup_spinner = prettify_streaming_markdown(renderer, is_interactive=True)
253
+ else:
254
+ # Get the correct header for interactive mode for non-rich renderers
255
+ header = ngpt_header()
256
+ live_display, stream_callback, setup_spinner = prettify_streaming_markdown(renderer, is_interactive=True, header_text=header)
257
+
211
258
  if not live_display:
212
259
  # Fallback to normal prettify if live display setup failed
213
260
  prettify = True
@@ -229,8 +276,9 @@ def interactive_chat_session(client, web_search=False, no_stream=False, temperat
229
276
  if stop_spinner_func:
230
277
  stop_spinner_func()
231
278
 
232
- # Clear the spinner line completely
233
- sys.stdout.write("\r" + " " * 100 + "\r")
279
+ # Clear the spinner line completely without leaving extra newlines
280
+ # Use direct terminal control to ensure consistency
281
+ sys.stdout.write("\r" + " " * shutil.get_terminal_size().columns + "\r")
234
282
  sys.stdout.flush()
235
283
 
236
284
  # Now start the live display
@@ -250,10 +298,9 @@ def interactive_chat_session(client, web_search=False, no_stream=False, temperat
250
298
 
251
299
  # Get AI response with conversation history
252
300
  response = client.chat(
253
- prompt=user_input,
301
+ prompt=enhanced_prompt,
254
302
  messages=conversation,
255
303
  stream=should_stream,
256
- web_search=web_search,
257
304
  temperature=temperature,
258
305
  top_p=top_p,
259
306
  max_tokens=max_tokens,
@@ -264,7 +311,9 @@ def interactive_chat_session(client, web_search=False, no_stream=False, temperat
264
311
  # Ensure spinner is stopped if no content was received
265
312
  if stop_spinner_event and not first_content_received:
266
313
  stop_spinner_event.set()
267
- sys.stdout.write("\r" + " " * 100 + "\r")
314
+ # Clear the spinner line completely without leaving extra newlines
315
+ # Use direct terminal control to ensure consistency
316
+ sys.stdout.write("\r" + " " * shutil.get_terminal_size().columns + "\r")
268
317
  sys.stdout.flush()
269
318
 
270
319
  # Stop live display if using stream-prettify
@@ -281,6 +330,7 @@ def interactive_chat_session(client, web_search=False, no_stream=False, temperat
281
330
  # Print response if not streamed (either due to no_stream or prettify)
282
331
  if no_stream or prettify:
283
332
  if prettify:
333
+ # For pretty formatting with rich, don't print any header text as the rich renderer already includes it
284
334
  prettify_markdown(response, renderer)
285
335
  else:
286
336
  print(response)
@@ -1,6 +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
5
  import sys
5
6
  import threading
6
7
 
@@ -53,6 +54,21 @@ def chat_mode(client, args, logger=None):
53
54
  # Log the user message if logging is enabled
54
55
  if logger:
55
56
  logger.log("user", prompt)
57
+
58
+ # Enhance prompt with web search if enabled
59
+ if args.web_search:
60
+ try:
61
+ original_prompt = prompt
62
+ prompt = enhance_prompt_with_web_search(prompt, logger=logger)
63
+ print("Enhanced input with web search results.")
64
+
65
+ # Log the enhanced prompt if logging is enabled
66
+ if logger:
67
+ # Use "web_search" role instead of "system" for clearer logs
68
+ logger.log("web_search", prompt.replace(original_prompt, "").strip())
69
+ except Exception as e:
70
+ print(f"{COLORS['yellow']}Warning: Failed to enhance prompt with web search: {str(e)}{COLORS['reset']}")
71
+ # Continue with the original prompt if web search fails
56
72
 
57
73
  # Create messages array with preprompt if available
58
74
  messages = None
@@ -87,7 +103,7 @@ def chat_mode(client, args, logger=None):
87
103
 
88
104
  # If regular prettify is enabled with streaming, inform the user
89
105
  if args.prettify and not args.no_stream:
90
- print(f"{COLORS['yellow']}Note: Streaming disabled to enable markdown rendering.{COLORS['reset']}")
106
+ print(f"{COLORS['yellow']}Note: Using standard markdown rendering (--prettify). For streaming markdown rendering, use --stream-prettify instead.{COLORS['reset']}")
91
107
 
92
108
  # Show a static message if live_display is not available
93
109
  if args.stream_prettify and not live_display:
@@ -123,7 +139,7 @@ def chat_mode(client, args, logger=None):
123
139
  if args.stream_prettify and live_display:
124
140
  stream_callback = spinner_handling_callback
125
141
 
126
- response = client.chat(prompt, stream=should_stream, web_search=args.web_search,
142
+ response = client.chat(prompt, stream=should_stream,
127
143
  temperature=args.temperature, top_p=args.top_p,
128
144
  max_tokens=args.max_tokens, messages=messages,
129
145
  markdown_format=args.prettify or args.stream_prettify,
@@ -1,6 +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
4
+ from ...utils import enhance_prompt_with_web_search
4
5
  import sys
5
6
  import threading
6
7
 
@@ -25,6 +26,21 @@ def code_mode(client, args, logger=None):
25
26
  # Log the user prompt if logging is enabled
26
27
  if logger:
27
28
  logger.log("user", prompt)
29
+
30
+ # Enhance prompt with web search if enabled
31
+ if args.web_search:
32
+ try:
33
+ original_prompt = prompt
34
+ prompt = enhance_prompt_with_web_search(prompt, logger=logger)
35
+ print("Enhanced input with web search results.")
36
+
37
+ # Log the enhanced prompt if logging is enabled
38
+ if logger:
39
+ # Use "web_search" role instead of "system" for clearer logs
40
+ logger.log("web_search", prompt.replace(original_prompt, "").strip())
41
+ except Exception as e:
42
+ print(f"{COLORS['yellow']}Warning: Failed to enhance prompt with web search: {str(e)}{COLORS['reset']}")
43
+ # Continue with the original prompt if web search fails
28
44
 
29
45
  # Setup for streaming and prettify logic
30
46
  stream_callback = None
@@ -62,7 +78,7 @@ def code_mode(client, args, logger=None):
62
78
  if has_markdown_renderer(args.renderer):
63
79
  should_stream = False
64
80
  use_regular_prettify = True
65
- print(f"{COLORS['yellow']}Note: Streaming disabled to enable regular markdown rendering (--prettify).{COLORS['reset']}")
81
+ print(f"{COLORS['yellow']}Note: Using standard markdown rendering (--prettify). For streaming markdown rendering, use --stream-prettify instead.{COLORS['reset']}")
66
82
  else:
67
83
  # Renderer not available for prettify
68
84
  print(f"{COLORS['yellow']}Warning: Renderer '{args.renderer}' not available for --prettify.{COLORS['reset']}")
@@ -110,8 +126,7 @@ def code_mode(client, args, logger=None):
110
126
 
111
127
  generated_code = client.generate_code(
112
128
  prompt=prompt,
113
- language=args.language,
114
- web_search=args.web_search,
129
+ language=args.language,
115
130
  temperature=args.temperature,
116
131
  top_p=args.top_p,
117
132
  max_tokens=args.max_tokens,
@@ -5,6 +5,7 @@ import time
5
5
  from ..formatters import COLORS
6
6
  from ..renderers import prettify_markdown, prettify_streaming_markdown
7
7
  from ..ui import get_multiline_input, spinner
8
+ from ...utils import enhance_prompt_with_web_search
8
9
 
9
10
  # System prompt for rewriting text
10
11
  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.
@@ -125,6 +126,21 @@ def rewrite_mode(client, args, logger=None):
125
126
  print(f"{COLORS['yellow']}Error: Empty input. Please provide text to rewrite.{COLORS['reset']}")
126
127
  return
127
128
 
129
+ # Enhance input with web search if enabled
130
+ if args.web_search:
131
+ try:
132
+ original_text = input_text
133
+ input_text = enhance_prompt_with_web_search(input_text, logger=logger)
134
+ print("Enhanced input with web search results.")
135
+
136
+ # Log the enhanced input if logging is enabled
137
+ if logger:
138
+ # Use "web_search" role instead of "system" for clearer logs
139
+ logger.log("web_search", input_text.replace(original_text, "").strip())
140
+ except Exception as e:
141
+ print(f"{COLORS['yellow']}Warning: Failed to enhance input with web search: {str(e)}{COLORS['reset']}")
142
+ # Continue with the original input if web search fails
143
+
128
144
  # Set up messages array with system prompt and user content
129
145
  messages = [
130
146
  {"role": "system", "content": REWRITE_SYSTEM_PROMPT},
@@ -156,7 +172,7 @@ def rewrite_mode(client, args, logger=None):
156
172
 
157
173
  # If regular prettify is enabled with streaming, inform the user
158
174
  if args.prettify and not args.no_stream:
159
- print(f"{COLORS['yellow']}Note: Streaming disabled to enable markdown rendering.{COLORS['reset']}")
175
+ print(f"{COLORS['yellow']}Note: Using standard markdown rendering (--prettify). For streaming markdown rendering, use --stream-prettify instead.{COLORS['reset']}")
160
176
 
161
177
  # Show a static message if live_display is not available
162
178
  if args.stream_prettify and not live_display:
@@ -195,7 +211,6 @@ def rewrite_mode(client, args, logger=None):
195
211
  response = client.chat(
196
212
  prompt=None, # Not used when messages are provided
197
213
  stream=should_stream,
198
- web_search=args.web_search,
199
214
  temperature=args.temperature,
200
215
  top_p=args.top_p,
201
216
  max_tokens=args.max_tokens,
@@ -1,5 +1,6 @@
1
1
  from ..formatters import COLORS
2
2
  from ..ui import spinner
3
+ from ...utils import enhance_prompt_with_web_search
3
4
  import subprocess
4
5
  import sys
5
6
  import threading
@@ -26,6 +27,21 @@ def shell_mode(client, args, logger=None):
26
27
  if logger:
27
28
  logger.log("user", prompt)
28
29
 
30
+ # Enhance prompt with web search if enabled
31
+ if args.web_search:
32
+ try:
33
+ original_prompt = prompt
34
+ prompt = enhance_prompt_with_web_search(prompt, logger=logger)
35
+ print("Enhanced input with web search results.")
36
+
37
+ # Log the enhanced prompt if logging is enabled
38
+ if logger:
39
+ # Use "web_search" role instead of "system" for clearer logs
40
+ logger.log("web_search", prompt.replace(original_prompt, "").strip())
41
+ except Exception as e:
42
+ print(f"{COLORS['yellow']}Warning: Failed to enhance prompt with web search: {str(e)}{COLORS['reset']}")
43
+ # Continue with the original prompt if web search fails
44
+
29
45
  # Start spinner while waiting for command generation
30
46
  stop_spinner = threading.Event()
31
47
  spinner_thread = threading.Thread(
@@ -37,9 +53,9 @@ def shell_mode(client, args, logger=None):
37
53
  spinner_thread.start()
38
54
 
39
55
  try:
40
- command = client.generate_shell_command(prompt, web_search=args.web_search,
41
- temperature=args.temperature, top_p=args.top_p,
42
- max_tokens=args.max_tokens)
56
+ command = client.generate_shell_command(prompt,
57
+ temperature=args.temperature, top_p=args.top_p,
58
+ max_tokens=args.max_tokens)
43
59
  finally:
44
60
  # Stop the spinner
45
61
  stop_spinner.set()
@@ -1,6 +1,7 @@
1
1
  from ..formatters import COLORS
2
2
  from ..renderers import prettify_markdown, prettify_streaming_markdown
3
3
  from ..ui import get_multiline_input, spinner
4
+ from ...utils import enhance_prompt_with_web_search
4
5
  import threading
5
6
  import sys
6
7
 
@@ -25,6 +26,21 @@ def text_mode(client, args, logger=None):
25
26
  if logger:
26
27
  logger.log("user", prompt)
27
28
 
29
+ # Enhance prompt with web search if enabled
30
+ if args.web_search:
31
+ try:
32
+ original_prompt = prompt
33
+ prompt = enhance_prompt_with_web_search(prompt, logger=logger)
34
+ print("Enhanced input with web search results.")
35
+
36
+ # Log the enhanced prompt if logging is enabled
37
+ if logger:
38
+ # Use "web_search" role instead of "system" for clearer logs
39
+ logger.log("web_search", prompt.replace(original_prompt, "").strip())
40
+ except Exception as e:
41
+ print(f"{COLORS['yellow']}Warning: Failed to enhance prompt with web search: {str(e)}{COLORS['reset']}")
42
+ # Continue with the original prompt if web search fails
43
+
28
44
  # Create messages array with preprompt if available
29
45
  messages = None
30
46
  if args.preprompt:
@@ -58,7 +74,7 @@ def text_mode(client, args, logger=None):
58
74
 
59
75
  # If regular prettify is enabled with streaming, inform the user
60
76
  if args.prettify and not args.no_stream:
61
- print(f"{COLORS['yellow']}Note: Streaming disabled to enable markdown rendering.{COLORS['reset']}")
77
+ print(f"{COLORS['yellow']}Note: Using standard markdown rendering (--prettify). For streaming markdown rendering, use --stream-prettify instead.{COLORS['reset']}")
62
78
 
63
79
  # Show a static message if live_display is not available
64
80
  if args.stream_prettify and not live_display:
@@ -94,7 +110,7 @@ def text_mode(client, args, logger=None):
94
110
  if args.stream_prettify and live_display:
95
111
  stream_callback = spinner_handling_callback
96
112
 
97
- response = client.chat(prompt, stream=should_stream, web_search=args.web_search,
113
+ response = client.chat(prompt, stream=should_stream,
98
114
  temperature=args.temperature, top_p=args.top_p,
99
115
  max_tokens=args.max_tokens, messages=messages,
100
116
  markdown_format=args.prettify or args.stream_prettify,
@@ -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()
@@ -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()