ngpt 2.3.3__py3-none-any.whl → 2.4.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 +131 -35
- ngpt/client.py +6 -6
- ngpt/config.py +80 -7
- {ngpt-2.3.3.dist-info → ngpt-2.4.0.dist-info}/METADATA +12 -2
- ngpt-2.4.0.dist-info/RECORD +9 -0
- ngpt-2.3.3.dist-info/RECORD +0 -9
- {ngpt-2.3.3.dist-info → ngpt-2.4.0.dist-info}/WHEEL +0 -0
- {ngpt-2.3.3.dist-info → ngpt-2.4.0.dist-info}/entry_points.txt +0 -0
- {ngpt-2.3.3.dist-info → ngpt-2.4.0.dist-info}/licenses/LICENSE +0 -0
ngpt/cli.py
CHANGED
@@ -302,13 +302,20 @@ def show_config_help():
|
|
302
302
|
print(f" 5. {COLORS['cyan']}Use --config-index to specify which configuration to use or edit:{COLORS['reset']}")
|
303
303
|
print(f" {COLORS['yellow']}ngpt --config-index 1 \"Your prompt\"{COLORS['reset']}")
|
304
304
|
|
305
|
-
print(f" 6. {COLORS['cyan']}Use --
|
305
|
+
print(f" 6. {COLORS['cyan']}Use --provider to specify which configuration to use by provider name:{COLORS['reset']}")
|
306
|
+
print(f" {COLORS['yellow']}ngpt --provider Gemini \"Your prompt\"{COLORS['reset']}")
|
307
|
+
|
308
|
+
print(f" 7. {COLORS['cyan']}Use --config without arguments to add a new configuration:{COLORS['reset']}")
|
306
309
|
print(f" {COLORS['yellow']}ngpt --config{COLORS['reset']}")
|
307
|
-
print(f" Or specify an index to edit an existing configuration:")
|
310
|
+
print(f" Or specify an index or provider to edit an existing configuration:")
|
308
311
|
print(f" {COLORS['yellow']}ngpt --config --config-index 1{COLORS['reset']}")
|
309
|
-
print(f"
|
312
|
+
print(f" {COLORS['yellow']}ngpt --config --provider Gemini{COLORS['reset']}")
|
313
|
+
|
314
|
+
print(f" 8. {COLORS['cyan']}Remove a configuration by index or provider:{COLORS['reset']}")
|
310
315
|
print(f" {COLORS['yellow']}ngpt --config --remove --config-index 1{COLORS['reset']}")
|
311
|
-
print(f"
|
316
|
+
print(f" {COLORS['yellow']}ngpt --config --remove --provider Gemini{COLORS['reset']}")
|
317
|
+
|
318
|
+
print(f" 9. {COLORS['cyan']}List available models for the current configuration:{COLORS['reset']}")
|
312
319
|
print(f" {COLORS['yellow']}ngpt --list-models{COLORS['reset']}")
|
313
320
|
|
314
321
|
def check_config(config):
|
@@ -325,7 +332,7 @@ def check_config(config):
|
|
325
332
|
|
326
333
|
return True
|
327
334
|
|
328
|
-
def interactive_chat_session(client, web_search=False, no_stream=False, temperature=0.7, top_p=1.0,
|
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):
|
329
336
|
"""Run an interactive chat session with conversation history."""
|
330
337
|
# Get terminal width for better formatting
|
331
338
|
try:
|
@@ -467,6 +474,7 @@ def interactive_chat_session(client, web_search=False, no_stream=False, temperat
|
|
467
474
|
|
468
475
|
# Skip empty messages but don't raise an error
|
469
476
|
if not user_input.strip():
|
477
|
+
print(f"{COLORS['yellow']}Empty message skipped. Type 'exit' to quit.{COLORS['reset']}")
|
470
478
|
continue
|
471
479
|
|
472
480
|
# Add user message to conversation
|
@@ -492,7 +500,7 @@ def interactive_chat_session(client, web_search=False, no_stream=False, temperat
|
|
492
500
|
web_search=web_search,
|
493
501
|
temperature=temperature,
|
494
502
|
top_p=top_p,
|
495
|
-
max_tokens=
|
503
|
+
max_tokens=max_tokens
|
496
504
|
)
|
497
505
|
|
498
506
|
# Add AI response to conversation history
|
@@ -554,6 +562,7 @@ def main():
|
|
554
562
|
config_group = parser.add_argument_group('Configuration Options')
|
555
563
|
config_group.add_argument('--config', nargs='?', const=True, help='Path to a custom config file or, if no value provided, enter interactive configuration mode to create a new config')
|
556
564
|
config_group.add_argument('--config-index', type=int, default=0, help='Index of the configuration to use or edit (default: 0)')
|
565
|
+
config_group.add_argument('--provider', help='Provider name to identify the configuration to use')
|
557
566
|
config_group.add_argument('--remove', action='store_true', help='Remove the configuration at the specified index (requires --config and --config-index)')
|
558
567
|
config_group.add_argument('--show-config', action='store_true', help='Show the current configuration(s) and exit')
|
559
568
|
config_group.add_argument('--all', action='store_true', help='Show details for all configurations (requires --show-config)')
|
@@ -572,12 +581,12 @@ def main():
|
|
572
581
|
help='Set temperature (controls randomness, default: 0.7)')
|
573
582
|
global_group.add_argument('--top_p', type=float, default=1.0,
|
574
583
|
help='Set top_p (controls diversity, default: 1.0)')
|
575
|
-
global_group.add_argument('--
|
584
|
+
global_group.add_argument('--max_tokens', type=int,
|
576
585
|
help='Set max response length in tokens')
|
577
586
|
global_group.add_argument('--log', metavar='FILE',
|
578
587
|
help='Set filepath to log conversation to (For interactive modes)')
|
579
588
|
global_group.add_argument('--preprompt',
|
580
|
-
help='Set
|
589
|
+
help='Set custom system prompt to control AI behavior')
|
581
590
|
|
582
591
|
# Mode flags (mutually exclusive)
|
583
592
|
mode_group = parser.add_argument_group('Modes (mutually exclusive)')
|
@@ -599,6 +608,10 @@ def main():
|
|
599
608
|
# Validate --all usage
|
600
609
|
if args.all and not args.show_config:
|
601
610
|
parser.error("--all can only be used with --show-config")
|
611
|
+
|
612
|
+
# Check for mutual exclusivity between --config-index and --provider
|
613
|
+
if args.config_index != 0 and args.provider:
|
614
|
+
parser.error("--config-index and --provider cannot be used together")
|
602
615
|
|
603
616
|
# Handle interactive configuration mode
|
604
617
|
if args.config is True: # --config was used without a value
|
@@ -607,20 +620,46 @@ def main():
|
|
607
620
|
# Handle configuration removal if --remove flag is present
|
608
621
|
if args.remove:
|
609
622
|
# Validate that config_index is explicitly provided
|
610
|
-
if '--config-index' not in sys.argv:
|
611
|
-
parser.error("--remove requires explicitly specifying --config-index")
|
623
|
+
if '--config-index' not in sys.argv and not args.provider:
|
624
|
+
parser.error("--remove requires explicitly specifying --config-index or --provider")
|
612
625
|
|
613
626
|
# Show config details before asking for confirmation
|
614
627
|
configs = load_configs(str(config_path))
|
615
628
|
|
629
|
+
# Determine the config index to remove
|
630
|
+
config_index = args.config_index
|
631
|
+
if args.provider:
|
632
|
+
# Find config index by provider name
|
633
|
+
matching_configs = [i for i, cfg in enumerate(configs) if cfg.get('provider', '').lower() == args.provider.lower()]
|
634
|
+
if not matching_configs:
|
635
|
+
print(f"Error: No configuration found for provider '{args.provider}'")
|
636
|
+
return
|
637
|
+
elif len(matching_configs) > 1:
|
638
|
+
print(f"Multiple configurations found for provider '{args.provider}':")
|
639
|
+
for i, idx in enumerate(matching_configs):
|
640
|
+
print(f" [{i}] Index {idx}: {configs[idx].get('model', 'Unknown model')}")
|
641
|
+
|
642
|
+
try:
|
643
|
+
choice = input("Choose a configuration to remove (or press Enter to cancel): ")
|
644
|
+
if choice and choice.isdigit() and 0 <= int(choice) < len(matching_configs):
|
645
|
+
config_index = matching_configs[int(choice)]
|
646
|
+
else:
|
647
|
+
print("Configuration removal cancelled.")
|
648
|
+
return
|
649
|
+
except (ValueError, IndexError, KeyboardInterrupt):
|
650
|
+
print("\nConfiguration removal cancelled.")
|
651
|
+
return
|
652
|
+
else:
|
653
|
+
config_index = matching_configs[0]
|
654
|
+
|
616
655
|
# Check if index is valid
|
617
|
-
if
|
618
|
-
print(f"Error: Configuration index {
|
656
|
+
if config_index < 0 or config_index >= len(configs):
|
657
|
+
print(f"Error: Configuration index {config_index} is out of range. Valid range: 0-{len(configs)-1}")
|
619
658
|
return
|
620
659
|
|
621
660
|
# Show the configuration that will be removed
|
622
|
-
config = configs[
|
623
|
-
print(f"Configuration to remove (index {
|
661
|
+
config = configs[config_index]
|
662
|
+
print(f"Configuration to remove (index {config_index}):")
|
624
663
|
print(f" Provider: {config.get('provider', 'N/A')}")
|
625
664
|
print(f" Model: {config.get('model', 'N/A')}")
|
626
665
|
print(f" Base URL: {config.get('base_url', 'N/A')}")
|
@@ -631,7 +670,7 @@ def main():
|
|
631
670
|
print("\nAre you sure you want to remove this configuration? [y/N] ", end='')
|
632
671
|
response = input().lower()
|
633
672
|
if response in ('y', 'yes'):
|
634
|
-
remove_config_entry(config_path,
|
673
|
+
remove_config_entry(config_path, config_index)
|
635
674
|
else:
|
636
675
|
print("Configuration removal cancelled.")
|
637
676
|
except KeyboardInterrupt:
|
@@ -643,20 +682,51 @@ def main():
|
|
643
682
|
# If --config-index was not explicitly specified, create a new entry by passing None
|
644
683
|
# This will cause add_config_entry to create a new entry at the end of the list
|
645
684
|
# Otherwise, edit the existing config at the specified index
|
646
|
-
config_index = None
|
685
|
+
config_index = None
|
647
686
|
|
648
|
-
#
|
649
|
-
|
650
|
-
|
651
|
-
|
652
|
-
|
687
|
+
# Determine if we're editing an existing config or creating a new one
|
688
|
+
if args.provider:
|
689
|
+
# Find config by provider name
|
690
|
+
configs = load_configs(str(config_path))
|
691
|
+
matching_configs = [i for i, cfg in enumerate(configs) if cfg.get('provider', '').lower() == args.provider.lower()]
|
692
|
+
|
693
|
+
if not matching_configs:
|
694
|
+
print(f"No configuration found for provider '{args.provider}'. Creating a new configuration.")
|
695
|
+
elif len(matching_configs) > 1:
|
696
|
+
print(f"Multiple configurations found for provider '{args.provider}':")
|
697
|
+
for i, idx in enumerate(matching_configs):
|
698
|
+
print(f" [{i}] Index {idx}: {configs[idx].get('model', 'Unknown model')}")
|
699
|
+
|
700
|
+
try:
|
701
|
+
choice = input("Choose a configuration to edit (or press Enter for the first one): ")
|
702
|
+
if choice and choice.isdigit() and 0 <= int(choice) < len(matching_configs):
|
703
|
+
config_index = matching_configs[int(choice)]
|
704
|
+
else:
|
705
|
+
config_index = matching_configs[0]
|
706
|
+
except (ValueError, IndexError, KeyboardInterrupt):
|
707
|
+
config_index = matching_configs[0]
|
708
|
+
else:
|
709
|
+
config_index = matching_configs[0]
|
710
|
+
|
653
711
|
print(f"Editing existing configuration at index {config_index}")
|
712
|
+
elif args.config_index != 0 or '--config-index' in sys.argv:
|
713
|
+
# Check if the index is valid
|
714
|
+
configs = load_configs(str(config_path))
|
715
|
+
if args.config_index >= 0 and args.config_index < len(configs):
|
716
|
+
config_index = args.config_index
|
717
|
+
print(f"Editing existing configuration at index {config_index}")
|
718
|
+
else:
|
719
|
+
print(f"Configuration index {args.config_index} is out of range. Creating a new configuration.")
|
720
|
+
else:
|
721
|
+
# Creating a new config
|
722
|
+
configs = load_configs(str(config_path))
|
723
|
+
print(f"Creating new configuration at index {len(configs)}")
|
654
724
|
|
655
725
|
add_config_entry(config_path, config_index)
|
656
726
|
return
|
657
727
|
|
658
|
-
# Load configuration using the specified index (needed for active config display)
|
659
|
-
active_config = load_config(args.config, args.config_index)
|
728
|
+
# Load configuration using the specified index or provider (needed for active config display)
|
729
|
+
active_config = load_config(args.config, args.config_index, args.provider)
|
660
730
|
|
661
731
|
# Command-line arguments override config settings for active config display
|
662
732
|
# This part is kept to ensure the active config display reflects potential overrides,
|
@@ -675,31 +745,57 @@ def main():
|
|
675
745
|
|
676
746
|
print(f"Configuration file: {config_path}")
|
677
747
|
print(f"Total configurations: {len(configs)}")
|
678
|
-
|
748
|
+
|
749
|
+
# Determine active configuration and display identifier
|
750
|
+
active_identifier = f"index {args.config_index}"
|
751
|
+
if args.provider:
|
752
|
+
active_identifier = f"provider '{args.provider}'"
|
753
|
+
print(f"Active configuration: {active_identifier}")
|
679
754
|
|
680
755
|
if args.all:
|
681
756
|
# Show details for all configurations
|
682
757
|
print("\nAll configuration details:")
|
683
758
|
for i, cfg in enumerate(configs):
|
684
|
-
|
685
|
-
|
759
|
+
provider = cfg.get('provider', 'N/A')
|
760
|
+
active_str = '(Active)' if (
|
761
|
+
(args.provider and provider.lower() == args.provider.lower()) or
|
762
|
+
(not args.provider and i == args.config_index)
|
763
|
+
) else ''
|
764
|
+
print(f"\n--- Configuration Index {i} / Provider: {COLORS['green']}{provider}{COLORS['reset']} {active_str} ---")
|
686
765
|
print(f" API Key: {'[Set]' if cfg.get('api_key') else '[Not Set]'}")
|
687
766
|
print(f" Base URL: {cfg.get('base_url', 'N/A')}")
|
688
|
-
print(f" Provider: {cfg.get('provider', 'N/A')}")
|
689
767
|
print(f" Model: {cfg.get('model', 'N/A')}")
|
690
768
|
else:
|
691
769
|
# Show active config details and summary list
|
692
770
|
print("\nActive configuration details:")
|
771
|
+
print(f" Provider: {COLORS['green']}{active_config.get('provider', 'N/A')}{COLORS['reset']}")
|
693
772
|
print(f" API Key: {'[Set]' if active_config.get('api_key') else '[Not Set]'}")
|
694
773
|
print(f" Base URL: {active_config.get('base_url', 'N/A')}")
|
695
|
-
print(f" Provider: {active_config.get('provider', 'N/A')}")
|
696
774
|
print(f" Model: {active_config.get('model', 'N/A')}")
|
697
775
|
|
698
776
|
if len(configs) > 1:
|
699
777
|
print("\nAvailable configurations:")
|
778
|
+
# Check for duplicate provider names for warning
|
779
|
+
provider_counts = {}
|
780
|
+
for cfg in configs:
|
781
|
+
provider = cfg.get('provider', 'N/A').lower()
|
782
|
+
provider_counts[provider] = provider_counts.get(provider, 0) + 1
|
783
|
+
|
700
784
|
for i, cfg in enumerate(configs):
|
701
|
-
|
702
|
-
|
785
|
+
provider = cfg.get('provider', 'N/A')
|
786
|
+
provider_display = provider
|
787
|
+
# Add warning for duplicate providers
|
788
|
+
if provider_counts.get(provider.lower(), 0) > 1:
|
789
|
+
provider_display = f"{provider} {COLORS['yellow']}(duplicate){COLORS['reset']}"
|
790
|
+
|
791
|
+
active_marker = "*" if (
|
792
|
+
(args.provider and provider.lower() == args.provider.lower()) or
|
793
|
+
(not args.provider and i == args.config_index)
|
794
|
+
) else " "
|
795
|
+
print(f"[{i}]{active_marker} {COLORS['green']}{provider_display}{COLORS['reset']} - {cfg.get('model', 'N/A')} ({'[API Key Set]' if cfg.get('api_key') else '[API Key Not Set]'})")
|
796
|
+
|
797
|
+
# Show instruction for using --provider
|
798
|
+
print(f"\nTip: Use {COLORS['yellow']}--provider NAME{COLORS['reset']} to select a configuration by provider name.")
|
703
799
|
|
704
800
|
return
|
705
801
|
|
@@ -738,7 +834,7 @@ def main():
|
|
738
834
|
# Interactive chat mode
|
739
835
|
interactive_chat_session(client, web_search=args.web_search, no_stream=args.no_stream,
|
740
836
|
temperature=args.temperature, top_p=args.top_p,
|
741
|
-
|
837
|
+
max_tokens=args.max_tokens, log_file=args.log, preprompt=args.preprompt)
|
742
838
|
elif args.shell:
|
743
839
|
if args.prompt is None:
|
744
840
|
try:
|
@@ -752,7 +848,7 @@ def main():
|
|
752
848
|
|
753
849
|
command = client.generate_shell_command(prompt, web_search=args.web_search,
|
754
850
|
temperature=args.temperature, top_p=args.top_p,
|
755
|
-
|
851
|
+
max_tokens=args.max_tokens)
|
756
852
|
if not command:
|
757
853
|
return # Error already printed by client
|
758
854
|
|
@@ -790,7 +886,7 @@ def main():
|
|
790
886
|
|
791
887
|
generated_code = client.generate_code(prompt, args.language, web_search=args.web_search,
|
792
888
|
temperature=args.temperature, top_p=args.top_p,
|
793
|
-
|
889
|
+
max_tokens=args.max_tokens)
|
794
890
|
if generated_code:
|
795
891
|
print(f"\nGenerated code:\n{generated_code}")
|
796
892
|
|
@@ -913,7 +1009,7 @@ def main():
|
|
913
1009
|
|
914
1010
|
response = client.chat(prompt, stream=not args.no_stream, web_search=args.web_search,
|
915
1011
|
temperature=args.temperature, top_p=args.top_p,
|
916
|
-
max_tokens=args.
|
1012
|
+
max_tokens=args.max_tokens, messages=messages)
|
917
1013
|
if args.no_stream and response:
|
918
1014
|
print(response)
|
919
1015
|
|
@@ -939,7 +1035,7 @@ def main():
|
|
939
1035
|
|
940
1036
|
response = client.chat(prompt, stream=not args.no_stream, web_search=args.web_search,
|
941
1037
|
temperature=args.temperature, top_p=args.top_p,
|
942
|
-
max_tokens=args.
|
1038
|
+
max_tokens=args.max_tokens, messages=messages)
|
943
1039
|
if args.no_stream and response:
|
944
1040
|
print(response)
|
945
1041
|
|
ngpt/client.py
CHANGED
@@ -167,7 +167,7 @@ class NGPTClient:
|
|
167
167
|
web_search: bool = False,
|
168
168
|
temperature: float = 0.4,
|
169
169
|
top_p: float = 0.95,
|
170
|
-
|
170
|
+
max_tokens: Optional[int] = None
|
171
171
|
) -> str:
|
172
172
|
"""
|
173
173
|
Generate a shell command based on the prompt.
|
@@ -177,7 +177,7 @@ class NGPTClient:
|
|
177
177
|
web_search: Whether to enable web search capability
|
178
178
|
temperature: Controls randomness in the response
|
179
179
|
top_p: Controls diversity via nucleus sampling
|
180
|
-
|
180
|
+
max_tokens: Maximum number of tokens to generate
|
181
181
|
|
182
182
|
Returns:
|
183
183
|
The generated shell command
|
@@ -228,7 +228,7 @@ Command:"""
|
|
228
228
|
web_search=web_search,
|
229
229
|
temperature=temperature,
|
230
230
|
top_p=top_p,
|
231
|
-
max_tokens=
|
231
|
+
max_tokens=max_tokens
|
232
232
|
)
|
233
233
|
except Exception as e:
|
234
234
|
print(f"Error generating shell command: {e}")
|
@@ -241,7 +241,7 @@ Command:"""
|
|
241
241
|
web_search: bool = False,
|
242
242
|
temperature: float = 0.4,
|
243
243
|
top_p: float = 0.95,
|
244
|
-
|
244
|
+
max_tokens: Optional[int] = None
|
245
245
|
) -> str:
|
246
246
|
"""
|
247
247
|
Generate code based on the prompt.
|
@@ -252,7 +252,7 @@ Command:"""
|
|
252
252
|
web_search: Whether to enable web search capability
|
253
253
|
temperature: Controls randomness in the response
|
254
254
|
top_p: Controls diversity via nucleus sampling
|
255
|
-
|
255
|
+
max_tokens: Maximum number of tokens to generate
|
256
256
|
|
257
257
|
Returns:
|
258
258
|
The generated code
|
@@ -285,7 +285,7 @@ Code:"""
|
|
285
285
|
web_search=web_search,
|
286
286
|
temperature=temperature,
|
287
287
|
top_p=top_p,
|
288
|
-
max_tokens=
|
288
|
+
max_tokens=max_tokens
|
289
289
|
)
|
290
290
|
except Exception as e:
|
291
291
|
print(f"Error generating code: {e}")
|
ngpt/config.py
CHANGED
@@ -75,9 +75,29 @@ def add_config_entry(config_path: Path, config_index: Optional[int] = None) -> N
|
|
75
75
|
if user_input:
|
76
76
|
entry["base_url"] = user_input
|
77
77
|
|
78
|
-
|
79
|
-
|
80
|
-
|
78
|
+
# For provider, check for uniqueness when creating new config
|
79
|
+
provider_unique = False
|
80
|
+
original_provider = entry['provider']
|
81
|
+
while not provider_unique:
|
82
|
+
user_input = input(f"Provider [{entry['provider']}]: ")
|
83
|
+
if user_input:
|
84
|
+
provider = user_input
|
85
|
+
else:
|
86
|
+
provider = entry['provider']
|
87
|
+
|
88
|
+
# When creating new config or changing provider, check uniqueness
|
89
|
+
if is_existing_config and provider.lower() == original_provider.lower():
|
90
|
+
# No change in provider name, so keep it
|
91
|
+
provider_unique = True
|
92
|
+
elif is_provider_unique(configs, provider, config_index if is_existing_config else None):
|
93
|
+
provider_unique = True
|
94
|
+
else:
|
95
|
+
print(f"Error: Provider '{provider}' already exists. Please choose a unique provider name.")
|
96
|
+
# If it's the existing provider, allow keeping it (for existing configs)
|
97
|
+
if is_existing_config and provider.lower() == original_provider.lower():
|
98
|
+
provider_unique = True
|
99
|
+
|
100
|
+
entry["provider"] = provider
|
81
101
|
|
82
102
|
user_input = input(f"Model [{entry['model']}]: ")
|
83
103
|
if user_input:
|
@@ -127,13 +147,46 @@ def load_configs(custom_path: Optional[str] = None) -> List[Dict[str, Any]]:
|
|
127
147
|
|
128
148
|
return configs
|
129
149
|
|
130
|
-
def load_config(custom_path: Optional[str] = None, config_index: int = 0) -> Dict[str, Any]:
|
150
|
+
def load_config(custom_path: Optional[str] = None, config_index: int = 0, provider: Optional[str] = None) -> Dict[str, Any]:
|
131
151
|
"""
|
132
|
-
Load a specific configuration by index and apply environment variables.
|
152
|
+
Load a specific configuration by index or provider name and apply environment variables.
|
133
153
|
Environment variables take precedence over the config file.
|
154
|
+
|
155
|
+
Args:
|
156
|
+
custom_path: Optional path to a custom config file
|
157
|
+
config_index: Index of the configuration to use (default: 0)
|
158
|
+
provider: Provider name to identify the configuration
|
159
|
+
|
160
|
+
Returns:
|
161
|
+
The selected configuration with environment variables applied
|
134
162
|
"""
|
135
163
|
configs = load_configs(custom_path)
|
136
164
|
|
165
|
+
# If provider is specified, try to find a matching config
|
166
|
+
if provider:
|
167
|
+
matching_configs = [i for i, cfg in enumerate(configs) if cfg.get('provider', '').lower() == provider.lower()]
|
168
|
+
|
169
|
+
if not matching_configs:
|
170
|
+
print(f"Warning: No configuration found for provider '{provider}'. Using default configuration.")
|
171
|
+
config_index = 0
|
172
|
+
elif len(matching_configs) > 1:
|
173
|
+
print(f"Warning: Multiple configurations found for provider '{provider}'.")
|
174
|
+
for i, idx in enumerate(matching_configs):
|
175
|
+
print(f" [{i}] Index {idx}: {configs[idx].get('model', 'Unknown model')}")
|
176
|
+
|
177
|
+
try:
|
178
|
+
choice = input("Choose a configuration (or press Enter for the first one): ")
|
179
|
+
if choice and choice.isdigit() and 0 <= int(choice) < len(matching_configs):
|
180
|
+
config_index = matching_configs[int(choice)]
|
181
|
+
else:
|
182
|
+
config_index = matching_configs[0]
|
183
|
+
print(f"Using first matching configuration (index {config_index}).")
|
184
|
+
except (ValueError, IndexError, KeyboardInterrupt):
|
185
|
+
config_index = matching_configs[0]
|
186
|
+
print(f"Using first matching configuration (index {config_index}).")
|
187
|
+
else:
|
188
|
+
config_index = matching_configs[0]
|
189
|
+
|
137
190
|
# If config_index is out of range, use the first config
|
138
191
|
if config_index < 0 or config_index >= len(configs):
|
139
192
|
if len(configs) > 0:
|
@@ -157,7 +210,7 @@ def load_config(custom_path: Optional[str] = None, config_index: int = 0) -> Dic
|
|
157
210
|
if env_var in os.environ and os.environ[env_var]:
|
158
211
|
config[config_key] = os.environ[env_var]
|
159
212
|
|
160
|
-
return config
|
213
|
+
return config
|
161
214
|
|
162
215
|
def remove_config_entry(config_path: Path, config_index: int) -> bool:
|
163
216
|
"""
|
@@ -182,4 +235,24 @@ def remove_config_entry(config_path: Path, config_index: int) -> bool:
|
|
182
235
|
return True
|
183
236
|
except Exception as e:
|
184
237
|
print(f"Error saving configuration: {e}")
|
185
|
-
return False
|
238
|
+
return False
|
239
|
+
|
240
|
+
def is_provider_unique(configs: List[Dict[str, Any]], provider: str, exclude_index: Optional[int] = None) -> bool:
|
241
|
+
"""
|
242
|
+
Check if a provider name is unique among configurations.
|
243
|
+
|
244
|
+
Args:
|
245
|
+
configs: List of configuration dictionaries
|
246
|
+
provider: Provider name to check
|
247
|
+
exclude_index: Optional index to exclude from the check (for updating existing config)
|
248
|
+
|
249
|
+
Returns:
|
250
|
+
True if the provider name is unique, False otherwise
|
251
|
+
"""
|
252
|
+
provider = provider.lower()
|
253
|
+
for i, cfg in enumerate(configs):
|
254
|
+
if i == exclude_index:
|
255
|
+
continue
|
256
|
+
if cfg.get('provider', '').lower() == provider:
|
257
|
+
return False
|
258
|
+
return True
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.4
|
2
2
|
Name: ngpt
|
3
|
-
Version: 2.
|
3
|
+
Version: 2.4.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
|
@@ -264,11 +264,12 @@ You can configure the client using the following options:
|
|
264
264
|
| `-n, --no-stream` | Return the whole response without streaming |
|
265
265
|
| `--temperature` | Set temperature (controls randomness, default: 0.7) |
|
266
266
|
| `--top_p` | Set top_p (controls diversity, default: 1.0) |
|
267
|
-
| `--
|
267
|
+
| `--max_tokens` | Set maximum response length in tokens |
|
268
268
|
| `--preprompt` | Set custom system prompt to control AI behavior |
|
269
269
|
| `--log` | Set filepath to log conversation to (for interactive modes) |
|
270
270
|
| `--config` | Path to a custom configuration file or, when used without a value, enters interactive configuration mode |
|
271
271
|
| `--config-index` | Index of the configuration to use (default: 0) |
|
272
|
+
| `--provider` | Provider name to identify the configuration to use (alternative to --config-index) |
|
272
273
|
| `--remove` | Remove the configuration at the specified index (requires --config and --config-index) |
|
273
274
|
| `--show-config` | Show configuration details and exit |
|
274
275
|
| `--all` | Used with `--show-config` to display all configurations |
|
@@ -291,8 +292,17 @@ ngpt --config
|
|
291
292
|
# Edit an existing configuration at index 1
|
292
293
|
ngpt --config --config-index 1
|
293
294
|
|
295
|
+
# Edit an existing configuration by provider name
|
296
|
+
ngpt --config --provider Gemini
|
297
|
+
|
294
298
|
# Remove a configuration at index 2
|
295
299
|
ngpt --config --remove --config-index 2
|
300
|
+
|
301
|
+
# Remove a configuration by provider name
|
302
|
+
ngpt --config --remove --provider Gemini
|
303
|
+
|
304
|
+
# Use a specific configuration by provider name
|
305
|
+
ngpt --provider OpenAI "Tell me about quantum computing"
|
296
306
|
```
|
297
307
|
|
298
308
|
In interactive mode:
|
@@ -0,0 +1,9 @@
|
|
1
|
+
ngpt/__init__.py,sha256=ehInP9w0MZlS1vZ1g6Cm4YE1ftmgF72CnEddQ3Le9n4,368
|
2
|
+
ngpt/cli.py,sha256=iIlsdKGnSUbdnzKRL6Vmk067KXDEtIZmLqRm8ZKH7XE,49101
|
3
|
+
ngpt/client.py,sha256=lJfyLONeBU7YqMVJJys6zPay7gcJTq108_rJ4wvOtok,13067
|
4
|
+
ngpt/config.py,sha256=WYOk_b1eiYjo6hpV3pfXr2RjqhOnmKqwZwKid1T41I4,10363
|
5
|
+
ngpt-2.4.0.dist-info/METADATA,sha256=Zcfa9uWB3PwwGgFz4fIhmu9XfLu_ZzQxIq7HF8CH-ew,13910
|
6
|
+
ngpt-2.4.0.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
7
|
+
ngpt-2.4.0.dist-info/entry_points.txt,sha256=1cnAMujyy34DlOahrJg19lePSnb08bLbkUs_kVerqdk,39
|
8
|
+
ngpt-2.4.0.dist-info/licenses/LICENSE,sha256=mQkpWoADxbHqE0HRefYLJdm7OpdrXBr3vNv5bZ8w72M,1065
|
9
|
+
ngpt-2.4.0.dist-info/RECORD,,
|
ngpt-2.3.3.dist-info/RECORD
DELETED
@@ -1,9 +0,0 @@
|
|
1
|
-
ngpt/__init__.py,sha256=ehInP9w0MZlS1vZ1g6Cm4YE1ftmgF72CnEddQ3Le9n4,368
|
2
|
-
ngpt/cli.py,sha256=9sQqBUxHQaiaI9QZ7F63Wky19iJIfEk5HmboqxQFpR8,43498
|
3
|
-
ngpt/client.py,sha256=75xmzO7e9wQ7y_LzZCacg3mkZdheewcBxB6moPftqYw,13067
|
4
|
-
ngpt/config.py,sha256=BF0G3QeiPma8l7EQyc37bR7LWZog7FHJQNe7uj9cr4w,6896
|
5
|
-
ngpt-2.3.3.dist-info/METADATA,sha256=pVnJjrQlmXNwdzCyohq-uP9al4GptzXc1nhPwI8ph7Y,13535
|
6
|
-
ngpt-2.3.3.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
7
|
-
ngpt-2.3.3.dist-info/entry_points.txt,sha256=1cnAMujyy34DlOahrJg19lePSnb08bLbkUs_kVerqdk,39
|
8
|
-
ngpt-2.3.3.dist-info/licenses/LICENSE,sha256=mQkpWoADxbHqE0HRefYLJdm7OpdrXBr3vNv5bZ8w72M,1065
|
9
|
-
ngpt-2.3.3.dist-info/RECORD,,
|
File without changes
|
File without changes
|
File without changes
|