ngpt 2.7.2__py3-none-any.whl → 2.9.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/__init__.py CHANGED
@@ -3,8 +3,19 @@ __version__ = get_version("ngpt")
3
3
 
4
4
  from .client import NGPTClient
5
5
  from .config import load_config, get_config_path, get_config_dir
6
+ from .cli_config import (
7
+ load_cli_config,
8
+ set_cli_config_option,
9
+ get_cli_config_option,
10
+ unset_cli_config_option,
11
+ apply_cli_config
12
+ )
6
13
 
7
- __all__ = ["NGPTClient", "__version__", "load_config", "get_config_path", "get_config_dir"]
14
+ __all__ = [
15
+ "NGPTClient", "__version__", "load_config", "get_config_path", "get_config_dir",
16
+ "load_cli_config", "set_cli_config_option", "get_cli_config_option",
17
+ "unset_cli_config_option", "apply_cli_config"
18
+ ]
8
19
 
9
20
  # Import cli last to avoid circular imports
10
21
  from .cli import main
ngpt/cli.py CHANGED
@@ -3,6 +3,14 @@ import sys
3
3
  import os
4
4
  from .client import NGPTClient
5
5
  from .config import load_config, get_config_path, load_configs, add_config_entry, remove_config_entry
6
+ from .cli_config import (
7
+ set_cli_config_option,
8
+ get_cli_config_option,
9
+ unset_cli_config_option,
10
+ apply_cli_config,
11
+ list_cli_config_options,
12
+ CLI_CONFIG_OPTIONS
13
+ )
6
14
  from . import __version__
7
15
 
8
16
  # Try to import markdown rendering libraries
