ngpt 1.7.0__py3-none-any.whl → 1.7.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.py +23 -3
- ngpt/client.py +139 -30
- {ngpt-1.7.0.dist-info → ngpt-1.7.2.dist-info}/METADATA +1 -1
- ngpt-1.7.2.dist-info/RECORD +9 -0
- ngpt-1.7.0.dist-info/RECORD +0 -9
- {ngpt-1.7.0.dist-info → ngpt-1.7.2.dist-info}/WHEEL +0 -0
- {ngpt-1.7.0.dist-info → ngpt-1.7.2.dist-info}/entry_points.txt +0 -0
- {ngpt-1.7.0.dist-info → ngpt-1.7.2.dist-info}/licenses/LICENSE +0 -0
ngpt/cli.py
CHANGED
@@ -385,20 +385,38 @@ 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
|
+
|
388
400
|
if args.all:
|
389
401
|
# Show details for all configurations
|
390
402
|
print("\nAll configuration details:")
|
391
403
|
for i, cfg in enumerate(configs):
|
392
404
|
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
|
+
|
393
408
|
print(f"\n--- Configuration Index {i} {active_str} ---")
|
394
|
-
print(f" API Key: {'[Set]' if cfg.get('api_key') else '[Not Set]'}")
|
409
|
+
print(f" API Key: {'[Set]' if cfg.get('api_key') else '[Not Set]'}{provider_str}")
|
395
410
|
print(f" Base URL: {cfg.get('base_url', 'N/A')}")
|
396
411
|
print(f" Provider: {cfg.get('provider', 'N/A')}")
|
397
412
|
print(f" Model: {cfg.get('model', 'N/A')}")
|
398
413
|
else:
|
399
414
|
# 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
|
+
|
400
418
|
print("\nActive configuration details:")
|
401
|
-
print(f" API Key: {'[Set]' if active_config.get('api_key') else '[Not Set]'}")
|
419
|
+
print(f" API Key: {'[Set]' if active_config.get('api_key') else '[Not Set]'}{provider_str}")
|
402
420
|
print(f" Base URL: {active_config.get('base_url', 'N/A')}")
|
403
421
|
print(f" Provider: {active_config.get('provider', 'N/A')}")
|
404
422
|
print(f" Model: {active_config.get('model', 'N/A')}")
|
@@ -407,7 +425,9 @@ def main():
|
|
407
425
|
print("\nAvailable configurations:")
|
408
426
|
for i, cfg in enumerate(configs):
|
409
427
|
active_marker = "*" if i == args.config_index else " "
|
410
|
-
|
428
|
+
detected = get_detected_provider(cfg)
|
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})")
|
411
431
|
|
412
432
|
return
|
413
433
|
|
ngpt/client.py
CHANGED
@@ -18,12 +18,65 @@ 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
|
+
|
21
25
|
# Default headers
|
22
26
|
self.headers = {
|
23
27
|
"Content-Type": "application/json",
|
24
28
|
"Authorization": f"Bearer {self.api_key}"
|
25
29
|
}
|
26
30
|
|
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
|
+
|
27
80
|
def chat(
|
28
81
|
self,
|
29
82
|
prompt: str,
|
@@ -79,11 +132,26 @@ class NGPTClient:
|
|
79
132
|
endpoint = "chat/completions"
|
80
133
|
url = f"{self.base_url}{endpoint}"
|
81
134
|
|
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
|
+
|
82
150
|
try:
|
83
151
|
if not stream:
|
84
152
|
# Regular request
|
85
153
|
try:
|
86
|
-
response = requests.post(url, headers=
|
154
|
+
response = requests.post(url, headers=headers, json=payload)
|
87
155
|
response.raise_for_status() # Raise exception for HTTP errors
|
88
156
|
result = response.json()
|
89
157
|
|
@@ -97,7 +165,7 @@ class NGPTClient:
|
|
97
165
|
else:
|
98
166
|
# Streaming request
|
99
167
|
collected_content = ""
|
100
|
-
with requests.post(url, headers=
|
168
|
+
with requests.post(url, headers=headers, json=payload, stream=True) as response:
|
101
169
|
response.raise_for_status() # Raise exception for HTTP errors
|
102
170
|
|
103
171
|
try:
|
@@ -271,36 +339,77 @@ Code:"""
|
|
271
339
|
if not self.api_key:
|
272
340
|
print("Error: API key is not set. Please configure your API key in the config file or provide it with --api-key.")
|
273
341
|
return []
|
274
|
-
|
275
|
-
# Endpoint for models
|
276
|
-
url = f"{self.base_url}models"
|
277
342
|
|
278
|
-
|
279
|
-
|
280
|
-
|
281
|
-
|
282
|
-
|
283
|
-
|
284
|
-
|
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"
|
285
352
|
else:
|
286
|
-
|
353
|
+
url = f"{base_url}/models"
|
354
|
+
|
355
|
+
# Gemini uses X-Goog-Api-Key instead of Bearer token
|
356
|
+
headers = {
|
357
|
+
"Content-Type": "application/json",
|
358
|
+
"X-Goog-Api-Key": self.api_key
|
359
|
+
}
|
360
|
+
|
361
|
+
try:
|
362
|
+
response = requests.get(url, headers=headers)
|
363
|
+
response.raise_for_status()
|
364
|
+
result = response.json()
|
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}")
|
287
378
|
return []
|
288
379
|
|
289
|
-
|
290
|
-
|
291
|
-
|
292
|
-
|
293
|
-
|
294
|
-
|
295
|
-
print("Error: Rate limit exceeded. Please try again later.")
|
296
|
-
else:
|
297
|
-
print(f"HTTP Error: {e}")
|
298
|
-
return []
|
299
|
-
|
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 []
|
380
|
+
except Exception as e:
|
381
|
+
print(f"Error retrieving Gemini models: {e}")
|
382
|
+
return []
|
383
|
+
else:
|
384
|
+
# Standard OpenAI-compatible endpoint
|
385
|
+
url = f"{self.base_url}models"
|
303
386
|
|
304
|
-
|
305
|
-
|
306
|
-
|
387
|
+
try:
|
388
|
+
response = requests.get(url, headers=self.headers)
|
389
|
+
response.raise_for_status() # Raise exception for HTTP errors
|
390
|
+
result = response.json()
|
391
|
+
|
392
|
+
if "data" in result:
|
393
|
+
return result["data"]
|
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 []
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.4
|
2
2
|
Name: ngpt
|
3
|
-
Version: 1.7.
|
3
|
+
Version: 1.7.2
|
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
|
@@ -0,0 +1,9 @@
|
|
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,,
|
ngpt-1.7.0.dist-info/RECORD
DELETED
@@ -1,9 +0,0 @@
|
|
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-1.7.0.dist-info/METADATA,sha256=N0MgHiXxicUgkDTNS6dfCw32CHDmgpnX0N4HpFKm2nE,10568
|
6
|
-
ngpt-1.7.0.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
7
|
-
ngpt-1.7.0.dist-info/entry_points.txt,sha256=1cnAMujyy34DlOahrJg19lePSnb08bLbkUs_kVerqdk,39
|
8
|
-
ngpt-1.7.0.dist-info/licenses/LICENSE,sha256=mQkpWoADxbHqE0HRefYLJdm7OpdrXBr3vNv5bZ8w72M,1065
|
9
|
-
ngpt-1.7.0.dist-info/RECORD,,
|
File without changes
|
File without changes
|
File without changes
|