ngpt 3.5.0__py3-none-any.whl → 3.5.2__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/modes/chat.py CHANGED
@@ -59,8 +59,31 @@ def chat_mode(client, args, logger=None):
59
59
  if args.web_search:
60
60
  try:
61
61
  original_prompt = prompt
62
- prompt = enhance_prompt_with_web_search(prompt, logger=logger)
63
- print("Enhanced input with web search results.")
62
+
63
+ # Start spinner for web search
64
+ stop_spinner = threading.Event()
65
+ spinner_thread = threading.Thread(
66
+ target=spinner,
67
+ args=("Searching the web for information...",),
68
+ kwargs={"stop_event": stop_spinner, "color": COLORS['cyan']}
69
+ )
70
+ spinner_thread.daemon = True
71
+ spinner_thread.start()
72
+
73
+ try:
74
+ prompt = enhance_prompt_with_web_search(prompt, logger=logger)
75
+ # Stop the spinner
76
+ stop_spinner.set()
77
+ spinner_thread.join()
78
+ # Clear the spinner line completely
79
+ sys.stdout.write("\r" + " " * 100 + "\r")
80
+ sys.stdout.flush()
81
+ print("Enhanced input with web search results.")
82
+ except Exception as e:
83
+ # Stop the spinner before re-raising
84
+ stop_spinner.set()
85
+ spinner_thread.join()
86
+ raise e
64
87
 
65
88
  # Log the enhanced prompt if logging is enabled
66
89
  if logger:
ngpt/cli/modes/code.py CHANGED
@@ -105,8 +105,31 @@ def code_mode(client, args, logger=None):
105
105
  if args.web_search:
106
106
  try:
107
107
  original_prompt = prompt
108
- prompt = enhance_prompt_with_web_search(prompt, logger=logger, disable_citations=True)
109
- print("Enhanced input with web search results.")
108
+
109
+ # Start spinner for web search
110
+ stop_spinner = threading.Event()
111
+ spinner_thread = threading.Thread(
112
+ target=spinner,
113
+ args=("Searching the web for information...",),
114
+ kwargs={"stop_event": stop_spinner, "color": COLORS['cyan']}
115
+ )
116
+ spinner_thread.daemon = True
117
+ spinner_thread.start()
118
+
119
+ try:
120
+ prompt = enhance_prompt_with_web_search(prompt, logger=logger, disable_citations=True)
121
+ # Stop the spinner
122
+ stop_spinner.set()
123
+ spinner_thread.join()
124
+ # Clear the spinner line completely
125
+ sys.stdout.write("\r" + " " * 100 + "\r")
126
+ sys.stdout.flush()
127
+ print("Enhanced input with web search results.")
128
+ except Exception as e:
129
+ # Stop the spinner before re-raising
130
+ stop_spinner.set()
131
+ spinner_thread.join()
132
+ raise e
110
133
 
111
134
  # Log the enhanced prompt if logging is enabled
112
135
  if logger:
@@ -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 ..ui import spinner
9
10
  from ...utils import enhance_prompt_with_web_search
10
11
 
11
12
  # Optional imports for enhanced UI