@@ -844,6 +852,143 @@ def prettify_streaming_markdown(renderer='rich', is_interactive=False, header_te
844
852
  print(f"{COLORS['yellow']}Error setting up Rich streaming display: {str(e)}{COLORS['reset']}")
845
853
  return None, None
846
854
 
855
+ def show_cli_config_help():
856
+ """Display help information about CLI configuration."""
857
+ print(f"\n{COLORS['green']}{COLORS['bold']}CLI Configuration Help:{COLORS['reset']}")
858
+ print(f" {COLORS['cyan']}Command syntax:{COLORS['reset']}")
859
+ print(f" {COLORS['yellow']}ngpt --cli-config set OPTION VALUE{COLORS['reset']} - Set a default value for OPTION")
860
+ print(f" {COLORS['yellow']}ngpt --cli-config get OPTION{COLORS['reset']} - Get the current value of OPTION")
861
+ print(f" {COLORS['yellow']}ngpt --cli-config get{COLORS['reset']} - Show all CLI configuration settings")
862
+ print(f" {COLORS['yellow']}ngpt --cli-config unset OPTION{COLORS['reset']} - Remove OPTION from configuration")
863
+ print(f" {COLORS['yellow']}ngpt --cli-config list{COLORS['reset']} - List all available options")
864
+
865
+ print(f"\n {COLORS['cyan']}Available options:{COLORS['reset']}")
866
+
867
+ # Group options by context
868
+ context_groups = {
869
+ "all": [],
870
+ "code": [],
871
+ "interactive": [],
872
+ "text": [],
873
+ "shell": []
874
+ }
875
+
876
+ for option, meta in CLI_CONFIG_OPTIONS.items():
877
+ for context in meta["context"]:
878
+ if context in context_groups:
879
+ if context == "all":
880
+ context_groups[context].append(option)
881
+ break
882
+ else:
883
+ context_groups[context].append(option)
884
+
885
+ # Print general options (available in all contexts)
886
+ print(f" {COLORS['yellow']}General options (all modes):{COLORS['reset']}")
887
+ for option in sorted(context_groups["all"]):
888
+ meta = CLI_CONFIG_OPTIONS[option]
889
+ default = f"(default: {meta['default']})" if meta['default'] is not None else ""
890
+ exclusive = f" [exclusive with: {', '.join(meta['exclusive'])}]" if "exclusive" in meta else ""
891
+ print(f" {COLORS['green']}{option}{COLORS['reset']} - {meta['type']} {default}{exclusive}")
892
+
893
+ # Print mode-specific options
894
+ for mode, options in [
895
+ ("code", "Code generation mode"),
896
+ ("interactive", "Interactive mode"),
897
+ ("text", "Text mode"),
898
+ ("shell", "Shell mode")
899
+ ]:
900
+ if context_groups[mode]:
901
+ print(f"\n {COLORS['yellow']}Options for {options}:{COLORS['reset']}")
902
+ for option in sorted(context_groups[mode]):
903
+ # Skip if already listed in general options
904
+ if option in context_groups["all"]:
905
+ continue
906
+ meta = CLI_CONFIG_OPTIONS[option]
907
+ default = f"(default: {meta['default']})" if meta['default'] is not None else ""
908
+ exclusive = f" [exclusive with: {', '.join(meta['exclusive'])}]" if "exclusive" in meta else ""
909
+ print(f" {COLORS['green']}{option}{COLORS['reset']} - {meta['type']} {default}{exclusive}")
910
+
911
+ print(f"\n {COLORS['cyan']}Example usage:{COLORS['reset']}")
912
+ print(f" {COLORS['yellow']}ngpt --cli-config set language java{COLORS['reset']} - Set default language to java for code generation")
913
+ print(f" {COLORS['yellow']}ngpt --cli-config set temperature 0.9{COLORS['reset']} - Set default temperature to 0.9")
914
+ print(f" {COLORS['yellow']}ngpt --cli-config set no-stream true{COLORS['reset']} - Disable streaming by default")
915
+ print(f" {COLORS['yellow']}ngpt --cli-config unset language{COLORS['reset']} - Remove language setting")
916
+
917
+ print(f"\n {COLORS['cyan']}Notes:{COLORS['reset']}")
918
+ print(f" - CLI configuration is stored in {COLORS['yellow']}~/.config/ngpt/ngpt-cli.conf{COLORS['reset']} (or equivalent for your OS)")
919
+ print(f" - Settings are applied based on context (e.g., language only applies to code generation mode)")
920
+ print(f" - Command-line arguments always override CLI configuration")
921
+ print(f" - Some options are mutually exclusive and will not be applied together")
922
+
923
+ def handle_cli_config(action, option=None, value=None):
924
+ """Handle CLI configuration commands."""
925
+ if action == "list":
926
+ # List all available options
927
+ print(f"{COLORS['green']}{COLORS['bold']}Available CLI configuration options:{COLORS['reset']}")
928
+ for option in list_cli_config_options():
929
+ meta = CLI_CONFIG_OPTIONS[option]
930
+ default = f"(default: {meta['default']})" if meta['default'] is not None else ""
931
+ contexts = ', '.join(meta['context'])
932
+ if "all" in meta['context']:
933
+ contexts = "all modes"
934
+ print(f" {COLORS['cyan']}{option}{COLORS['reset']} - {meta['type']} {default} - Available in: {contexts}")
935
+ return
936
+
937
+ if action == "get":
938
+ if option is None:
939
+ # Get all options
940
+ success, config = get_cli_config_option()
941
+ if success and config:
942
+ print(f"{COLORS['green']}{COLORS['bold']}Current CLI configuration:{COLORS['reset']}")
943
+ for opt, val in config.items():
944
+ if opt in CLI_CONFIG_OPTIONS:
945
+ print(f" {COLORS['cyan']}{opt}{COLORS['reset']} = {val}")
946
+ else:
947
+ print(f" {COLORS['yellow']}{opt}{COLORS['reset']} = {val} (unknown option)")
948
+ else:
949
+ print(f"{COLORS['yellow']}No CLI configuration set. Use 'ngpt --cli-config set OPTION VALUE' to set options.{COLORS['reset']}")
950
+ else:
951
+ # Get specific option
952
+ success, result = get_cli_config_option(option)
953
+ if success:
954
+ if result is None:
955
+ print(f"{COLORS['cyan']}{option}{COLORS['reset']} is not set (default: {CLI_CONFIG_OPTIONS.get(option, {}).get('default', 'N/A')})")
956
+ else:
957
+ print(f"{COLORS['cyan']}{option}{COLORS['reset']} = {result}")
958
+ else:
959
+ print(f"{COLORS['yellow']}{result}{COLORS['reset']}")
960
+ return
961
+
962
+ if action == "set":
963
+ if option is None or value is None:
964
+ print(f"{COLORS['yellow']}Error: Both OPTION and VALUE are required for 'set' command.{COLORS['reset']}")
965
+ print(f"Usage: ngpt --cli-config set OPTION VALUE")
966
+ return
967
+
968
+ success, message = set_cli_config_option(option, value)
969
+ if success:
970
+ print(f"{COLORS['green']}{message}{COLORS['reset']}")
971
+ else:
972
+ print(f"{COLORS['yellow']}{message}{COLORS['reset']}")
973
+ return
974
+
975
+ if action == "unset":
976
+ if option is None:
977
+ print(f"{COLORS['yellow']}Error: OPTION is required for 'unset' command.{COLORS['reset']}")
978
+ print(f"Usage: ngpt --cli-config unset OPTION")
979
+ return
980
+
981
+ success, message = unset_cli_config_option(option)
982
+ if success:
983
+ print(f"{COLORS['green']}{message}{COLORS['reset']}")
984
+ else:
985
+ print(f"{COLORS['yellow']}{message}{COLORS['reset']}")
986
+ return
987
+
988
+ # If we get here, the action is not recognized
989
+ print(f"{COLORS['yellow']}Error: Unknown action '{action}'. Use 'set', 'get', 'unset', or 'list'.{COLORS['reset']}")
990
+ show_cli_config_help()
991
+
847
992
  def main():
848
993
  # Colorize description - use a shorter description to avoid line wrapping issues
849
994
  description = f"{COLORS['cyan']}{COLORS['bold']}nGPT{COLORS['reset']} - Interact with AI language models via OpenAI-compatible APIs"
@@ -921,8 +1066,30 @@ def main():
921
1066
  # Prompt argument
922
1067
  parser.add_argument('prompt', nargs='?', default=None, help='The prompt to send')
923
1068
 
1069
+ # Add CLI configuration command
1070
+ config_group.add_argument('--cli-config', nargs='*', metavar='COMMAND',
1071
+ help='Manage CLI configuration (set, get, unset, list)')
1072
+
924
1073
  args = parser.parse_args()
925
1074
 
1075
+ # Handle CLI configuration command
1076
+ if args.cli_config is not None:
1077
+ # Show help if no arguments or "help" argument
1078
+ if len(args.cli_config) == 0 or (len(args.cli_config) > 0 and args.cli_config[0].lower() == "help"):
1079
+ show_cli_config_help()
1080
+ return
1081
+
1082
+ action = args.cli_config[0].lower()
1083
+ option = args.cli_config[1] if len(args.cli_config) > 1 else None
1084
+ value = args.cli_config[2] if len(args.cli_config) > 2 else None
1085
+
1086
+ if action in ("set", "get", "unset", "list"):
1087
+ handle_cli_config(action, option, value)
1088
+ return
1089
+ else:
1090
+ show_cli_config_help()
1091
+ return
1092
+
926
1093
  # Validate --all usage
927
1094
  if args.all and not args.show_config:
928
1095
  parser.error("--all can only be used with --show-config")
@@ -932,8 +1099,28 @@ def main():
932
1099
  show_available_renderers()
933
1100
  return
934
1101
 
935
- # Check for mutual exclusivity between --config-index and --provider
936
- if args.config_index != 0 and args.provider:
1102
+ # Load CLI configuration early
1103
+ from .cli_config import load_cli_config
1104
+ cli_config = load_cli_config()
1105
+
1106
+ # Priority order for config selection:
1107
+ # 1. Command-line arguments (args.provider, args.config_index)
1108
+ # 2. CLI configuration (cli_config["provider"], cli_config["config-index"])
1109
+ # 3. Default values (None, 0)
1110
+
1111
+ # Get provider/config-index from CLI config if not specified in args
1112
+ effective_provider = args.provider
1113
+ effective_config_index = args.config_index
1114
+
1115
+ # Only apply CLI config for provider/config-index if not explicitly set on command line
1116
+ if not effective_provider and 'provider' in cli_config and '--provider' not in sys.argv:
1117
+ effective_provider = cli_config['provider']
1118
+
1119
+ if effective_config_index == 0 and 'config-index' in cli_config and '--config-index' not in sys.argv:
1120
+ effective_config_index = cli_config['config-index']
1121
+
1122
+ # Check for mutual exclusivity between provider and config-index
1123
+ if effective_config_index != 0 and effective_provider:
937
1124
  parser.error("--config-index and --provider cannot be used together")
938
1125
 
939
1126
  # Handle interactive configuration mode
@@ -943,22 +1130,22 @@ def main():
943
1130
  # Handle configuration removal if --remove flag is present
944
1131
  if args.remove:
945
1132
  # Validate that config_index is explicitly provided
946
- if '--config-index' not in sys.argv and not args.provider:
1133
+ if '--config-index' not in sys.argv and not effective_provider:
947
1134
  parser.error("--remove requires explicitly specifying --config-index or --provider")
948
1135
 
949
1136
  # Show config details before asking for confirmation
950
1137
  configs = load_configs(str(config_path))
951
1138
 
952
1139
  # Determine the config index to remove
953
- config_index = args.config_index
954
- if args.provider:
1140
+ config_index = effective_config_index
1141
+ if effective_provider:
955
1142
  # Find config index by provider name
956
- matching_configs = [i for i, cfg in enumerate(configs) if cfg.get('provider', '').lower() == args.provider.lower()]
1143
+ matching_configs = [i for i, cfg in enumerate(configs) if cfg.get('provider', '').lower() == effective_provider.lower()]
957
1144
  if not matching_configs:
958
- print(f"Error: No configuration found for provider '{args.provider}'")
1145
+ print(f"Error: No configuration found for provider '{effective_provider}'")
959
1146
  return
960
1147
  elif len(matching_configs) > 1:
961
- print(f"Multiple configurations found for provider '{args.provider}':")
1148
+ print(f"Multiple configurations found for provider '{effective_provider}':")
962
1149
  for i, idx in enumerate(matching_configs):
963
1150
  print(f" [{i}] Index {idx}: {configs[idx].get('model', 'Unknown model')}")
964
1151
 
@@ -1008,15 +1195,15 @@ def main():
1008
1195
  config_index = None
1009
1196
 
1010
1197
  # Determine if we're editing an existing config or creating a new one
1011
- if args.provider:
1198
+ if effective_provider:
1012
1199
  # Find config by provider name
1013
1200
  configs = load_configs(str(config_path))
1014
- matching_configs = [i for i, cfg in enumerate(configs) if cfg.get('provider', '').lower() == args.provider.lower()]
1201
+ matching_configs = [i for i, cfg in enumerate(configs) if cfg.get('provider', '').lower() == effective_provider.lower()]
1015
1202
 
1016
1203
  if not matching_configs:
1017
- print(f"No configuration found for provider '{args.provider}'. Creating a new configuration.")
1204
+ print(f"No configuration found for provider '{effective_provider}'. Creating a new configuration.")
1018
1205
  elif len(matching_configs) > 1:
1019
- print(f"Multiple configurations found for provider '{args.provider}':")
1206
+ print(f"Multiple configurations found for provider '{effective_provider}':")
1020
1207
  for i, idx in enumerate(matching_configs):
1021
1208
  print(f" [{i}] Index {idx}: {configs[idx].get('model', 'Unknown model')}")
1022
1209
 
@@ -1032,14 +1219,14 @@ def main():
1032
1219
  config_index = matching_configs[0]
1033
1220
 
1034
1221
  print(f"Editing existing configuration at index {config_index}")
1035
- elif args.config_index != 0 or '--config-index' in sys.argv:
1222
+ elif effective_config_index != 0 or '--config-index' in sys.argv:
1036
1223
  # Check if the index is valid
1037
1224
  configs = load_configs(str(config_path))
1038
- if args.config_index >= 0 and args.config_index < len(configs):
1039
- config_index = args.config_index
1225
+ if effective_config_index >= 0 and effective_config_index < len(configs):
1226
+ config_index = effective_config_index
1040
1227
  print(f"Editing existing configuration at index {config_index}")
1041
1228
  else:
1042
- print(f"Configuration index {args.config_index} is out of range. Creating a new configuration.")
1229
+ print(f"Configuration index {effective_config_index} is out of range. Creating a new configuration.")
1043
1230
  else:
1044
1231
  # Creating a new config
1045
1232
  configs = load_configs(str(config_path))
@@ -1048,12 +1235,10 @@ def main():
1048
1235
  add_config_entry(config_path, config_index)
1049
1236
  return
1050
1237
 
1051
- # Load configuration using the specified index or provider (needed for active config display)
1052
- active_config = load_config(args.config, args.config_index, args.provider)
1238
+ # Load configuration using the effective provider/config-index
1239
+ active_config = load_config(args.config, effective_config_index, effective_provider)
1053
1240
 
1054
1241
  # Command-line arguments override config settings for active config display
1055
- # This part is kept to ensure the active config display reflects potential overrides,
1056
- # even though the overrides don't affect the stored configurations displayed with --all.
1057
1242
  if args.api_key:
1058
1243
  active_config["api_key"] = args.api_key
1059
1244
  if args.base_url:
@@ -1070,9 +1255,9 @@ def main():
1070
1255
  print(f"Total configurations: {len(configs)}")
1071
1256
 
1072
1257
  # Determine active configuration and display identifier
1073
- active_identifier = f"index {args.config_index}"
1074
- if args.provider:
1075
- active_identifier = f"provider '{args.provider}'"
1258
+ active_identifier = f"index {effective_config_index}"
1259
+ if effective_provider:
1260
+ active_identifier = f"provider '{effective_provider}'"
1076
1261
  print(f"Active configuration: {active_identifier}")
1077
1262
 
1078
1263
  if args.all:
@@ -1081,8 +1266,8 @@ def main():
1081
1266
  for i, cfg in enumerate(configs):
1082
1267
  provider = cfg.get('provider', 'N/A')
1083
1268
  active_str = '(Active)' if (
1084
- (args.provider and provider.lower() == args.provider.lower()) or
1085
- (not args.provider and i == args.config_index)
1269
+ (effective_provider and provider.lower() == effective_provider.lower()) or
1270
+ (not effective_provider and i == effective_config_index)
1086
1271
  ) else ''
1087
1272
  print(f"\n--- Configuration Index {i} / Provider: {COLORS['green']}{provider}{COLORS['reset']} {active_str} ---")
1088
1273
  print(f" API Key: {'[Set]' if cfg.get('api_key') else '[Not Set]'}")
@@ -1112,8 +1297,8 @@ def main():
1112
1297
  provider_display = f"{provider} {COLORS['yellow']}(duplicate){COLORS['reset']}"
1113
1298
 
1114
1299
  active_marker = "*" if (
1115
- (args.provider and provider.lower() == args.provider.lower()) or
1116
- (not args.provider and i == args.config_index)
1300
+ (effective_provider and provider.lower() == effective_provider.lower()) or
1301
+ (not effective_provider and i == effective_config_index)
1117
1302
  ) else " "
1118
1303
  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]'})")
