ngpt 1.7.2__py3-none-any.whl → 2.0.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- ngpt/cli.py +3 -23
- ngpt/client.py +30 -139
- {ngpt-1.7.2.dist-info → ngpt-2.0.0.dist-info}/METADATA +6 -3
- ngpt-2.0.0.dist-info/RECORD +9 -0
- ngpt-1.7.2.dist-info/RECORD +0 -9
- {ngpt-1.7.2.dist-info → ngpt-2.0.0.dist-info}/WHEEL +0 -0
- {ngpt-1.7.2.dist-info → ngpt-2.0.0.dist-info}/entry_points.txt +0 -0
- {ngpt-1.7.2.dist-info → ngpt-2.0.0.dist-info}/licenses/LICENSE +0 -0
ngpt/cli.py
CHANGED
@@ -385,38 +385,20 @@ def main():
|
|
385
385
|
print(f"Total configurations: {len(configs)}")
|
386
386
|
print(f"Active configuration index: {args.config_index}")
|
387
387
|
|
388
|
-
# Temporarily initialize a client to get the detected provider for each config
|
389
|
-
def get_detected_provider(config):
|
390
|
-
base_url = config.get('base_url', '')
|
391
|
-
temp_client = NGPTClient(
|
392
|
-
api_key=config.get('api_key', ''),
|
393
|
-
base_url=base_url,
|
394
|
-
provider=config.get('provider', 'N/A'),
|
395
|
-
model=config.get('model', 'N/A')
|
396
|
-
)
|
397
|
-
detected = temp_client._detect_provider_from_url(base_url)
|
398
|
-
return detected if detected else config.get('provider', 'N/A')
|
399
|
-
|
400
388
|
if args.all:
|
401
389
|
# Show details for all configurations
|
402
390
|
print("\nAll configuration details:")
|
403
391
|
for i, cfg in enumerate(configs):
|
404
392
|
active_str = '(Active)' if i == args.config_index else ''
|
405
|
-
detected_provider = get_detected_provider(cfg)
|
406
|
-
provider_str = f" [{detected_provider}]" if detected_provider else ""
|
407
|
-
|
408
393
|
print(f"\n--- Configuration Index {i} {active_str} ---")
|
409
|
-
print(f" API Key: {'[Set]' if cfg.get('api_key') else '[Not Set]'}
|
394
|
+
print(f" API Key: {'[Set]' if cfg.get('api_key') else '[Not Set]'}")
|
410
395
|
print(f" Base URL: {cfg.get('base_url', 'N/A')}")
|
411
396
|
print(f" Provider: {cfg.get('provider', 'N/A')}")
|
412
397
|
print(f" Model: {cfg.get('model', 'N/A')}")
|
413
398
|
else:
|
414
399
|
# Show active config details and summary list
|
415
|
-
detected_provider = get_detected_provider(active_config)
|
416
|
-
provider_str = f" [{detected_provider}]" if detected_provider else ""
|
417
|
-
|
418
400
|
print("\nActive configuration details:")
|
419
|
-
print(f" API Key: {'[Set]' if active_config.get('api_key') else '[Not Set]'}
|
401
|
+
print(f" API Key: {'[Set]' if active_config.get('api_key') else '[Not Set]'}")
|
420
402
|
print(f" Base URL: {active_config.get('base_url', 'N/A')}")
|
421
403
|
print(f" Provider: {active_config.get('provider', 'N/A')}")
|
422
404
|
print(f" Model: {active_config.get('model', 'N/A')}")
|
@@ -425,9 +407,7 @@ def main():
|
|
425
407
|
print("\nAvailable configurations:")
|
426
408
|
for i, cfg in enumerate(configs):
|
427
409
|
active_marker = "*" if i == args.config_index else " "
|
428
|
-
|
429
|
-
detected_str = f" [{detected}]" if detected else ""
|
430
|
-
print(f"[{i}]{active_marker} {cfg.get('provider', 'N/A')} - {cfg.get('model', 'N/A')} ({'[API Key Set]' if cfg.get('api_key') else '[API Key Not Set]'}{detected_str})")
|
410
|
+
print(f"[{i}]{active_marker} {cfg.get('provider', 'N/A')} - {cfg.get('model', 'N/A')} ({'[API Key Set]' if cfg.get('api_key') else '[API Key Not Set]'})")
|
431
411
|
|
432
412
|
return
|
433
413
|
|
ngpt/client.py
CHANGED
@@ -18,65 +18,12 @@ class NGPTClient:
|
|
18
18
|
self.base_url = base_url if base_url.endswith('/') else base_url + '/'
|
19
19
|
self.model = model
|
20
20
|
|
21
|
-
# Detect provider based on base_url domain first
|
22
|
-
detected_provider = self._detect_provider_from_url(self.base_url)
|
23
|
-
self.provider = detected_provider if detected_provider else provider
|
24
|
-
|
25
21
|
# Default headers
|
26
22
|
self.headers = {
|
27
23
|
"Content-Type": "application/json",
|
28
24
|
"Authorization": f"Bearer {self.api_key}"
|
29
25
|
}
|
30
26
|
|
31
|
-
def _detect_provider_from_url(self, url: str) -> str:
|
32
|
-
"""
|
33
|
-
Detect the API provider based on the base URL domain.
|
34
|
-
|
35
|
-
Args:
|
36
|
-
url: The base URL to analyze
|
37
|
-
|
38
|
-
Returns:
|
39
|
-
Detected provider name or empty string if not detected
|
40
|
-
"""
|
41
|
-
import re
|
42
|
-
from urllib.parse import urlparse
|
43
|
-
|
44
|
-
# Extract domain from URL
|
45
|
-
try:
|
46
|
-
parsed_url = urlparse(url)
|
47
|
-
domain = parsed_url.netloc.lower()
|
48
|
-
|
49
|
-
# Check for known API provider domains
|
50
|
-
if 'openai.com' in domain:
|
51
|
-
return 'OpenAI'
|
52
|
-
elif 'anthropic.com' in domain:
|
53
|
-
return 'Anthropic'
|
54
|
-
elif 'generativelanguage.googleapis.com' in domain:
|
55
|
-
return 'Gemini'
|
56
|
-
elif 'groq.com' in domain:
|
57
|
-
return 'Groq'
|
58
|
-
elif 'cohere.com' in domain or 'cohere.ai' in domain:
|
59
|
-
return 'Cohere'
|
60
|
-
elif 'mistral.ai' in domain:
|
61
|
-
return 'Mistral'
|
62
|
-
elif 'claude.ai' in domain:
|
63
|
-
return 'Claude'
|
64
|
-
elif re.match(r'(127\.0\.0\.1|localhost)', domain):
|
65
|
-
# Local APIs, check path components for clues
|
66
|
-
path = parsed_url.path.lower()
|
67
|
-
if '/ollama' in path:
|
68
|
-
return 'Ollama'
|
69
|
-
elif '/llama' in path:
|
70
|
-
return 'Llama'
|
71
|
-
# Default to the explicitly provided provider for local APIs
|
72
|
-
return ''
|
73
|
-
|
74
|
-
# Return empty string if no match found, will fall back to provided provider
|
75
|
-
return ''
|
76
|
-
except Exception:
|
77
|
-
# In case of any parsing error, fall back to provided provider
|
78
|
-
return ''
|
79
|
-
|
80
27
|
def chat(
|
81
28
|
self,
|
82
29
|
prompt: str,
|
@@ -132,26 +79,11 @@ class NGPTClient:
|
|
132
79
|
endpoint = "chat/completions"
|
133
80
|
url = f"{self.base_url}{endpoint}"
|
134
81
|
|
135
|
-
# Set headers based on provider
|
136
|
-
headers = self.headers.copy()
|
137
|
-
provider = self.provider.lower() if hasattr(self, 'provider') else ""
|
138
|
-
|
139
|
-
# Provider-specific customizations
|
140
|
-
if 'gemini' in provider:
|
141
|
-
# Gemini uses X-Goog-Api-Key instead of Bearer token
|
142
|
-
headers = {
|
143
|
-
"Content-Type": "application/json",
|
144
|
-
"X-Goog-Api-Key": self.api_key
|
145
|
-
}
|
146
|
-
|
147
|
-
# Gemini may require a different endpoint or payload structure
|
148
|
-
# This is a simplistic approach - may need further customization
|
149
|
-
|
150
82
|
try:
|
151
83
|
if not stream:
|
152
84
|
# Regular request
|
153
85
|
try:
|
154
|
-
response = requests.post(url, headers=headers, json=payload)
|
86
|
+
response = requests.post(url, headers=self.headers, json=payload)
|
155
87
|
response.raise_for_status() # Raise exception for HTTP errors
|
156
88
|
result = response.json()
|
157
89
|
|
@@ -165,7 +97,7 @@ class NGPTClient:
|
|
165
97
|
else:
|
166
98
|
# Streaming request
|
167
99
|
collected_content = ""
|
168
|
-
with requests.post(url, headers=headers, json=payload, stream=True) as response:
|
100
|
+
with requests.post(url, headers=self.headers, json=payload, stream=True) as response:
|
169
101
|
response.raise_for_status() # Raise exception for HTTP errors
|
170
102
|
|
171
103
|
try:
|
@@ -339,77 +271,36 @@ Code:"""
|
|
339
271
|
if not self.api_key:
|
340
272
|
print("Error: API key is not set. Please configure your API key in the config file or provide it with --api-key.")
|
341
273
|
return []
|
342
|
-
|
343
|
-
provider = getattr(self, 'provider', '').lower()
|
344
|
-
|
345
|
-
# Handle provider-specific API differences
|
346
|
-
if 'gemini' in provider:
|
347
|
-
# Gemini API specific handling
|
348
|
-
# Base URL typically ends with /v1beta or similar - we want to add /models
|
349
|
-
base_url = self.base_url.rstrip('/')
|
350
|
-
if base_url.endswith('/v1beta'):
|
351
|
-
url = f"{base_url}/models"
|
352
|
-
else:
|
353
|
-
url = f"{base_url}/models"
|
354
274
|
|
355
|
-
|
356
|
-
|
357
|
-
|
358
|
-
|
359
|
-
|
275
|
+
# Endpoint for models
|
276
|
+
url = f"{self.base_url}models"
|
277
|
+
|
278
|
+
try:
|
279
|
+
response = requests.get(url, headers=self.headers)
|
280
|
+
response.raise_for_status() # Raise exception for HTTP errors
|
281
|
+
result = response.json()
|
360
282
|
|
361
|
-
|
362
|
-
|
363
|
-
|
364
|
-
|
365
|
-
|
366
|
-
# Gemini API returns models in a different format
|
367
|
-
if "models" in result:
|
368
|
-
# Transform to match our expected format
|
369
|
-
return [{"id": model.get("name", "").split("/")[-1],
|
370
|
-
"owned_by": "Google"}
|
371
|
-
for model in result["models"]]
|
372
|
-
else:
|
373
|
-
print("Error: Unexpected response format when retrieving Gemini models.")
|
374
|
-
return []
|
375
|
-
|
376
|
-
except requests.exceptions.HTTPError as e:
|
377
|
-
print(f"HTTP Error with Gemini API: {e}")
|
283
|
+
if "data" in result:
|
284
|
+
return result["data"]
|
285
|
+
else:
|
286
|
+
print("Error: Unexpected response format when retrieving models.")
|
378
287
|
return []
|
379
288
|
|
380
|
-
|
381
|
-
|
382
|
-
|
383
|
-
|
384
|
-
|
385
|
-
|
289
|
+
except requests.exceptions.HTTPError as e:
|
290
|
+
if e.response.status_code == 401:
|
291
|
+
print("Error: Authentication failed. Please check your API key.")
|
292
|
+
elif e.response.status_code == 404:
|
293
|
+
print(f"Error: Models endpoint not found at {url}")
|
294
|
+
elif e.response.status_code == 429:
|
295
|
+
print("Error: Rate limit exceeded. Please try again later.")
|
296
|
+
else:
|
297
|
+
print(f"HTTP Error: {e}")
|
298
|
+
return []
|
386
299
|
|
387
|
-
|
388
|
-
|
389
|
-
|
390
|
-
|
391
|
-
|
392
|
-
|
393
|
-
|
394
|
-
else:
|
395
|
-
print("Error: Unexpected response format when retrieving models.")
|
396
|
-
return []
|
397
|
-
|
398
|
-
except requests.exceptions.HTTPError as e:
|
399
|
-
if e.response.status_code == 401:
|
400
|
-
print("Error: Authentication failed. Please check your API key.")
|
401
|
-
elif e.response.status_code == 404:
|
402
|
-
print(f"Error: Models endpoint not found at {url}")
|
403
|
-
elif e.response.status_code == 429:
|
404
|
-
print("Error: Rate limit exceeded. Please try again later.")
|
405
|
-
else:
|
406
|
-
print(f"HTTP Error: {e}")
|
407
|
-
return []
|
408
|
-
|
409
|
-
except requests.exceptions.ConnectionError:
|
410
|
-
print(f"Error: Could not connect to {self.base_url}. Please check your internet connection and base URL.")
|
411
|
-
return []
|
412
|
-
|
413
|
-
except Exception as e:
|
414
|
-
print(f"Error: An unexpected error occurred while retrieving models: {e}")
|
415
|
-
return []
|
300
|
+
except requests.exceptions.ConnectionError:
|
301
|
+
print(f"Error: Could not connect to {self.base_url}. Please check your internet connection and base URL.")
|
302
|
+
return []
|
303
|
+
|
304
|
+
except Exception as e:
|
305
|
+
print(f"Error: An unexpected error occurred while retrieving models: {e}")
|
306
|
+
return []
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.4
|
2
2
|
Name: ngpt
|
3
|
-
Version:
|
3
|
+
Version: 2.0.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
|
@@ -121,9 +121,12 @@ ngpt --show-config
|
|
121
121
|
# Show all configurations
|
122
122
|
ngpt --show-config --all
|
123
123
|
|
124
|
-
# List available models for the
|
124
|
+
# List available models for the active configuration
|
125
125
|
ngpt --list-models
|
126
126
|
|
127
|
+
# List models for a specific configuration
|
128
|
+
ngpt --list-models --config-index 1
|
129
|
+
|
127
130
|
# With custom options
|
128
131
|
ngpt --api-key your-key --base-url http://your-endpoint --model your-model "Hello"
|
129
132
|
|
@@ -214,7 +217,7 @@ You can configure the client using the following options:
|
|
214
217
|
| `--api-key` | API key for the service |
|
215
218
|
| `--base-url` | Base URL for the API |
|
216
219
|
| `--model` | Model to use |
|
217
|
-
| `--list-models` | List all available models for the
|
220
|
+
| `--list-models` | List all available models for the selected configuration (can be combined with --config-index) |
|
218
221
|
| `--web-search` | Enable web search capability |
|
219
222
|
| `-n, --no-stream` | Return the whole response without streaming |
|
220
223
|
| `--config` | Path to a custom configuration file or, when used without a value, enters interactive configuration mode |
|
@@ -0,0 +1,9 @@
|
|
1
|
+
ngpt/__init__.py,sha256=ehInP9w0MZlS1vZ1g6Cm4YE1ftmgF72CnEddQ3Le9n4,368
|
2
|
+
ngpt/cli.py,sha256=AyIraZFq7icPot0moqPVJer72iqbtJxKBhy6VH-dwAA,28746
|
3
|
+
ngpt/client.py,sha256=ygtY2xuu-PAFPrz1CUJxcj3hyWw7q2kRG85ClDGClCw,12089
|
4
|
+
ngpt/config.py,sha256=BF0G3QeiPma8l7EQyc37bR7LWZog7FHJQNe7uj9cr4w,6896
|
5
|
+
ngpt-2.0.0.dist-info/METADATA,sha256=goeEJKkrJeTSrluucsXpuyinzJXbpyTSB5_s6fb4gV8,10686
|
6
|
+
ngpt-2.0.0.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
7
|
+
ngpt-2.0.0.dist-info/entry_points.txt,sha256=1cnAMujyy34DlOahrJg19lePSnb08bLbkUs_kVerqdk,39
|
8
|
+
ngpt-2.0.0.dist-info/licenses/LICENSE,sha256=mQkpWoADxbHqE0HRefYLJdm7OpdrXBr3vNv5bZ8w72M,1065
|
9
|
+
ngpt-2.0.0.dist-info/RECORD,,
|
ngpt-1.7.2.dist-info/RECORD
DELETED
@@ -1,9 +0,0 @@
|
|
1
|
-
ngpt/__init__.py,sha256=ehInP9w0MZlS1vZ1g6Cm4YE1ftmgF72CnEddQ3Le9n4,368
|
2
|
-
ngpt/cli.py,sha256=Ba3B4VAs-fnuSdfo22dAe2vmSy3ADVZlKYiDGTy6C6I,29818
|
3
|
-
ngpt/client.py,sha256=qwAmBHSJvadbsvK5YPbY0By1M4bBvxd7j8piP5UdTgE,16553
|
4
|
-
ngpt/config.py,sha256=BF0G3QeiPma8l7EQyc37bR7LWZog7FHJQNe7uj9cr4w,6896
|
5
|
-
ngpt-1.7.2.dist-info/METADATA,sha256=uW2p_2ENm1OJLNW35nZcyDr_L0JvD5eeMrTvWGCPQmg,10568
|
6
|
-
ngpt-1.7.2.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
7
|
-
ngpt-1.7.2.dist-info/entry_points.txt,sha256=1cnAMujyy34DlOahrJg19lePSnb08bLbkUs_kVerqdk,39
|
8
|
-
ngpt-1.7.2.dist-info/licenses/LICENSE,sha256=mQkpWoADxbHqE0HRefYLJdm7OpdrXBr3vNv5bZ8w72M,1065
|
9
|
-
ngpt-1.7.2.dist-info/RECORD,,
|
File without changes
|
File without changes
|
File without changes
|