@@ -198,9 +199,30 @@ def interactive_chat_session(client, web_search=False, no_stream=False, temperat
198
199
  enhanced_prompt = user_input
199
200
  if web_search:
200
201
  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']}")
202
+ # Start spinner for web search
203
+ stop_spinner = threading.Event()
204
+ spinner_thread = threading.Thread(
205
+ target=spinner,
206
+ args=("Searching the web for information...",),
207
+ kwargs={"stop_event": stop_spinner, "color": COLORS['cyan']}
208
+ )
209
+ spinner_thread.daemon = True
210
+ spinner_thread.start()
211
+
212
+ try:
213
+ enhanced_prompt = enhance_prompt_with_web_search(user_input, logger=logger)
214
+ # Stop the spinner
215
+ stop_spinner.set()
216
+ spinner_thread.join()
217
+ # Clear the spinner line completely
218
+ sys.stdout.write("\r" + " " * shutil.get_terminal_size().columns + "\r")
219
+ sys.stdout.flush()
220
+ print(f"{COLORS['green']}Enhanced input with web search results.{COLORS['reset']}")
221
+ except Exception as e:
222
+ # Stop the spinner before re-raising
223
+ stop_spinner.set()
224
+ spinner_thread.join()
225
+ raise e
204
226
 
205
227
  # Update the user message in conversation with enhanced prompt
206
228
  for i in range(len(conversation) - 1, -1, -1):
ngpt/cli/modes/rewrite.py CHANGED
@@ -130,8 +130,31 @@ def rewrite_mode(client, args, logger=None):
130
130
  if args.web_search:
131
131
  try:
132
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.")
133
+
134
+ # Start spinner for web search
135
+ stop_spinner = threading.Event()
136
+ spinner_thread = threading.Thread(
137
+ target=spinner,
138
+ args=("Searching the web for information...",),
139
+ kwargs={"stop_event": stop_spinner, "color": COLORS['cyan']}
140
+ )
141
+ spinner_thread.daemon = True
142
+ spinner_thread.start()
143
+
144
+ try:
145
+ input_text = enhance_prompt_with_web_search(input_text, logger=logger)
146
+ # Stop the spinner
147
+ stop_spinner.set()
148
+ spinner_thread.join()
149
+ # Clear the spinner line completely
150
+ sys.stdout.write("\r" + " " * 100 + "\r")
151
+ sys.stdout.flush()
152
+ print("Enhanced input with web search results.")
153
+ except Exception as e:
154
+ # Stop the spinner before re-raising
155
+ stop_spinner.set()
156
+ spinner_thread.join()
157
+ raise e
135
158
 
136
159
  # Log the enhanced input if logging is enabled
137
160
  if logger:
ngpt/cli/modes/shell.py CHANGED
@@ -60,8 +60,31 @@ def shell_mode(client, args, logger=None):
60
60
  if args.web_search:
61
61
  try:
62
62
  original_prompt = prompt
63
- prompt = enhance_prompt_with_web_search(prompt, logger=logger, disable_citations=True)
64
- print("Enhanced input with web search results.")
63
+
64
+ # Start spinner for web search
65
+ stop_spinner = threading.Event()
66
+ spinner_thread = threading.Thread(
67
+ target=spinner,
68
+ args=("Searching the web for information...",),
69
+ kwargs={"stop_event": stop_spinner, "color": COLORS['cyan']}
70
+ )
71
+ spinner_thread.daemon = True
72
+ spinner_thread.start()
73
+
74
+ try:
75
+ prompt = enhance_prompt_with_web_search(prompt, logger=logger, disable_citations=True)
76
+ # Stop the spinner
77
+ stop_spinner.set()
78
+ spinner_thread.join()
79
+ # Clear the spinner line completely
80
+ sys.stdout.write("\r" + " " * 100 + "\r")
81
+ sys.stdout.flush()
82
+ print("Enhanced input with web search results.")
83
+ except Exception as e:
84
+ # Stop the spinner before re-raising
85
+ stop_spinner.set()
86
+ spinner_thread.join()
87
+ raise e
65
88
 
66
89
  # Log the enhanced prompt if logging is enabled
67
90
  if logger:
ngpt/cli/modes/text.py CHANGED
@@ -30,8 +30,31 @@ def text_mode(client, args, logger=None):
30
30
  if args.web_search:
31
31
  try:
32
32
  original_prompt = prompt
33
- prompt = enhance_prompt_with_web_search(prompt, logger=logger)
34
- print("Enhanced input with web search results.")
33
+
34
+ # Start spinner for web search
35
+ stop_spinner = threading.Event()
36
+ spinner_thread = threading.Thread(
37
+ target=spinner,
38
+ args=("Searching the web for information...",),
39
+ kwargs={"stop_event": stop_spinner, "color": COLORS['cyan']}
40
+ )
41
+ spinner_thread.daemon = True
42
+ spinner_thread.start()
43
+
44
+ try:
45
+ prompt = enhance_prompt_with_web_search(prompt, logger=logger)
46
+ # Stop the spinner
47
+ stop_spinner.set()
48
+ spinner_thread.join()
49
+ # Clear the spinner line completely
50
+ sys.stdout.write("\r" + " " * 100 + "\r")
51
+ sys.stdout.flush()
52
+ print("Enhanced input with web search results.")
53
+ except Exception as e:
54
+ # Stop the spinner before re-raising
55
+ stop_spinner.set()
56
+ spinner_thread.join()
57
+ raise e
35
58
 
36
59
  # Log the enhanced prompt if logging is enabled
37
60
  if logger:
ngpt/utils/web_search.py CHANGED
@@ -1,5 +1,5 @@
1
1
  """
2
- Web search utilities for nGPT using duckduckgo-search and BeautifulSoup4.
2
+ Web search utilities for nGPT using BeautifulSoup4.
3
3
 
4
4
  This module provides functionality to search the web and extract
5
5
  information from search results to enhance AI prompts.
@@ -7,8 +7,7 @@ information from search results to enhance AI prompts.
7
7
 
8
8
  import re
9
9
  from typing import List, Dict, Any, Optional
10
- from duckduckgo_search import DDGS
11
- from urllib.parse import urlparse
10
+ from urllib.parse import urlparse, parse_qs
12
11
  import requests
13
12
  import sys
14
13
  import datetime
@@ -32,17 +31,17 @@ def get_logger():
32
31
  if _logger is not None:
33
32
  return _logger
34
33
  else:
35
- # Default logging to stderr if no logger provided, but only for errors
34
+ # Default logging behavior - suppress all messages to console
36
35
  class DefaultLogger:
37
36
  def info(self, msg): pass # Suppress INFO messages
38
- def error(self, msg): print(f"ERROR: {msg}", file=sys.stderr)
37
+ def error(self, msg): pass # Suppress ERROR messages instead of printing to stderr
39
38
  def warning(self, msg): pass # Suppress WARNING messages
40
39
  def debug(self, msg): pass
41
40
  return DefaultLogger()
42
41
 
43
42
  def perform_web_search(query: str, max_results: int = 5) -> List[Dict[str, Any]]:
44
43
  """
45
- Search the web using DuckDuckGo and return relevant results.
44
+ Search DuckDuckGo directly and return relevant results.
46
45
 
47
46
  Args:
48
47
  query: The search query
@@ -53,8 +52,48 @@ def perform_web_search(query: str, max_results: int = 5) -> List[Dict[str, Any]]
53
52
  """
54
53
  logger = get_logger()
55
54
  try:
56
- ddgs = DDGS()
57
- results = list(ddgs.text(query, max_results=max_results))
55
+ # Headers to mimic a browser request
56
+ headers = {
57
+ 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.0.0 Safari/537.36',
58
+ 'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8',
59
+ }
60
+
61
+ # DuckDuckGo search URL
62
+ encoded_query = requests.utils.quote(query)
63
+ url = f"https://html.duckduckgo.com/html/?q={encoded_query}"
64
+
65
+ # Fetch search results
66
+ response = requests.get(url, headers=headers, timeout=10)
67
+ response.raise_for_status()
68
+
69
+ # Parse HTML response with html.parser (no lxml dependency)
70
+ soup = BeautifulSoup(response.text, 'html.parser')
71
+ results = []
72
+
73
+ # Extract search results
74
+ for result in soup.select('.result')[:max_results]:
75
+ title_elem = result.select_one('.result__title')
76
+ snippet_elem = result.select_one('.result__snippet')
77
+ url_elem = result.select_one('.result__url')
78
+
79
+ # Extract actual URL from DDG's redirect URL if needed
80
+ href = title_elem.find('a')['href'] if title_elem and title_elem.find('a') else None
81
+ if href and href.startswith('/'):
82
+ # Parse DDG redirect URL to get actual URL
83
+ parsed_url = urlparse(href)
84
+ query_params = parse_qs(parsed_url.query)
85
+ actual_url = query_params.get('uddg', [None])[0]
86
+ else:
87
+ actual_url = href
88
+
89
+ # Add result to list
90
+ if title_elem and actual_url:
91
+ results.append({
92
+ 'title': title_elem.get_text(strip=True),
93
+ 'href': actual_url,
94
+ 'body': snippet_elem.get_text(strip=True) if snippet_elem else ''
95
+ })
96
+
58
97
  return results
59
98
  except Exception as e:
60
99
  logger.error(f"Error performing web search: {str(e)}")
@@ -112,18 +151,15 @@ def extract_article_content(url: str, max_chars: int = 5000) -> Optional[str]:
112
151
  # Default to UTF-8 if we can't detect
113
152
  response.encoding = 'utf-8'
114
153
 
115
- # Parse with BeautifulSoup using lxml parser for better results if available
116
- try:
117
- soup = BeautifulSoup(response.text, 'lxml')
118
- except ImportError:
119
- soup = BeautifulSoup(response.text, 'html.parser')
154
+ # Parse with BeautifulSoup using html.parser
155
+ soup = BeautifulSoup(response.text, 'html.parser')
120
156
 
121
157
  # Extract main content using multiple strategies
122
158
  extracted_content = None
123
159
 
124
160
  # ---------- PREPROCESSING ----------
125
161
  # Clone the soup before preprocessing
126
- processed_soup = BeautifulSoup(str(soup), 'lxml' if 'lxml' in str(type(soup.parser)) else 'html.parser')
162
+ processed_soup = BeautifulSoup(str(soup), 'html.parser')
127
163
 
128
164
  # Remove all script, style tags and comments
129
165
  for element in processed_soup.find_all(['script', 'style', 'noscript']):
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: ngpt
3
- Version: 3.5.0
3
+ Version: 3.5.2
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
@@ -29,7 +29,6 @@ Classifier: Topic :: Software Development :: Libraries :: Python Modules
29
29
  Classifier: Topic :: Utilities
30
30
  Requires-Python: >=3.8
31
31
  Requires-Dist: beautifulsoup4>=4.12.0
32
- Requires-Dist: duckduckgo-search>=3.0.0
33
32
  Requires-Dist: prompt-toolkit>=3.0.0
34
33
  Requires-Dist: pyperclip>=1.8.0
35
34
  Requires-Dist: requests>=2.31.0
@@ -46,10 +45,10 @@ Description-Content-Type: text/markdown
46
45
  </p>
47
46
 
48
47
  <p align="center">
49
- <a href="https://nazdridoy.github.io/ngpt/installation/"><img src="https://img.shields.io/badge/Linux-support-blue?logo=linux" alt="Linux"></a>
50
- <a href="https://nazdridoy.github.io/ngpt/installation/"><img src="https://img.shields.io/badge/Windows-support-blue?logo=windows" alt="Windows"></a>
51
- <a href="https://nazdridoy.github.io/ngpt/installation/"><img src="https://img.shields.io/badge/macOS-support-blue?logo=apple" alt="macOS"></a>
52
- <a href="https://nazdridoy.github.io/ngpt/installation/"><img src="https://img.shields.io/badge/Android-Termux-blue?logo=android" alt="Android"></a>
48
+ <a href="https://nazdridoy.github.io/ngpt/installation/#linuxmacos"><img src="https://img.shields.io/badge/Linux-support-blue?logo=linux" alt="Linux"></a>
49
+ <a href="https://nazdridoy.github.io/ngpt/installation/#windows"><img src="https://img.shields.io/badge/Windows-support-blue?logo=windows" alt="Windows"></a>
50
+ <a href="https://nazdridoy.github.io/ngpt/installation/#linuxmacos"><img src="https://img.shields.io/badge/macOS-support-blue?logo=apple" alt="macOS"></a>
51
+ <a href="https://nazdridoy.github.io/ngpt/installation/#android-termux"><img src="https://img.shields.io/badge/Android-Termux-blue?logo=android" alt="Android"></a>
53
52
  </p>
54
53
 
55
54
  🤖 nGPT: A 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. Fast, lightweight, and designed for both casual users and developers.
@@ -9,20 +9,20 @@ ngpt/cli/main.py,sha256=oKX7ryTIrsvQRJHVnH2a763pGyNZthq81wkrRILwHLw,28932
9
9
  ngpt/cli/renderers.py,sha256=m71BeUXKynpKKGXFzwRSW1XngvyKiZ_xEsdujUbU0MA,16597
10
10
  ngpt/cli/ui.py,sha256=HoHDFpLiwMBP5wtMb8YYo244FMiqiPFRoBNcNGp6N0A,7310
11
11
  ngpt/cli/modes/__init__.py,sha256=KP7VR6Xw9k1p5Jcu0F38RDxSFvFIzH3j1ThDLNwznUI,363
12
- ngpt/cli/modes/chat.py,sha256=pv8Ec1l3XsHnW1o3iukxmKg3pK4UJeODl8mC73MH0qg,6927
13
- ngpt/cli/modes/code.py,sha256=ounayP8exu8Bb8QFL4TcyF2d3QI_QlsG5tf6v1FktxE,11470
12
+ ngpt/cli/modes/chat.py,sha256=jfKkrtSkx1gKPsKXDMxZ7BiJiMsCtFHyZCGIdmNQ0fk,7816
13
+ ngpt/cli/modes/code.py,sha256=VYpAHXNLDmrOznTIy4XgQPM7gunSnw3VorvrjPG_1Wg,12359
14
14
  ngpt/cli/modes/gitcommsg.py,sha256=rsfMoeOupmNp-5p5fsMSPAf18BbzXWq-4PF2HjEz6SY,46991
15
- ngpt/cli/modes/interactive.py,sha256=COJABbSgfyVC98lvFD1K2yqKPqyH20vbVrYYh8oiPUk,16395
16
- ngpt/cli/modes/rewrite.py,sha256=ftD-6M9iQ7g4rLdlKyyLTRiJWYtbz64LIG4PIByxmOk,11472
17
- ngpt/cli/modes/shell.py,sha256=9oaOvzKc0VZ0Hjervbzo_kryMlYVZH0IXhc0MaBTYVk,8008
18
- ngpt/cli/modes/text.py,sha256=gdn4opioZ6G3nvfrTkp-dpoD-Of_ZvjVVRggVd6edkg,5528
15
+ ngpt/cli/modes/interactive.py,sha256=TtBrZUX45CVfKOPvkb1ya7dIQhXLILtn7ajmfM9ohso,17419
16
+ ngpt/cli/modes/rewrite.py,sha256=9o37-o5Q8SGfJ91-8pD4h7d5RsJeu85OEdv3nXKzQiA,12361
17
+ ngpt/cli/modes/shell.py,sha256=FaFOzxTxPJCpYsiQJIttS69fZg-asV4LVA5TLaGVN0Y,8897
18
+ ngpt/cli/modes/text.py,sha256=7t5WWXMFxGkBM5HMP4irbN9aQwxE2YgywjiVPep710k,6417
19
19
  ngpt/utils/__init__.py,sha256=qu_66I1Vtav2f1LDiPn5J3DUsbK7o1CSScMcTkYqxoM,1179
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/web_search.py,sha256=_e78fk-6WDVqHDIaAMP9CDoX4FyOWM9XhdSZcr5KasI,29163
24
- ngpt-3.5.0.dist-info/METADATA,sha256=BEcmK1bv4EZ_Q6W7MDnDPo304e-_L0zk1bDgH4mmp8Q,23926
25
- ngpt-3.5.0.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
26
- ngpt-3.5.0.dist-info/entry_points.txt,sha256=SqAAvLhMrsEpkIr4YFRdUeyuXQ9o0IBCeYgE6AVojoI,44
27
- ngpt-3.5.0.dist-info/licenses/LICENSE,sha256=mQkpWoADxbHqE0HRefYLJdm7OpdrXBr3vNv5bZ8w72M,1065
28
- ngpt-3.5.0.dist-info/RECORD,,
23
+ ngpt/utils/web_search.py,sha256=w5ke4KJMRxq7r5jtbUXvspja6XhjoPZloVkZ0IvBXIE,30731
24
+ ngpt-3.5.2.dist-info/METADATA,sha256=sC8YBnPKWY39E1CMiaYzcxW5JWMWkPOoZ05K8KgspuQ,23931
25
+ ngpt-3.5.2.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
26
+ ngpt-3.5.2.dist-info/entry_points.txt,sha256=SqAAvLhMrsEpkIr4YFRdUeyuXQ9o0IBCeYgE6AVojoI,44
27
+ ngpt-3.5.2.dist-info/licenses/LICENSE,sha256=mQkpWoADxbHqE0HRefYLJdm7OpdrXBr3vNv5bZ8w72M,1065
28
+ ngpt-3.5.2.dist-info/RECORD,,
File without changes