1119
1304
 
@@ -1173,6 +1358,9 @@ def main():
1173
1358
 
1174
1359
  # Handle modes
1175
1360
  if args.interactive:
1361
+ # Apply CLI config for interactive mode
1362
+ args = apply_cli_config(args, "interactive")
1363
+
1176
1364
  # Interactive chat mode
1177
1365
  interactive_chat_session(
1178
1366
  client,
@@ -1188,6 +1376,9 @@ def main():
1188
1376
  stream_prettify=args.stream_prettify
1189
1377
  )
1190
1378
  elif args.shell:
1379
+ # Apply CLI config for shell mode
1380
+ args = apply_cli_config(args, "shell")
1381
+
1191
1382
  if args.prompt is None:
1192
1383
  try:
1193
1384
  print("Enter shell command description: ", end='')
@@ -1226,6 +1417,9 @@ def main():
1226
1417
  print(f"\nError:\n{e.stderr}")
1227
1418
 
1228
1419
  elif args.code:
1420
+ # Apply CLI config for code mode
1421
+ args = apply_cli_config(args, "code")
1422
+
1229
1423
  if args.prompt is None:
1230
1424
  try:
1231
1425
  print("Enter code description: ", end='')
@@ -1236,30 +1430,55 @@ def main():
1236
1430
  else:
