ngpt 2.4.0__tar.gz → 2.5.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-2.4.0 → ngpt-2.5.0}/PKG-INFO +17 -1
- {ngpt-2.4.0 → ngpt-2.5.0}/README.md +13 -0
- {ngpt-2.4.0 → ngpt-2.5.0}/docs/installation.md +16 -0
- {ngpt-2.4.0 → ngpt-2.5.0}/docs/overview.md +2 -0
- {ngpt-2.4.0 → ngpt-2.5.0}/docs/usage/cli_usage.md +49 -2
- {ngpt-2.4.0 → ngpt-2.5.0}/ngpt/cli.py +248 -13
- {ngpt-2.4.0 → ngpt-2.5.0}/pyproject.toml +5 -1
- {ngpt-2.4.0 → ngpt-2.5.0}/uv.lock +63 -1
- {ngpt-2.4.0 → ngpt-2.5.0}/.github/workflows/python-publish.yml +0 -0
- {ngpt-2.4.0 → ngpt-2.5.0}/.gitignore +0 -0
- {ngpt-2.4.0 → ngpt-2.5.0}/.python-version +0 -0
- {ngpt-2.4.0 → ngpt-2.5.0}/COMMIT_GUIDELINES.md +0 -0
- {ngpt-2.4.0 → ngpt-2.5.0}/CONTRIBUTING.md +0 -0
- {ngpt-2.4.0 → ngpt-2.5.0}/LICENSE +0 -0
- {ngpt-2.4.0 → ngpt-2.5.0}/docs/CONTRIBUTING.md +0 -0
- {ngpt-2.4.0 → ngpt-2.5.0}/docs/LICENSE.md +0 -0
- {ngpt-2.4.0 → ngpt-2.5.0}/docs/README.md +0 -0
- {ngpt-2.4.0 → ngpt-2.5.0}/docs/_config.yml +0 -0
- {ngpt-2.4.0 → ngpt-2.5.0}/docs/api/README.md +0 -0
- {ngpt-2.4.0 → ngpt-2.5.0}/docs/api/client.md +0 -0
- {ngpt-2.4.0 → ngpt-2.5.0}/docs/api/config.md +0 -0
- {ngpt-2.4.0 → ngpt-2.5.0}/docs/assets/css/style.scss +0 -0
- {ngpt-2.4.0 → ngpt-2.5.0}/docs/configuration.md +0 -0
- {ngpt-2.4.0 → ngpt-2.5.0}/docs/examples/README.md +0 -0
- {ngpt-2.4.0 → ngpt-2.5.0}/docs/examples/advanced.md +0 -0
- {ngpt-2.4.0 → ngpt-2.5.0}/docs/examples/basic.md +0 -0
- {ngpt-2.4.0 → ngpt-2.5.0}/docs/examples/integrations.md +0 -0
- {ngpt-2.4.0 → ngpt-2.5.0}/docs/usage/README.md +0 -0
- {ngpt-2.4.0 → ngpt-2.5.0}/docs/usage/library_usage.md +0 -0
- {ngpt-2.4.0 → ngpt-2.5.0}/ngpt/__init__.py +0 -0
- {ngpt-2.4.0 → ngpt-2.5.0}/ngpt/client.py +0 -0
- {ngpt-2.4.0 → ngpt-2.5.0}/ngpt/config.py +0 -0
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.4
|
2
2
|
Name: ngpt
|
3
|
-
Version: 2.
|
3
|
+
Version: 2.5.0
|
4
4
|
Summary: A lightweight Python CLI and library for interacting with OpenAI-compatible APIs, supporting both official and self-hosted LLM endpoints.
|
5
5
|
Project-URL: Homepage, https://github.com/nazdridoy/ngpt
|
6
6
|
Project-URL: Repository, https://github.com/nazdridoy/ngpt
|
@@ -30,6 +30,9 @@ Classifier: Topic :: Utilities
|
|
30
30
|
Requires-Python: >=3.8
|
31
31
|
Requires-Dist: prompt-toolkit>=3.0.0
|
32
32
|
Requires-Dist: requests>=2.31.0
|
33
|
+
Requires-Dist: rich>=14.0.0
|
34
|
+
Provides-Extra: prettify
|
35
|
+
Requires-Dist: rich>=10.0.0; extra == 'prettify'
|
33
36
|
Description-Content-Type: text/markdown
|
34
37
|
|
35
38
|
# nGPT
|
@@ -76,9 +79,18 @@ ngpt -n "Tell me about quantum computing"
|
|
76
79
|
# Generate code
|
77
80
|
ngpt --code "function to calculate the Fibonacci sequence"
|
78
81
|
|
82
|
+
# Generate code with syntax highlighting
|
83
|
+
ngpt --code --prettify "function to calculate the Fibonacci sequence"
|
84
|
+
|
79
85
|
# Generate and execute shell commands
|
80
86
|
ngpt --shell "list all files in the current directory"
|
81
87
|
|
88
|
+
# Display markdown responses with beautiful formatting
|
89
|
+
ngpt --prettify "Explain markdown syntax with examples"
|
90
|
+
|
91
|
+
# Use a specific markdown renderer
|
92
|
+
ngpt --prettify --renderer=rich "Create a markdown table"
|
93
|
+
|
82
94
|
# Use multiline editor for complex prompts
|
83
95
|
ngpt --text
|
84
96
|
|
@@ -99,6 +111,7 @@ For more examples and detailed usage, visit the [CLI Usage Guide](https://nazdri
|
|
99
111
|
- 💬 **Interactive Chat**: Continuous conversation with memory in modern UI
|
100
112
|
- 📊 **Streaming Responses**: Real-time output for better user experience
|
101
113
|
- 🔍 **Web Search**: Integrated with compatible API endpoints
|
114
|
+
- 🎨 **Markdown Rendering**: Beautiful formatting of markdown and code with syntax highlighting
|
102
115
|
- ⚙️ **Multiple Configurations**: Cross-platform config system supporting different profiles
|
103
116
|
- 💻 **Shell Command Generation**: OS-aware command execution
|
104
117
|
- 🧩 **Clean Code Generation**: Output code without markdown or explanations
|
@@ -267,6 +280,9 @@ You can configure the client using the following options:
|
|
267
280
|
| `--max_tokens` | Set maximum response length in tokens |
|
268
281
|
| `--preprompt` | Set custom system prompt to control AI behavior |
|
269
282
|
| `--log` | Set filepath to log conversation to (for interactive modes) |
|
283
|
+
| `--prettify` | Render markdown responses and code with syntax highlighting |
|
284
|
+
| `--renderer` | Select which markdown renderer to use with --prettify (auto, rich, or glow) |
|
285
|
+
| `--list-renderers` | Show available markdown renderers for use with --prettify |
|
270
286
|
| `--config` | Path to a custom configuration file or, when used without a value, enters interactive configuration mode |
|
271
287
|
| `--config-index` | Index of the configuration to use (default: 0) |
|
272
288
|
| `--provider` | Provider name to identify the configuration to use (alternative to --config-index) |
|
@@ -42,9 +42,18 @@ ngpt -n "Tell me about quantum computing"
|
|
42
42
|
# Generate code
|
43
43
|
ngpt --code "function to calculate the Fibonacci sequence"
|
44
44
|
|
45
|
+
# Generate code with syntax highlighting
|
46
|
+
ngpt --code --prettify "function to calculate the Fibonacci sequence"
|
47
|
+
|
45
48
|
# Generate and execute shell commands
|
46
49
|
ngpt --shell "list all files in the current directory"
|
47
50
|
|
51
|
+
# Display markdown responses with beautiful formatting
|
52
|
+
ngpt --prettify "Explain markdown syntax with examples"
|
53
|
+
|
54
|
+
# Use a specific markdown renderer
|
55
|
+
ngpt --prettify --renderer=rich "Create a markdown table"
|
56
|
+
|
48
57
|
# Use multiline editor for complex prompts
|
49
58
|
ngpt --text
|
50
59
|
|
@@ -65,6 +74,7 @@ For more examples and detailed usage, visit the [CLI Usage Guide](https://nazdri
|
|
65
74
|
- 💬 **Interactive Chat**: Continuous conversation with memory in modern UI
|
66
75
|
- 📊 **Streaming Responses**: Real-time output for better user experience
|
67
76
|
- 🔍 **Web Search**: Integrated with compatible API endpoints
|
77
|
+
- 🎨 **Markdown Rendering**: Beautiful formatting of markdown and code with syntax highlighting
|
68
78
|
- ⚙️ **Multiple Configurations**: Cross-platform config system supporting different profiles
|
69
79
|
- 💻 **Shell Command Generation**: OS-aware command execution
|
70
80
|
- 🧩 **Clean Code Generation**: Output code without markdown or explanations
|
@@ -233,6 +243,9 @@ You can configure the client using the following options:
|
|
233
243
|
| `--max_tokens` | Set maximum response length in tokens |
|
234
244
|
| `--preprompt` | Set custom system prompt to control AI behavior |
|
235
245
|
| `--log` | Set filepath to log conversation to (for interactive modes) |
|
246
|
+
| `--prettify` | Render markdown responses and code with syntax highlighting |
|
247
|
+
| `--renderer` | Select which markdown renderer to use with --prettify (auto, rich, or glow) |
|
248
|
+
| `--list-renderers` | Show available markdown renderers for use with --prettify |
|
236
249
|
| `--config` | Path to a custom configuration file or, when used without a value, enters interactive configuration mode |
|
237
250
|
| `--config-index` | Index of the configuration to use (default: 0) |
|
238
251
|
| `--provider` | Provider name to identify the configuration to use (alternative to --config-index) |
|
@@ -8,6 +8,10 @@ There are several ways to install nGPT depending on your needs and environment.
|
|
8
8
|
- `requests` library (automatically installed as a dependency)
|
9
9
|
- `prompt_toolkit` library (automatically installed as a dependency)
|
10
10
|
|
11
|
+
## Optional Dependencies
|
12
|
+
|
13
|
+
- `rich` library - For enhanced markdown rendering with syntax highlighting
|
14
|
+
|
11
15
|
## Installing from PyPI (Recommended)
|
12
16
|
|
13
17
|
The simplest way to install nGPT is through the Python Package Index (PyPI):
|
@@ -18,6 +22,18 @@ pip install ngpt
|
|
18
22
|
|
19
23
|
This will install the latest stable release of nGPT and all its dependencies.
|
20
24
|
|
25
|
+
For markdown rendering capabilities, install with the prettify extra:
|
26
|
+
|
27
|
+
```bash
|
28
|
+
pip install ngpt[prettify]
|
29
|
+
```
|
30
|
+
|
31
|
+
Alternatively, you can install the optional dependency separately:
|
32
|
+
|
33
|
+
```bash
|
34
|
+
pip install rich
|
35
|
+
```
|
36
|
+
|
21
37
|
## Installing in a Virtual Environment
|
22
38
|
|
23
39
|
It's often good practice to install Python packages in a virtual environment to avoid conflicts:
|
@@ -18,6 +18,8 @@ nGPT is a lightweight Python library and command-line interface (CLI) tool desig
|
|
18
18
|
|
19
19
|
- **Web Search Integration**: Works with compatible API endpoints that support web search capabilities.
|
20
20
|
|
21
|
+
- **Markdown Rendering**: Beautiful formatting of markdown responses and syntax highlighting for code.
|
22
|
+
|
21
23
|
- **Multiple Configuration Support**: Maintain different API configurations for various services or models.
|
22
24
|
|
23
25
|
- **Shell Command Generation**: Generate OS-aware commands that work on your specific platform.
|
@@ -48,6 +48,9 @@ Here are the most commonly used options:
|
|
48
48
|
| `--temperature` | Set temperature (controls randomness, default: 0.7) |
|
49
49
|
| `--top_p` | Set top_p (controls diversity, default: 1.0) |
|
50
50
|
| `--max_tokens` | Set maximum response length in tokens |
|
51
|
+
| `--prettify` | Render markdown responses and code with syntax highlighting |
|
52
|
+
| `--renderer` | Select which markdown renderer to use (auto, rich, or glow) |
|
53
|
+
| `--list-renderers` | Show available markdown renderers on your system |
|
51
54
|
| `--config-index` | Index of the configuration to use (default: 0) |
|
52
55
|
| `--provider` | Provider name to identify the configuration to use (alternative to --config-index) |
|
53
56
|
|
@@ -140,6 +143,44 @@ This opens an editor where you can:
|
|
140
143
|
- Press Ctrl+D or F10 to submit the text
|
141
144
|
- Press Esc to cancel
|
142
145
|
|
146
|
+
### Markdown Rendering
|
147
|
+
|
148
|
+
Display markdown responses with beautiful formatting and syntax highlighting:
|
149
|
+
|
150
|
+
```bash
|
151
|
+
ngpt --prettify "Explain markdown syntax with examples"
|
152
|
+
```
|
153
|
+
|
154
|
+
This renders the AI's response with proper markdown formatting, including:
|
155
|
+
- Syntax highlighting for code blocks
|
156
|
+
- Proper rendering of tables
|
157
|
+
- Formatted headers, lists, and other markdown elements
|
158
|
+
|
159
|
+
You can specify which markdown renderer to use:
|
160
|
+
|
161
|
+
```bash
|
162
|
+
# Use Rich (Python library) renderer
|
163
|
+
ngpt --prettify --renderer=rich "Create a markdown table comparing programming languages"
|
164
|
+
|
165
|
+
# Use Glow (terminal-based) renderer
|
166
|
+
ngpt --prettify --renderer=glow "Write documentation with code examples"
|
167
|
+
|
168
|
+
# Use automatic selection (default is Rich if available)
|
169
|
+
ngpt --prettify --renderer=auto "Explain blockchain with code examples"
|
170
|
+
```
|
171
|
+
|
172
|
+
Combine with code generation for syntax-highlighted code:
|
173
|
+
|
174
|
+
```bash
|
175
|
+
ngpt -c --prettify "function to calculate the Fibonacci sequence"
|
176
|
+
```
|
177
|
+
|
178
|
+
See available renderers on your system:
|
179
|
+
|
180
|
+
```bash
|
181
|
+
ngpt --list-renderers
|
182
|
+
```
|
183
|
+
|
143
184
|
### Using Web Search
|
144
185
|
|
145
186
|
Enable web search capability (if your API endpoint supports it):
|
@@ -248,8 +289,14 @@ ngpt --model gpt-4o-mini -n "Explain quantum entanglement"
|
|
248
289
|
# Interactive session with custom prompt and logging
|
249
290
|
ngpt -i --preprompt "You are a data science tutor" --log datasci_tutoring.txt
|
250
291
|
|
251
|
-
#
|
252
|
-
ngpt --
|
292
|
+
# Generate code with syntax highlighting
|
293
|
+
ngpt -c --prettify "create a sorting algorithm"
|
294
|
+
|
295
|
+
# Render markdown with web search for up-to-date information
|
296
|
+
ngpt --prettify --web-search "Create a markdown table of recent SpaceX launches"
|
297
|
+
|
298
|
+
# Interactive session with markdown rendering
|
299
|
+
ngpt -i --prettify --renderer=rich
|
253
300
|
```
|
254
301
|
|
255
302
|
### Using a Custom Configuration File
|
@@ -5,6 +5,23 @@ from .client import NGPTClient
|
|
5
5
|
from .config import load_config, get_config_path, load_configs, add_config_entry, remove_config_entry
|
6
6
|
from . import __version__
|
7
7
|
|
8
|
+
# Try to import markdown rendering libraries
|
9
|
+
try:
|
10
|
+
import rich
|
11
|
+
from rich.markdown import Markdown
|
12
|
+
from rich.console import Console
|
13
|
+
HAS_RICH = True
|
14
|
+
except ImportError:
|
15
|
+
HAS_RICH = False
|
16
|
+
|
17
|
+
# Try to import the glow command if available
|
18
|
+
def has_glow_installed():
|
19
|
+
"""Check if glow is installed in the system."""
|
20
|
+
import shutil
|
21
|
+
return shutil.which("glow") is not None
|
22
|
+
|
23
|
+
HAS_GLOW = has_glow_installed()
|
24
|
+
|
8
25
|
# ANSI color codes for terminal output
|
9
26
|
COLORS = {
|
10
27
|
"reset": "\033[0m",
|
@@ -68,6 +85,162 @@ if not HAS_COLOR:
|
|
68
85
|
for key in COLORS:
|
69
86
|
COLORS[key] = ""
|
70
87
|
|
88
|
+
def has_markdown_renderer(renderer='auto'):
|
89
|
+
"""Check if the specified markdown renderer is available.
|
90
|
+
|
91
|
+
Args:
|
92
|
+
renderer (str): Which renderer to check: 'auto', 'rich', or 'glow'
|
93
|
+
|
94
|
+
Returns:
|
95
|
+
bool: True if the renderer is available, False otherwise
|
96
|
+
"""
|
97
|
+
if renderer == 'auto':
|
98
|
+
return HAS_RICH or HAS_GLOW
|
99
|
+
elif renderer == 'rich':
|
100
|
+
return HAS_RICH
|
101
|
+
elif renderer == 'glow':
|
102
|
+
return HAS_GLOW
|
103
|
+
else:
|
104
|
+
return False
|
105
|
+
|
106
|
+
def show_available_renderers():
|
107
|
+
"""Show which markdown renderers are available and their status."""
|
108
|
+
print(f"\n{COLORS['cyan']}{COLORS['bold']}Available Markdown Renderers:{COLORS['reset']}")
|
109
|
+
|
110
|
+
if HAS_GLOW:
|
111
|
+
print(f" {COLORS['green']}✓ Glow{COLORS['reset']} - Terminal-based Markdown renderer")
|
112
|
+
else:
|
113
|
+
print(f" {COLORS['yellow']}✗ Glow{COLORS['reset']} - Not installed (https://github.com/charmbracelet/glow)")
|
114
|
+
|
115
|
+
if HAS_RICH:
|
116
|
+
print(f" {COLORS['green']}✓ Rich{COLORS['reset']} - Python library for terminal formatting (Recommended)")
|
117
|
+
else:
|
118
|
+
print(f" {COLORS['yellow']}✗ Rich{COLORS['reset']} - Not installed (pip install rich)")
|
119
|
+
|
120
|
+
if not HAS_GLOW and not HAS_RICH:
|
121
|
+
print(f"\n{COLORS['yellow']}To enable prettified markdown output, install one of the above renderers.{COLORS['reset']}")
|
122
|
+
else:
|
123
|
+
renderers = []
|
124
|
+
if HAS_RICH:
|
125
|
+
renderers.append("rich")
|
126
|
+
if HAS_GLOW:
|
127
|
+
renderers.append("glow")
|
128
|
+
print(f"\n{COLORS['green']}Usage examples:{COLORS['reset']}")
|
129
|
+
print(f" ngpt --prettify \"Your prompt here\" {COLORS['gray']}# Beautify markdown responses{COLORS['reset']}")
|
130
|
+
print(f" ngpt -c --prettify \"Write a sort function\" {COLORS['gray']}# Syntax highlight generated code{COLORS['reset']}")
|
131
|
+
if renderers:
|
132
|
+
renderer = renderers[0]
|
133
|
+
print(f" ngpt --prettify --renderer={renderer} \"Your prompt\" {COLORS['gray']}# Specify renderer{COLORS['reset']}")
|
134
|
+
|
135
|
+
print("")
|
136
|
+
|
137
|
+
def warn_if_no_markdown_renderer(renderer='auto'):
|
138
|
+
"""Warn the user if the specified markdown renderer is not available.
|
139
|
+
|
140
|
+
Args:
|
141
|
+
renderer (str): Which renderer to check: 'auto', 'rich', or 'glow'
|
142
|
+
|
143
|
+
Returns:
|
144
|
+
bool: True if the renderer is available, False otherwise
|
145
|
+
"""
|
146
|
+
if has_markdown_renderer(renderer):
|
147
|
+
return True
|
148
|
+
|
149
|
+
if renderer == 'auto':
|
150
|
+
print(f"{COLORS['yellow']}Warning: No markdown rendering library available.{COLORS['reset']}")
|
151
|
+
print(f"{COLORS['yellow']}Install 'rich' package with: pip install rich{COLORS['reset']}")
|
152
|
+
print(f"{COLORS['yellow']}Or install 'glow' from https://github.com/charmbracelet/glow{COLORS['reset']}")
|
153
|
+
elif renderer == 'rich':
|
154
|
+
print(f"{COLORS['yellow']}Warning: Rich is not available.{COLORS['reset']}")
|
155
|
+
print(f"{COLORS['yellow']}Install with: pip install rich{COLORS['reset']}")
|
156
|
+
elif renderer == 'glow':
|
157
|
+
print(f"{COLORS['yellow']}Warning: Glow is not available.{COLORS['reset']}")
|
158
|
+
print(f"{COLORS['yellow']}Install from https://github.com/charmbracelet/glow{COLORS['reset']}")
|
159
|
+
else:
|
160
|
+
print(f"{COLORS['yellow']}Error: Invalid renderer '{renderer}'. Use 'auto', 'rich', or 'glow'.{COLORS['reset']}")
|
161
|
+
|
162
|
+
return False
|
163
|
+
|
164
|
+
def prettify_markdown(text, renderer='auto'):
|
165
|
+
"""Render markdown text with beautiful formatting using either Rich or Glow.
|
166
|
+
|
167
|
+
The function handles both general markdown and code blocks with syntax highlighting.
|
168
|
+
For code generation mode, it automatically wraps the code in markdown code blocks.
|
169
|
+
|
170
|
+
Args:
|
171
|
+
text (str): Markdown text to render
|
172
|
+
renderer (str): Which renderer to use: 'auto', 'rich', or 'glow'
|
173
|
+
|
174
|
+
Returns:
|
175
|
+
bool: True if rendering was successful, False otherwise
|
176
|
+
"""
|
177
|
+
# For 'auto', prefer rich if available, otherwise use glow
|
178
|
+
if renderer == 'auto':
|
179
|
+
if HAS_RICH:
|
180
|
+
return prettify_markdown(text, 'rich')
|
181
|
+
elif HAS_GLOW:
|
182
|
+
return prettify_markdown(text, 'glow')
|
183
|
+
else:
|
184
|
+
return False
|
185
|
+
|
186
|
+
# Use glow for rendering
|
187
|
+
elif renderer == 'glow':
|
188
|
+
if not HAS_GLOW:
|
189
|
+
print(f"{COLORS['yellow']}Warning: Glow is not available. Install from https://github.com/charmbracelet/glow{COLORS['reset']}")
|
190
|
+
# Fall back to rich if available
|
191
|
+
if HAS_RICH:
|
192
|
+
print(f"{COLORS['yellow']}Falling back to Rich renderer.{COLORS['reset']}")
|
193
|
+
return prettify_markdown(text, 'rich')
|
194
|
+
return False
|
195
|
+
|
196
|
+
# Use glow
|
197
|
+
import tempfile
|
198
|
+
import subprocess
|
199
|
+
|
200
|
+
with tempfile.NamedTemporaryFile(mode='w', suffix='.md', delete=False) as temp:
|
201
|
+
temp_filename = temp.name
|
202
|
+
temp.write(text)
|
203
|
+
|
204
|
+
try:
|
205
|
+
# Execute glow on the temporary file
|
206
|
+
subprocess.run(["glow", temp_filename], check=True)
|
207
|
+
os.unlink(temp_filename)
|
208
|
+
return True
|
209
|
+
except Exception as e:
|
210
|
+
print(f"{COLORS['yellow']}Error using glow: {str(e)}{COLORS['reset']}")
|
211
|
+
os.unlink(temp_filename)
|
212
|
+
|
213
|
+
# Fall back to rich if available
|
214
|
+
if HAS_RICH:
|
215
|
+
print(f"{COLORS['yellow']}Falling back to Rich renderer.{COLORS['reset']}")
|
216
|
+
return prettify_markdown(text, 'rich')
|
217
|
+
return False
|
218
|
+
|
219
|
+
# Use rich for rendering
|
220
|
+
elif renderer == 'rich':
|
221
|
+
if not HAS_RICH:
|
222
|
+
print(f"{COLORS['yellow']}Warning: Rich is not available. Install with: pip install rich{COLORS['reset']}")
|
223
|
+
# Fall back to glow if available
|
224
|
+
if HAS_GLOW:
|
225
|
+
print(f"{COLORS['yellow']}Falling back to Glow renderer.{COLORS['reset']}")
|
226
|
+
return prettify_markdown(text, 'glow')
|
227
|
+
return False
|
228
|
+
|
229
|
+
# Use rich
|
230
|
+
try:
|
231
|
+
console = Console()
|
232
|
+
md = Markdown(text)
|
233
|
+
console.print(md)
|
234
|
+
return True
|
235
|
+
except Exception as e:
|
236
|
+
print(f"{COLORS['yellow']}Error using rich for markdown: {str(e)}{COLORS['reset']}")
|
237
|
+
return False
|
238
|
+
|
239
|
+
# Invalid renderer specified
|
240
|
+
else:
|
241
|
+
print(f"{COLORS['yellow']}Error: Invalid renderer '{renderer}'. Use 'auto', 'rich', or 'glow'.{COLORS['reset']}")
|
242
|
+
return False
|
243
|
+
|
71
244
|
# Custom help formatter with color support
|
72
245
|
class ColoredHelpFormatter(argparse.HelpFormatter):
|
73
246
|
"""Help formatter that properly handles ANSI color codes without breaking alignment."""
|
@@ -332,7 +505,7 @@ def check_config(config):
|
|
332
505
|
|
333
506
|
return True
|
334
507
|
|
335
|
-
def interactive_chat_session(client, web_search=False, no_stream=False, temperature=0.7, top_p=1.0, max_tokens=None, log_file=None, preprompt=None):
|
508
|
+
def interactive_chat_session(client, web_search=False, no_stream=False, temperature=0.7, top_p=1.0, max_tokens=None, log_file=None, preprompt=None, prettify=False, renderer='auto'):
|
336
509
|
"""Run an interactive chat session with conversation history."""
|
337
510
|
# Get terminal width for better formatting
|
338
511
|
try:
|
@@ -492,11 +665,19 @@ def interactive_chat_session(client, web_search=False, no_stream=False, temperat
|
|
492
665
|
else:
|
493
666
|
print(f"\n{ngpt_header()}: {COLORS['reset']}", flush=True)
|
494
667
|
|
668
|
+
# If prettify is enabled, we need to disable streaming to collect the full response
|
669
|
+
should_stream = not no_stream and not prettify
|
670
|
+
|
671
|
+
# If prettify is enabled with streaming, inform the user
|
672
|
+
if prettify and not no_stream:
|
673
|
+
print(f"\n{COLORS['yellow']}Note: Streaming disabled to enable markdown rendering.{COLORS['reset']}")
|
674
|
+
print(f"\n{ngpt_header()}: {COLORS['reset']}", flush=True)
|
675
|
+
|
495
676
|
# Get AI response with conversation history
|
496
677
|
response = client.chat(
|
497
678
|
prompt=user_input,
|
498
679
|
messages=conversation,
|
499
|
-
stream=
|
680
|
+
stream=should_stream,
|
500
681
|
web_search=web_search,
|
501
682
|
temperature=temperature,
|
502
683
|
top_p=top_p,
|
@@ -508,9 +689,12 @@ def interactive_chat_session(client, web_search=False, no_stream=False, temperat
|
|
508
689
|
assistant_message = {"role": "assistant", "content": response}
|
509
690
|
conversation.append(assistant_message)
|
510
691
|
|
511
|
-
# Print response if not streamed
|
512
|
-
if no_stream:
|
513
|
-
|
692
|
+
# Print response if not streamed (either due to no_stream or prettify)
|
693
|
+
if no_stream or prettify:
|
694
|
+
if prettify:
|
695
|
+
prettify_markdown(response, renderer)
|
696
|
+
else:
|
697
|
+
print(response)
|
514
698
|
|
515
699
|
# Log assistant response if logging is enabled
|
516
700
|
if log_handle:
|
@@ -567,6 +751,7 @@ def main():
|
|
567
751
|
config_group.add_argument('--show-config', action='store_true', help='Show the current configuration(s) and exit')
|
568
752
|
config_group.add_argument('--all', action='store_true', help='Show details for all configurations (requires --show-config)')
|
569
753
|
config_group.add_argument('--list-models', action='store_true', help='List all available models for the current configuration and exit')
|
754
|
+
config_group.add_argument('--list-renderers', action='store_true', help='Show available markdown renderers for use with --prettify')
|
570
755
|
|
571
756
|
# Global options
|
572
757
|
global_group = parser.add_argument_group('Global Options')
|
@@ -587,6 +772,10 @@ def main():
|
|
587
772
|
help='Set filepath to log conversation to (For interactive modes)')
|
588
773
|
global_group.add_argument('--preprompt',
|
589
774
|
help='Set custom system prompt to control AI behavior')
|
775
|
+
global_group.add_argument('--prettify', action='store_const', const='auto',
|
776
|
+
help='Render markdown responses and code with syntax highlighting and formatting')
|
777
|
+
global_group.add_argument('--renderer', choices=['auto', 'rich', 'glow'], default='auto',
|
778
|
+
help='Select which markdown renderer to use with --prettify (auto, rich, or glow)')
|
590
779
|
|
591
780
|
# Mode flags (mutually exclusive)
|
592
781
|
mode_group = parser.add_argument_group('Modes (mutually exclusive)')
|
@@ -609,6 +798,11 @@ def main():
|
|
609
798
|
if args.all and not args.show_config:
|
610
799
|
parser.error("--all can only be used with --show-config")
|
611
800
|
|
801
|
+
# Handle --renderers flag to show available markdown renderers
|
802
|
+
if args.list_renderers:
|
803
|
+
show_available_renderers()
|
804
|
+
return
|
805
|
+
|
612
806
|
# Check for mutual exclusivity between --config-index and --provider
|
613
807
|
if args.config_index != 0 and args.provider:
|
614
808
|
parser.error("--config-index and --provider cannot be used together")
|
@@ -808,6 +1002,17 @@ def main():
|
|
808
1002
|
if not args.show_config and not args.list_models and not check_config(active_config):
|
809
1003
|
return
|
810
1004
|
|
1005
|
+
# Check if --prettify is used but no markdown renderer is available
|
1006
|
+
# This will warn the user immediately if they request prettify but don't have the tools
|
1007
|
+
has_renderer = True
|
1008
|
+
if args.prettify:
|
1009
|
+
has_renderer = warn_if_no_markdown_renderer(args.renderer)
|
1010
|
+
if not has_renderer:
|
1011
|
+
# Set a flag to disable prettify since we already warned the user
|
1012
|
+
print(f"{COLORS['yellow']}Continuing without markdown rendering.{COLORS['reset']}")
|
1013
|
+
show_available_renderers()
|
1014
|
+
args.prettify = False
|
1015
|
+
|
811
1016
|
# Initialize client using the potentially overridden active_config
|
812
1017
|
client = NGPTClient(**active_config)
|
813
1018
|
|
@@ -834,7 +1039,7 @@ def main():
|
|
834
1039
|
# Interactive chat mode
|
835
1040
|
interactive_chat_session(client, web_search=args.web_search, no_stream=args.no_stream,
|
836
1041
|
temperature=args.temperature, top_p=args.top_p,
|
837
|
-
max_tokens=args.max_tokens, log_file=args.log, preprompt=args.preprompt)
|
1042
|
+
max_tokens=args.max_tokens, log_file=args.log, preprompt=args.preprompt, prettify=args.prettify, renderer=args.renderer)
|
838
1043
|
elif args.shell:
|
839
1044
|
if args.prompt is None:
|
840
1045
|
try:
|
@@ -888,7 +1093,13 @@ def main():
|
|
888
1093
|
temperature=args.temperature, top_p=args.top_p,
|
889
1094
|
max_tokens=args.max_tokens)
|
890
1095
|
if generated_code:
|
891
|
-
|
1096
|
+
if args.prettify:
|
1097
|
+
# Format code as markdown with proper syntax highlighting
|
1098
|
+
markdown_code = f"```{args.language}\n{generated_code}\n```"
|
1099
|
+
print("\nGenerated code:")
|
1100
|
+
prettify_markdown(markdown_code, args.renderer)
|
1101
|
+
else:
|
1102
|
+
print(f"\nGenerated code:\n{generated_code}")
|
892
1103
|
|
893
1104
|
elif args.text:
|
894
1105
|
if args.prompt is not None:
|
@@ -1006,12 +1217,24 @@ def main():
|
|
1006
1217
|
{"role": "system", "content": args.preprompt},
|
1007
1218
|
{"role": "user", "content": prompt}
|
1008
1219
|
]
|
1220
|
+
|
1221
|
+
# If prettify is enabled, we need to disable streaming to collect the full response
|
1222
|
+
should_stream = not args.no_stream and not args.prettify
|
1223
|
+
|
1224
|
+
# If prettify is enabled with streaming, inform the user
|
1225
|
+
if args.prettify and not args.no_stream:
|
1226
|
+
print(f"{COLORS['yellow']}Note: Streaming disabled to enable markdown rendering.{COLORS['reset']}")
|
1009
1227
|
|
1010
|
-
response = client.chat(prompt, stream=
|
1228
|
+
response = client.chat(prompt, stream=should_stream, web_search=args.web_search,
|
1011
1229
|
temperature=args.temperature, top_p=args.top_p,
|
1012
1230
|
max_tokens=args.max_tokens, messages=messages)
|
1013
|
-
|
1014
|
-
|
1231
|
+
|
1232
|
+
# Handle non-stream response (either because no_stream was set or prettify forced it)
|
1233
|
+
if (args.no_stream or args.prettify) and response:
|
1234
|
+
if args.prettify:
|
1235
|
+
prettify_markdown(response, args.renderer)
|
1236
|
+
else:
|
1237
|
+
print(response)
|
1015
1238
|
|
1016
1239
|
else:
|
1017
1240
|
# Default to chat mode
|
@@ -1032,12 +1255,24 @@ def main():
|
|
1032
1255
|
{"role": "system", "content": args.preprompt},
|
1033
1256
|
{"role": "user", "content": prompt}
|
1034
1257
|
]
|
1258
|
+
|
1259
|
+
# If prettify is enabled, we need to disable streaming to collect the full response
|
1260
|
+
should_stream = not args.no_stream and not args.prettify
|
1261
|
+
|
1262
|
+
# If prettify is enabled with streaming, inform the user
|
1263
|
+
if args.prettify and not args.no_stream:
|
1264
|
+
print(f"{COLORS['yellow']}Note: Streaming disabled to enable markdown rendering.{COLORS['reset']}")
|
1035
1265
|
|
1036
|
-
response = client.chat(prompt, stream=
|
1266
|
+
response = client.chat(prompt, stream=should_stream, web_search=args.web_search,
|
1037
1267
|
temperature=args.temperature, top_p=args.top_p,
|
1038
1268
|
max_tokens=args.max_tokens, messages=messages)
|
1039
|
-
|
1040
|
-
|
1269
|
+
|
1270
|
+
# Handle non-stream response (either because no_stream was set or prettify forced it)
|
1271
|
+
if (args.no_stream or args.prettify) and response:
|
1272
|
+
if args.prettify:
|
1273
|
+
prettify_markdown(response, args.renderer)
|
1274
|
+
else:
|
1275
|
+
print(response)
|
1041
1276
|
|
1042
1277
|
except KeyboardInterrupt:
|
1043
1278
|
print("\nOperation cancelled by user. Exiting gracefully.")
|
@@ -1,6 +1,6 @@
|
|
1
1
|
[project]
|
2
2
|
name = "ngpt"
|
3
|
-
version = "2.
|
3
|
+
version = "2.5.0"
|
4
4
|
description = "A lightweight Python CLI and library for interacting with OpenAI-compatible APIs, supporting both official and self-hosted LLM endpoints."
|
5
5
|
authors = [
|
6
6
|
{name = "nazDridoy", email = "nazdridoy399@gmail.com"},
|
@@ -8,6 +8,7 @@ authors = [
|
|
8
8
|
dependencies = [
|
9
9
|
"requests>=2.31.0",
|
10
10
|
"prompt_toolkit>=3.0.0",
|
11
|
+
"rich>=14.0.0",
|
11
12
|
]
|
12
13
|
requires-python = ">=3.8"
|
13
14
|
readme = "README.md"
|
@@ -34,6 +35,9 @@ classifiers = [
|
|
34
35
|
"Intended Audience :: System Administrators",
|
35
36
|
]
|
36
37
|
|
38
|
+
[project.optional-dependencies]
|
39
|
+
prettify = ["rich>=10.0.0"]
|
40
|
+
|
37
41
|
[project.urls]
|
38
42
|
"Homepage" = "https://github.com/nazdridoy/ngpt"
|
39
43
|
"Repository" = "https://github.com/nazdridoy/ngpt"
|
@@ -111,20 +111,50 @@ wheels = [
|
|
111
111
|
{ url = "https://files.pythonhosted.org/packages/76/c6/c88e154df9c4e1a2a66ccf0005a88dfb2650c1dffb6f5ce603dfbd452ce3/idna-3.10-py3-none-any.whl", hash = "sha256:946d195a0d259cbba61165e88e65941f16e9b36ea6ddb97f00452bae8b1287d3", size = 70442 },
|
112
112
|
]
|
113
113
|
|
114
|
+
[[package]]
|
115
|
+
name = "markdown-it-py"
|
116
|
+
version = "3.0.0"
|
117
|
+
source = { registry = "https://pypi.org/simple" }
|
118
|
+
dependencies = [
|
119
|
+
{ name = "mdurl" },
|
120
|
+
]
|
121
|
+
sdist = { url = "https://files.pythonhosted.org/packages/38/71/3b932df36c1a044d397a1f92d1cf91ee0a503d91e470cbd670aa66b07ed0/markdown-it-py-3.0.0.tar.gz", hash = "sha256:e3f60a94fa066dc52ec76661e37c851cb232d92f9886b15cb560aaada2df8feb", size = 74596 }
|
122
|
+
wheels = [
|
123
|
+
{ url = "https://files.pythonhosted.org/packages/42/d7/1ec15b46af6af88f19b8e5ffea08fa375d433c998b8a7639e76935c14f1f/markdown_it_py-3.0.0-py3-none-any.whl", hash = "sha256:355216845c60bd96232cd8d8c40e8f9765cc86f46880e43a8fd22dc1a1a8cab1", size = 87528 },
|
124
|
+
]
|
125
|
+
|
126
|
+
[[package]]
|
127
|
+
name = "mdurl"
|
128
|
+
version = "0.1.2"
|
129
|
+
source = { registry = "https://pypi.org/simple" }
|
130
|
+
sdist = { url = "https://files.pythonhosted.org/packages/d6/54/cfe61301667036ec958cb99bd3efefba235e65cdeb9c84d24a8293ba1d90/mdurl-0.1.2.tar.gz", hash = "sha256:bb413d29f5eea38f31dd4754dd7377d4465116fb207585f97bf925588687c1ba", size = 8729 }
|
131
|
+
wheels = [
|
132
|
+
{ url = "https://files.pythonhosted.org/packages/b3/38/89ba8ad64ae25be8de66a6d463314cf1eb366222074cfda9ee839c56a4b4/mdurl-0.1.2-py3-none-any.whl", hash = "sha256:84008a41e51615a49fc9966191ff91509e3c40b939176e643fd50a5c2196b8f8", size = 9979 },
|
133
|
+
]
|
134
|
+
|
114
135
|
[[package]]
|
115
136
|
name = "ngpt"
|
116
|
-
version = "2.
|
137
|
+
version = "2.5.0"
|
117
138
|
source = { editable = "." }
|
118
139
|
dependencies = [
|
119
140
|
{ name = "prompt-toolkit" },
|
120
141
|
{ name = "requests" },
|
142
|
+
{ name = "rich" },
|
143
|
+
]
|
144
|
+
|
145
|
+
[package.optional-dependencies]
|
146
|
+
prettify = [
|
147
|
+
{ name = "rich" },
|
121
148
|
]
|
122
149
|
|
123
150
|
[package.metadata]
|
124
151
|
requires-dist = [
|
125
152
|
{ name = "prompt-toolkit", specifier = ">=3.0.0" },
|
126
153
|
{ name = "requests", specifier = ">=2.31.0" },
|
154
|
+
{ name = "rich", specifier = ">=14.0.0" },
|
155
|
+
{ name = "rich", marker = "extra == 'prettify'", specifier = ">=10.0.0" },
|
127
156
|
]
|
157
|
+
provides-extras = ["prettify"]
|
128
158
|
|
129
159
|
[[package]]
|
130
160
|
name = "prompt-toolkit"
|
@@ -138,6 +168,15 @@ wheels = [
|
|
138
168
|
{ url = "https://files.pythonhosted.org/packages/ce/4f/5249960887b1fbe561d9ff265496d170b55a735b76724f10ef19f9e40716/prompt_toolkit-3.0.51-py3-none-any.whl", hash = "sha256:52742911fde84e2d423e2f9a4cf1de7d7ac4e51958f648d9540e0fb8db077b07", size = 387810 },
|
139
169
|
]
|
140
170
|
|
171
|
+
[[package]]
|
172
|
+
name = "pygments"
|
173
|
+
version = "2.19.1"
|
174
|
+
source = { registry = "https://pypi.org/simple" }
|
175
|
+
sdist = { url = "https://files.pythonhosted.org/packages/7c/2d/c3338d48ea6cc0feb8446d8e6937e1408088a72a39937982cc6111d17f84/pygments-2.19.1.tar.gz", hash = "sha256:61c16d2a8576dc0649d9f39e089b5f02bcd27fba10d8fb4dcc28173f7a45151f", size = 4968581 }
|
176
|
+
wheels = [
|
177
|
+
{ url = "https://files.pythonhosted.org/packages/8a/0b/9fcc47d19c48b59121088dd6da2488a49d5f72dacf8262e2790a1d2c7d15/pygments-2.19.1-py3-none-any.whl", hash = "sha256:9ea1544ad55cecf4b8242fab6dd35a93bbce657034b0611ee383099054ab6d8c", size = 1225293 },
|
178
|
+
]
|
179
|
+
|
141
180
|
[[package]]
|
142
181
|
name = "requests"
|
143
182
|
version = "2.32.3"
|
@@ -154,6 +193,29 @@ wheels = [
|
|
154
193
|
{ url = "https://files.pythonhosted.org/packages/f9/9b/335f9764261e915ed497fcdeb11df5dfd6f7bf257d4a6a2a686d80da4d54/requests-2.32.3-py3-none-any.whl", hash = "sha256:70761cfe03c773ceb22aa2f671b4757976145175cdfca038c02654d061d6dcc6", size = 64928 },
|
155
194
|
]
|
156
195
|
|
196
|
+
[[package]]
|
197
|
+
name = "rich"
|
198
|
+
version = "14.0.0"
|
199
|
+
source = { registry = "https://pypi.org/simple" }
|
200
|
+
dependencies = [
|
201
|
+
{ name = "markdown-it-py" },
|
202
|
+
{ name = "pygments" },
|
203
|
+
{ name = "typing-extensions", marker = "python_full_version < '3.11'" },
|
204
|
+
]
|
205
|
+
sdist = { url = "https://files.pythonhosted.org/packages/a1/53/830aa4c3066a8ab0ae9a9955976fb770fe9c6102117c8ec4ab3ea62d89e8/rich-14.0.0.tar.gz", hash = "sha256:82f1bc23a6a21ebca4ae0c45af9bdbc492ed20231dcb63f297d6d1021a9d5725", size = 224078 }
|
206
|
+
wheels = [
|
207
|
+
{ url = "https://files.pythonhosted.org/packages/0d/9b/63f4c7ebc259242c89b3acafdb37b41d1185c07ff0011164674e9076b491/rich-14.0.0-py3-none-any.whl", hash = "sha256:1c9491e1951aac09caffd42f448ee3d04e58923ffe14993f6e83068dc395d7e0", size = 243229 },
|
208
|
+
]
|
209
|
+
|
210
|
+
[[package]]
|
211
|
+
name = "typing-extensions"
|
212
|
+
version = "4.13.2"
|
213
|
+
source = { registry = "https://pypi.org/simple" }
|
214
|
+
sdist = { url = "https://files.pythonhosted.org/packages/f6/37/23083fcd6e35492953e8d2aaaa68b860eb422b34627b13f2ce3eb6106061/typing_extensions-4.13.2.tar.gz", hash = "sha256:e6c81219bd689f51865d9e372991c540bda33a0379d5573cddb9a3a23f7caaef", size = 106967 }
|
215
|
+
wheels = [
|
216
|
+
{ url = "https://files.pythonhosted.org/packages/8b/54/b1ae86c0973cc6f0210b53d508ca3641fb6d0c56823f288d108bc7ab3cc8/typing_extensions-4.13.2-py3-none-any.whl", hash = "sha256:a439e7c04b49fec3e5d3e2beaa21755cadbbdc391694e28ccdd36ca4a1408f8c", size = 45806 },
|
217
|
+
]
|
218
|
+
|
157
219
|
[[package]]
|
158
220
|
name = "urllib3"
|
159
221
|
version = "2.2.3"
|
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
|