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 +25 -2
- ngpt/cli/modes/code.py +25 -2
- ngpt/cli/modes/interactive.py +25 -3
- ngpt/cli/modes/rewrite.py +25 -2
- ngpt/cli/modes/shell.py +25 -2
- ngpt/cli/modes/text.py +25 -2
- ngpt/utils/web_search.py +50 -14
- {ngpt-3.5.0.dist-info → ngpt-3.5.2.dist-info}/METADATA +5 -6
- {ngpt-3.5.0.dist-info → ngpt-3.5.2.dist-info}/RECORD +12 -12
- {ngpt-3.5.0.dist-info → ngpt-3.5.2.dist-info}/WHEEL +0 -0
- {ngpt-3.5.0.dist-info → ngpt-3.5.2.dist-info}/entry_points.txt +0 -0
- {ngpt-3.5.0.dist-info → ngpt-3.5.2.dist-info}/licenses/LICENSE +0 -0
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
|
-
|
63
|
-
|
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
|
-
|
109
|
-
|
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:
|
ngpt/cli/modes/interactive.py
CHANGED
@@ -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
|
-
|
202
|
-
|
203
|
-
|
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
|
-
|
134
|
-
|
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
|
-
|
64
|
-
|
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
|
-
|
34
|
-
|
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
|
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
|
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
|
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):
|
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
|
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
|
-
|
57
|
-
|
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
|
116
|
-
|
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), '
|
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.
|
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
|
50
|
-
<a href="https://nazdridoy.github.io/ngpt/installation
|
51
|
-
<a href="https://nazdridoy.github.io/ngpt/installation
|
52
|
-
<a href="https://nazdridoy.github.io/ngpt/installation
|
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=
|
13
|
-
ngpt/cli/modes/code.py,sha256=
|
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=
|
16
|
-
ngpt/cli/modes/rewrite.py,sha256=
|
17
|
-
ngpt/cli/modes/shell.py,sha256=
|
18
|
-
ngpt/cli/modes/text.py,sha256=
|
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=
|
24
|
-
ngpt-3.5.
|
25
|
-
ngpt-3.5.
|
26
|
-
ngpt-3.5.
|
27
|
-
ngpt-3.5.
|
28
|
-
ngpt-3.5.
|
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
|
File without changes
|
File without changes
|