1237
1431
  prompt = args.prompt
1238
1432
 
1239
- # Setup for stream-prettify with code generation
1433
+ # Setup for streaming and prettify logic
1240
1434
  stream_callback = None
1241
1435
  live_display = None
1242
- should_stream = False
1243
-
1436
+ should_stream = True # Default to streaming
1437
+ use_stream_prettify = False
1438
+ use_regular_prettify = False
1439
+
1440
+ # Determine final behavior based on flag priority
1244
1441
  if args.stream_prettify:
1245
- should_stream = True # Enable streaming
1246
- # This is the code generation mode, not interactive
1247
- live_display, stream_callback = prettify_streaming_markdown(args.renderer)
1248
- if not live_display:
1249
- # Fallback to normal prettify if live display setup failed
1250
- args.prettify = True
1251
- args.stream_prettify = False
1442
+ # Highest priority: stream-prettify
1443
+ if has_markdown_renderer('rich'):
1444
+ should_stream = True
1445
+ use_stream_prettify = True
1446
+ live_display, stream_callback = prettify_streaming_markdown(args.renderer)
1447
+ if not live_display:
1448
+ # Fallback if live display fails
1449
+ use_stream_prettify = False
1450
+ use_regular_prettify = True
1451
+ should_stream = False
1452
+ print(f"{COLORS['yellow']}Live display setup failed. Falling back to regular prettify mode.{COLORS['reset']}")
1453
+ else:
1454
+ # Rich not available for stream-prettify
1455
+ print(f"{COLORS['yellow']}Warning: Rich is not available for --stream-prettify. Install with: pip install \"ngpt[full]\".{COLORS['reset']}")
1456
+ print(f"{COLORS['yellow']}Falling back to default streaming without prettify.{COLORS['reset']}")
1457
+ should_stream = True
1458
+ use_stream_prettify = False
1459
+ elif args.no_stream:
1460
+ # Second priority: no-stream
1461
+ should_stream = False
1462
+ use_regular_prettify = False # No prettify if no streaming
1463
+ elif args.prettify:
1464
+ # Third priority: prettify (requires disabling stream)
1465
+ if has_markdown_renderer(args.renderer):
1252
1466
  should_stream = False
1253
- print(f"{COLORS['yellow']}Falling back to regular prettify mode.{COLORS['reset']}")
1254
-
1255
- # If regular prettify is enabled with streaming, inform the user
1256
- if args.prettify and not args.no_stream:
1257
- print(f"{COLORS['yellow']}Note: Streaming disabled to enable markdown rendering.{COLORS['reset']}")
1467
+ use_regular_prettify = True
1468
+ print(f"{COLORS['yellow']}Note: Streaming disabled to enable regular markdown rendering (--prettify).{COLORS['reset']}")
1469
+ else:
1470
+ # Renderer not available for prettify
1471
+ print(f"{COLORS['yellow']}Warning: Renderer '{args.renderer}' not available for --prettify.{COLORS['reset']}")
1472
+ show_available_renderers()
1473
+ print(f"{COLORS['yellow']}Falling back to default streaming without prettify.{COLORS['reset']}")
1474
+ should_stream = True
1475
+ use_regular_prettify = False
1476
+ # else: Default is should_stream = True
1258
1477
 
1259
1478
  print("\nGenerating code...")
1260
1479
 
1261
1480
  # Start live display if using stream-prettify
1262
- if args.stream_prettify and live_display:
1481
+ if use_stream_prettify and live_display:
1263
1482
  live_display.start()
1264
1483
 
1265
1484
  generated_code = client.generate_code(
@@ -1269,23 +1488,29 @@ def main():
1269
1488
  temperature=args.temperature,
1270
1489
  top_p=args.top_p,
1271
1490
  max_tokens=args.max_tokens,
1272
- markdown_format=args.prettify or args.stream_prettify,
1491
+ # Request markdown from API if any prettify option is active
1492
+ markdown_format=use_regular_prettify or use_stream_prettify,
1273
1493
  stream=should_stream,
1274
1494
  stream_callback=stream_callback
1275
1495
  )
1276
1496
 
1277
1497
  # Stop live display if using stream-prettify
1278
- if args.stream_prettify and live_display:
1498
+ if use_stream_prettify and live_display:
1279
1499
  live_display.stop()
1280
1500
 
1281
- if generated_code and not args.stream_prettify:
1282
- if args.prettify:
1501
+ # Print non-streamed output if needed
1502
+ if generated_code and not should_stream:
1503
+ if use_regular_prettify:
1283
1504
  print("\nGenerated code:")
1284
1505
  prettify_markdown(generated_code, args.renderer)
1285
1506
  else:
1507
+ # Should only happen if --no-stream was used without prettify
1286
1508
  print(f"\nGenerated code:\n{generated_code}")
1287
-
1509
+
1288
1510
  elif args.text:
1511
+ # Apply CLI config for text mode
1512
+ args = apply_cli_config(args, "text")
1513
+
1289
1514
  if args.prompt is not None:
1290
1515
  prompt = args.prompt
1291
1516
  else:
@@ -1447,6 +1672,9 @@ def main():
1447
1672
 
1448
1673
  else:
1449
1674
  # Default to chat mode
1675
+ # Apply CLI config for default chat mode
1676
+ args = apply_cli_config(args, "all")
1677
+
1450
1678
  if args.prompt is None:
1451
1679
  try:
1452
1680
  print("Enter your prompt: ", end='')
ngpt/cli_config.py ADDED
@@ -0,0 +1,247 @@
1
+ import os
2
+ import sys
3
+ import json
4
+ from pathlib import Path
5
+ from typing import Dict, Optional, Any, List, Union, Tuple
6
+
7
+ # CLI config options with their types and default values
8
+ CLI_CONFIG_OPTIONS = {
9
+ "language": {"type": "str", "default": "python", "context": ["code"]},
10
+ "provider": {"type": "str", "default": None, "context": ["all"]},
11
+ "temperature": {"type": "float", "default": 0.7, "context": ["all"]},
12
+ "top_p": {"type": "float", "default": 1.0, "context": ["all"]},
13
+ "max_tokens": {"type": "int", "default": None, "context": ["all"]},
14
+ "log": {"type": "str", "default": None, "context": ["interactive", "text"]},
15
+ "preprompt": {"type": "str", "default": None, "context": ["all"]},
16
+ "no-stream": {"type": "bool", "default": False, "context": ["all"], "exclusive": ["prettify", "stream-prettify"]},
17
+ "prettify": {"type": "bool", "default": False, "context": ["all"], "exclusive": ["no-stream", "stream-prettify"]},
18
+ "stream-prettify": {"type": "bool", "default": False, "context": ["all"], "exclusive": ["no-stream", "prettify"]},
19
+ "renderer": {"type": "str", "default": "auto", "context": ["all"]},
20
+ "config-index": {"type": "int", "default": 0, "context": ["all"]},
21
+ "web-search": {"type": "bool", "default": False, "context": ["all"]},
22
+ }
23
+
24
+ def get_cli_config_dir() -> Path:
25
+ """Get the appropriate CLI config directory based on OS."""
26
+ if sys.platform == "win32":
27
+ # Windows
28
+ config_dir = Path(os.environ.get("APPDATA", "")) / "ngpt"
29
+ elif sys.platform == "darwin":
30
+ # macOS
31
+ config_dir = Path.home() / "Library" / "Application Support" / "ngpt"
32
+ else:
33
+ # Linux and other Unix-like systems
34
+ xdg_config_home = os.environ.get("XDG_CONFIG_HOME")
35
+ if xdg_config_home:
36
+ config_dir = Path(xdg_config_home) / "ngpt"
37
+ else:
38
+ config_dir = Path.home() / ".config" / "ngpt"
39
+
40
+ # Ensure the directory exists
41
+ config_dir.mkdir(parents=True, exist_ok=True)
42
+ return config_dir
43
+
44
+ def get_cli_config_path() -> Path:
45
+ """Get the path to the CLI config file."""
46
+ return get_cli_config_dir() / "ngpt-cli.conf"
47
+
48
+ def load_cli_config() -> Dict[str, Any]:
49
+ """Load CLI configuration from the config file."""
50
+ config_path = get_cli_config_path()
51
+
52
+ # Default empty config
53
+ config = {}
54
+
55
+ # Load from config file if it exists
56
+ if config_path.exists():
57
+ try:
58
+ with open(config_path, "r") as f:
59
+ config = json.load(f)
60
+ except (json.JSONDecodeError, IOError) as e:
61
+ print(f"Warning: Could not read CLI config file: {e}", file=sys.stderr)
62
+
63
+ return config
64
+
65
+ def save_cli_config(config: Dict[str, Any]) -> bool:
66
+ """Save CLI configuration to the config file."""
67
+ config_path = get_cli_config_path()
68
+
69
+ try:
70
+ with open(config_path, "w") as f:
71
+ json.dump(config, f, indent=2)
72
+ return True
73
+ except Exception as e:
74
+ print(f"Error saving CLI configuration: {e}", file=sys.stderr)
75
+ return False
76
+
77
+ def set_cli_config_option(option: str, value: Any) -> Tuple[bool, str]:
78
+ """Set a CLI configuration option.
79
+
80
+ Args:
81
+ option: The name of the option to set
82
+ value: The value to set
83
+
84
+ Returns:
85
+ Tuple of (success, message)
86
+ """
87
+ # Check if option is valid
88
+ if option not in CLI_CONFIG_OPTIONS:
89
+ return False, f"Error: Unknown option '{option}'"
90
+
91
+ # Load current config
92
+ config = load_cli_config()
93
+
94
+ # Parse and validate the value based on option type
95
+ option_type = CLI_CONFIG_OPTIONS[option]["type"]
96
+
97
+ try:
98
+ if option_type == "str":
99
+ parsed_value = str(value)
100
+ elif option_type == "int":
101
+ parsed_value = int(value)
102
+ elif option_type == "float":
103
+ parsed_value = float(value)
104
+ elif option_type == "bool":
105
+ if isinstance(value, bool):
106
+ parsed_value = value
107
+ elif value.lower() in ("true", "yes", "1", "t", "y"):
108
+ parsed_value = True
109
+ elif value.lower() in ("false", "no", "0", "f", "n"):
110
+ parsed_value = False
111
+ else:
112
+ return False, f"Error: Invalid boolean value '{value}' for option '{option}'"
113
+ else:
114
+ return False, f"Error: Unsupported option type '{option_type}'"
115
+
116
+ # Handle mutual exclusivity for boolean options
117
+ if option_type == "bool" and "exclusive" in CLI_CONFIG_OPTIONS[option]:
118
+ if parsed_value: # If setting this option to True
119
+ # Set all other exclusive options to False
120
+ for excl_option in CLI_CONFIG_OPTIONS[option]["exclusive"]:
121
+ config[excl_option] = False
122
+ # No special handling needed if setting to False, just update the value
123
+
124
+ # Set the value in the config
125
+ config[option] = parsed_value
126
+
127
+ # Save the config
128
+ if save_cli_config(config):
129
+ return True, f"Successfully set {option}={parsed_value}"
130
+ else:
131
+ return False, "Error saving configuration"
132
+
133
+ except (ValueError, TypeError) as e:
134
+ return False, f"Error: {str(e)}"
135
+
136
+ def get_cli_config_option(option: str = None) -> Tuple[bool, Union[str, Dict[str, Any]]]:
137
+ """Get a CLI configuration option or all options.
138
+
139
+ Args:
140
+ option: The name of the option to get, or None for all options
141
+
142
+ Returns:
143
+ Tuple of (success, value/message)
144
+ """
145
+ # Load current config
146
+ config = load_cli_config()
147
+
148
+ # Return all options if no specific option requested
149
+ if option is None:
150
+ return True, config
151
+
152
+ # Check if option is valid
153
+ if option not in CLI_CONFIG_OPTIONS:
154
+ return False, f"Error: Unknown option '{option}'"
155
+
156
+ # Return the option value if set, otherwise the default
157
+ if option in config:
158
+ return True, config[option]
159
+ else:
160
+ return True, CLI_CONFIG_OPTIONS[option]["default"]
161
+
162
+ def unset_cli_config_option(option: str) -> Tuple[bool, str]:
163
+ """Unset a CLI configuration option.
164
+
165
+ Args:
166
+ option: The name of the option to unset
167
+
168
+ Returns:
169
+ Tuple of (success, message)
170
+ """
171
+ # Check if option is valid
172
+ if option not in CLI_CONFIG_OPTIONS:
173
+ return False, f"Error: Unknown option '{option}'"
174
+
175
+ # Load current config
176
+ config = load_cli_config()
177
+
178
+ # Remove the option if it exists
179
+ if option in config:
180
+ del config[option]
181
+
182
+ # Save the config
183
+ if save_cli_config(config):
184
+ return True, f"Successfully unset {option}"
185
+ else:
186
+ return False, "Error saving configuration"
187
+ else:
188
+ return True, f"Note: Option '{option}' was not set"
189
+
190
+ def apply_cli_config(args: Any, mode: str) -> Any:
191
+ """Apply CLI configuration to args object, respecting context and not overriding explicit args.
192
+
193
+ Args:
194
+ args: The argparse namespace object
195
+ mode: The current mode ('interactive', 'shell', 'code', 'text', or 'all' for default)
196
+
197
+ Returns:
198
+ Updated args object
199
+ """
200
+ # Load CLI config
201
+ cli_config = load_cli_config()
202
+
203
+ # Get command-line arguments provided by the user
204
+ explicit_args = set(arg for arg in sys.argv[1:] if arg.startswith('--'))
205
+
206
+ # For each option in CLI config, check if it should be applied
207
+ for option, value in cli_config.items():
208
+ # Skip if not a valid option
209
+ if option not in CLI_CONFIG_OPTIONS:
210
+ continue
211
+
212
+ # Check context
213
+ option_context = CLI_CONFIG_OPTIONS[option]["context"]
214
+ if "all" not in option_context and mode not in option_context:
215
+ continue
216
+
217
+ # Convert dashes to underscores for argparse compatibility
218
+ arg_name = option.replace("-", "_")
219
+
220
+ # Skip if explicitly set via command line
221
+ # Check common variants like --option
222
+ cli_option = f"--{option}"
223
+ if cli_option in explicit_args:
224
+ continue
225
+
226
+ # Check exclusivity constraints against *explicitly set* args
227
+ # Don't apply a CLI config option if an exclusive option was explicitly set
228
+ if "exclusive" in CLI_CONFIG_OPTIONS[option]:
229
+ skip = False
230
+ for excl_option in CLI_CONFIG_OPTIONS[option]["exclusive"]:
231
+ excl_cli_option = f"--{excl_option}"
232
+ if excl_cli_option in explicit_args:
233
+ skip = True
234
+ break # Skip applying this CLI config value
235
+ if skip:
236
+ continue
237
+
238
+ # Apply the value from CLI config
239
+ # Ensure the attribute exists on args before setting
240
+ if hasattr(args, arg_name):
241
+ setattr(args, arg_name, value)
242
+
243
+ return args
244
+
245
+ def list_cli_config_options() -> List[str]:
246
+ """List all available CLI configuration options."""
247
+ return sorted(CLI_CONFIG_OPTIONS.keys())
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: ngpt
3
- Version: 2.7.2
3
+ Version: 2.9.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
@@ -28,10 +28,9 @@ Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
28
28
  Classifier: Topic :: Software Development :: Libraries :: Python Modules
29
29
  Classifier: Topic :: Utilities
30
30
  Requires-Python: >=3.8
31
+ Requires-Dist: prompt-toolkit>=3.0.0
31
32
  Requires-Dist: requests>=2.31.0
32
- Provides-Extra: full
33
- Requires-Dist: prompt-toolkit>=3.0.0; extra == 'full'
34
- Requires-Dist: rich>=10.0.0; extra == 'full'
33
+ Requires-Dist: rich>=10.0.0
35
34
  Description-Content-Type: text/markdown
36
35
 
37
36
  # nGPT
@@ -43,6 +42,9 @@ Description-Content-Type: text/markdown
43
42
 
44
43
  A lightweight Python CLI and library for interacting with OpenAI-compatible APIs, supporting both official and self-hosted LLM endpoints.
45
44
 
45
+ ![2025-04-23_16-18-01](https://github.com/user-attachments/assets/b8e58926-5165-4352-b48b-9f4a982da86e)
46
+
47
+
46
48
  ## Table of Contents
47
49
  - [Quick Start](#quick-start)
48
50
  - [Features](#features)
@@ -52,8 +54,10 @@ A lightweight Python CLI and library for interacting with OpenAI-compatible APIs
52
54
  - [Documentation](https://nazdridoy.github.io/ngpt/)
53
55
  - [CLI Tool](#as-a-cli-tool)
54
56
  - [Python Library](#as-a-library)
57
+ - [CLI Framework](#as-a-cli-framework)
55
58
  - [Configuration](#configuration)
56
59
  - [Command Line Options](#command-line-options)
60
+ - [CLI Configuration](#cli-configuration)
57
61
  - [Interactive Configuration](#interactive-configuration)
58
62
  - [Configuration File](#configuration-file)
59
63
  - [Configuration Priority](#configuration-priority)
@@ -63,11 +67,14 @@ A lightweight Python CLI and library for interacting with OpenAI-compatible APIs
63
67
  ## Quick Start
64
68
 
65
69
  ```bash
66
- # Install
70
+ # Install with pip
67
71
  pip install ngpt
68
72
 
69
- # Install with additional features
70
- pip install "ngpt[full]"
73
+ # Or install with uv (faster)
74
+ uv pip install ngpt
75
+
76
+ # Or install globally as a CLI tool (recommended)
77
+ uv tool install ngpt
71
78
 
72
79
  # Chat with default settings
73
80
  ngpt "Tell me about quantum computing"
@@ -113,8 +120,8 @@ For more examples and detailed usage, visit the [CLI Usage Guide](https://nazdri
113
120
 
114
121
  ## Features
115
122
 
116
- - ✅ **Dual Mode**: Use as a CLI tool or import as a Python library
117
- - 🪶 **Lightweight**: Minimal dependencies (just `requests`)
123
+ - ✅ **Versatile**: Use as a CLI tool, Python library, or CLI framework for building custom tools
124
+ - 🪶 **Lightweight**: Minimal dependencies with everything you need included
118
125
  - 🔄 **API Flexibility**: Works with OpenAI, Ollama, Groq, and any compatible endpoint
119
126
  - 💬 **Interactive Chat**: Continuous conversation with memory in modern UI
120
127
  - 📊 **Streaming Responses**: Real-time output for better user experience
@@ -127,6 +134,7 @@ For more examples and detailed usage, visit the [CLI Usage Guide](https://nazdri
127
134
  - 📝 **Rich Multiline Editor**: Interactive multiline text input with syntax highlighting and intuitive controls
128
135
  - 🎭 **System Prompts**: Customize model behavior with custom system prompts
129
136
  - 📃 **Conversation Logging**: Save your conversations to text files for later reference
137
+ - 🧰 **CLI Components**: Reusable components for building custom AI-powered command-line tools
130
138
 
131
139
  See the [Feature Overview](https://nazdridoy.github.io/ngpt/overview.html) for more details.
132
140
 
@@ -140,25 +148,25 @@ Key documentation sections:
140
148
  - [Installation Guide](https://nazdridoy.github.io/ngpt/installation.html)
141
149
  - [CLI Usage Guide](https://nazdridoy.github.io/ngpt/usage/cli_usage.html)
142
150
  - [Library Usage Guide](https://nazdridoy.github.io/ngpt/usage/library_usage.html)
151
+ - [CLI Framework Guide](https://nazdridoy.github.io/ngpt/usage/cli_framework.html)
143
152
  - [Configuration Guide](https://nazdridoy.github.io/ngpt/configuration.html)
144
153
  - [Examples & Tutorials](https://nazdridoy.github.io/ngpt/examples/basic.html)
145
154
 
146
155
  ## Installation
147
156
 
148
157
  ```bash
149
- # Basic installation (minimal dependencies)
158
+ # Installation with pip
150
159
  pip install ngpt
151
160
 
152
- # Full installation with all features (recommended)
153
- pip install "ngpt[full]"
161
+ # Or install with uv (faster installation)
162
+ uv pip install ngpt
163
+
164
+ # Or install globally as a CLI tool (recommended for command-line usage)
165
+ uv tool install ngpt
154
166
  ```
155
167
 
156
168
  Requires Python 3.8 or newer.
157
169
 
158
- The full installation includes:
159
- - Enhanced markdown rendering with syntax highlighting
160
- - Improved interactive input experience with multiline editing
161
-
162
170
  For detailed installation instructions, see the [Installation Guide](https://nazdridoy.github.io/ngpt/installation.html).
163
171
 
164
172
  ## Usage
@@ -278,6 +286,47 @@ print(code)
278
286
 
279
287
  For advanced usage patterns and integrations, check out the [Advanced Examples](https://nazdridoy.github.io/ngpt/examples/advanced.html).
280
288
 
289
+ ### As a CLI Framework
290
+
291
+ nGPT can also be used as a framework to build your own AI-powered command-line tools. You can leverage nGPT's pre-built CLI components to quickly develop sophisticated CLI applications.
292
+
293
+ ```python
294
+ from ngpt import NGPTClient, load_config
295
+ from ngpt.cli import interactive_chat_session, prettify_markdown, ColoredHelpFormatter
296
+ import argparse
297
+
298
+ # Create a custom CLI tool with colorized help
299
+ parser = argparse.ArgumentParser(
300
+ description="Specialized Code Assistant",
301
+ formatter_class=ColoredHelpFormatter
302
+ )
303
+ parser.add_argument("prompt", nargs="?", help="Code description")
304
+ parser.add_argument("--language", "-l", default="python", help="Programming language")
305
+ parser.add_argument("--interactive", "-i", action="store_true", help="Start interactive mode")
306
+ args = parser.parse_args()
307
+
308
+ # Initialize client
309
+ client = NGPTClient(**load_config())
310
+
311
+ # Use interactive session for conversation
312
+ if args.interactive:
313
+ system_prompt = f"You are an expert {args.language} developer. Provide clear, detailed answers."
314
+ interactive_chat_session(client=client, preprompt=system_prompt, prettify=True)
315
+ elif args.prompt:
316
+ # Generate and prettify code
317
+ code = client.generate_code(args.prompt, language=args.language)
318
+ print(prettify_markdown(f"```{args.language}\n{code}\n```"))
319
+ ```
320
+
321
+ This allows you to build specialized AI tools like:
322
+ - Code generators for specific languages or frameworks
323
+ - Domain-specific assistants (SQL, legal, finance, etc.)
324
+ - Documentation generators
325
+ - Translation tools
326
+ - And much more
327
+
328
+ For detailed information about building CLI tools with nGPT, see the [CLI Framework Guide](https://nazdridoy.github.io/ngpt/usage/cli_framework.html) and explore the [CLI Component Examples](https://nazdridoy.github.io/ngpt/examples/cli_components.html).
329
+
281
330
  ## Configuration
282
331
 
283
332
  ### Command Line Options
@@ -304,17 +353,85 @@ You can configure the client using the following options:
304
353
  | `--config` | Path to a custom configuration file or, when used without a value, enters interactive configuration mode |
305
354
  | `--config-index` | Index of the configuration to use (default: 0) |
306
355
  | `--provider` | Provider name to identify the configuration to use (alternative to --config-index) |
307
- | `--remove` | Remove the configuration at the specified index (requires --config and --config-index) |
356
+ | `--remove` | Remove the configuration at the specified index (requires --config and --config-index or --provider) |
308
357
  | `--show-config` | Show configuration details and exit |
309
358
  | `--all` | Used with `--show-config` to display all configurations |
310
359
  | `-i, --interactive` | Start an interactive chat session with stylish UI, conversation history, and special commands |
311
360
  | `-s, --shell` | Generate and execute shell commands |
312
361
  | `-c, --code` | Generate clean code output |
313
362
  | `-t, --text` | Open interactive multiline editor for complex prompts |
363
+ | `--language` | Programming language to generate code in (for code mode, default: python) |
364
+ | `--cli-config` | Manage CLI configuration settings (set, get, unset, list, help) |
314
365
  | `-v, --version` | Show version information |
315
366
 
316
367
  For a complete reference of all available options, see the [CLI Usage Guide](https://nazdridoy.github.io/ngpt/usage/cli_usage.html).
317
368
 
369
+ ### CLI Configuration
370
+
371
+ NGPT offers a CLI configuration system that allows you to set default values for command-line options:
372
+
373
+ ```bash
374
+ # Set default options
375
+ ngpt --cli-config set language typescript
376
+ ngpt --cli-config set temperature 0.9
377
+ ngpt --cli-config set prettify true
378
+
379
+ # View current settings
380
+ ngpt --cli-config get
381
+
382
+ # Get a specific setting
383
+ ngpt --cli-config get language
384
+
385
+ # Remove a setting
386
+ ngpt --cli-config unset prettify
387
+
388
+ # List all available options
389
+ ngpt --cli-config list
390
+
391
+ # Show help information
392
+ ngpt --cli-config help
393
+ ```
394
+
395
+ Key features of CLI configuration:
396
+ - **Context-Aware**: Settings are applied based on the current command mode (e.g., `language` only applies in code generation mode `-c`).
397
+ - **Priority**: When determining option values, NGPT uses the following priority order (highest to lowest):
398
+ 1. Command-line arguments
399
+ 2. Environment variables
400
+ 3. CLI configuration (ngpt-cli.conf)
401
+ 4. Main configuration file (ngpt.conf)
402
+ 5. Default values
403
+ - **Mutual Exclusivity**: For options like `no-stream`, `prettify`, and `stream-prettify`, setting one to `True` automatically sets the others to `False` in the configuration file, ensuring consistency.
404
+ - **Smart Selection**: The `provider` setting is used to select which configuration profile to use, offering a persistent way to select your preferred API.
405
+
406
+ Available options include:
407
+ - General options (all modes): `provider`, `temperature`, `top_p`, `max_tokens`, `preprompt`, `renderer`, `config-index`, `web-search`
408
+ - Mode-specific options: `language` (code mode only), `log` (interactive and text modes)
409
+ - Mutually exclusive options: `no-stream`, `prettify`, `stream-prettify`
410
+
411
+ #### Practical Examples
412
+
413
+ ```bash
414
+ # Set Gemini as your default provider
415
+ ngpt --cli-config set provider Gemini
416
+ # Now you can run commands without specifying --provider
417
+ ngpt "Explain quantum computing"
418
+
419
+ # Configure code generation for TypeScript
420
+ ngpt --cli-config set language typescript
421
+ # Now in code mode, TypeScript will be used by default
422
+ ngpt -c "Write a function to sort an array"
423
+
424
+ # Set a higher temperature for more creative responses
425
+ ngpt --cli-config set temperature 0.9
426
+ ```
427
+
428
+ The CLI configuration is stored in:
429
+ - Linux: `~/.config/ngpt/ngpt-cli.conf`
430
+ - macOS: `~/Library/Application Support/ngpt/ngpt-cli.conf`
431
+ - Windows: `%APPDATA%\ngpt\ngpt-cli.conf`
432
+
433
+ For more details, see the [CLI Configuration Guide](https://nazdridoy.github.io/ngpt/usage/cli_config.html).
434
+
318
435
  ### Interactive Configuration
319
436
 
320
437
  The `--config` option without arguments enters interactive configuration mode, allowing you to add or edit configurations:
@@ -387,10 +504,11 @@ For details on the configuration file format and structure, see the [Configurati
387
504
 
388
505
  nGPT determines configuration values in the following order (highest priority first):
389
506
 
390
- 1. Command line arguments (`--api-key`, `--base-url`, `--model`)
507
+ 1. Command line arguments (`--api-key`, `--base-url`, `--model`, etc.)
391
508
  2. Environment variables (`OPENAI_API_KEY`, `OPENAI_BASE_URL`, `OPENAI_MODEL`)
392
- 3. Configuration file (selected by `--config-index`, defaults to index 0)
393
- 4. Default values
509
+ 3. CLI configuration file (`ngpt-cli.conf`, managed with `--cli-config`)
510
+ 4. Main configuration file `ngpt.conf` or `custom-config-file`
511
+ 5. Default values
394
512
 
395
513
  ## Contributing
396
514
 
@@ -0,0 +1,10 @@
1
+ ngpt/__init__.py,sha256=awvycdj3tgcOr0BO81L4XU6DOtnToxFqkPHe1Pyu0Bw,652
2
+ ngpt/cli.py,sha256=8E65ovaJE0OJ3-M6aw_FqCNSy_1-x1h4NwhnsZOlXq4,81580
3
+ ngpt/cli_config.py,sha256=LatikOJTtohbmCfXM6AH44qplbpyAD-x1kMkmaJHRS0,9046
4
+ ngpt/client.py,sha256=Rv-JO8RAmw1v3gdLkwaPe_PEw6p83cejO0YNT_DDjeg,15134
5
+ ngpt/config.py,sha256=WYOk_b1eiYjo6hpV3pfXr2RjqhOnmKqwZwKid1T41I4,10363
6
+ ngpt-2.9.0.dist-info/METADATA,sha256=mA32IXz-eKDqJBnXFjdOq9miHkQc2sDUBv78zw7sWec,20277
7
+ ngpt-2.9.0.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
8
+ ngpt-2.9.0.dist-info/entry_points.txt,sha256=1cnAMujyy34DlOahrJg19lePSnb08bLbkUs_kVerqdk,39
9
+ ngpt-2.9.0.dist-info/licenses/LICENSE,sha256=mQkpWoADxbHqE0HRefYLJdm7OpdrXBr3vNv5bZ8w72M,1065
10
+ ngpt-2.9.0.dist-info/RECORD,,
@@ -1,9 +0,0 @@
1
- ngpt/__init__.py,sha256=ehInP9w0MZlS1vZ1g6Cm4YE1ftmgF72CnEddQ3Le9n4,368
2
- ngpt/cli.py,sha256=kdEgC4mOml4I3uX_w1SsCZUqlaccdyIQGtox3IjPhbs,70115
3
- ngpt/client.py,sha256=Rv-JO8RAmw1v3gdLkwaPe_PEw6p83cejO0YNT_DDjeg,15134
4
- ngpt/config.py,sha256=WYOk_b1eiYjo6hpV3pfXr2RjqhOnmKqwZwKid1T41I4,10363
5
- ngpt-2.7.2.dist-info/METADATA,sha256=J1epGacDeo1w9bWUUqAQ8LvUI1I-rEtKIxm6TwRA-7U,15452
6
- ngpt-2.7.2.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
7
- ngpt-2.7.2.dist-info/entry_points.txt,sha256=1cnAMujyy34DlOahrJg19lePSnb08bLbkUs_kVerqdk,39
8
- ngpt-2.7.2.dist-info/licenses/LICENSE,sha256=mQkpWoADxbHqE0HRefYLJdm7OpdrXBr3vNv5bZ8w72M,1065
9
- ngpt-2.7.2.dist-info/RECORD,,
File without changes