ngpt 1.1.3__py3-none-any.whl → 1.2.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 CHANGED
@@ -2,7 +2,7 @@ import argparse
2
2
  import sys
3
3
  import os
4
4
  from .client import NGPTClient
5
- from .config import load_config, get_config_path, load_configs, add_config_entry
5
+ from .config import load_config, get_config_path, load_configs, add_config_entry, remove_config_entry
6
6
  from . import __version__
7
7
 
8
8
  def show_config_help():
@@ -40,11 +40,15 @@ def show_config_help():
40
40
  print(" 4. Or provide command line arguments:")
41
41
  print(" ngpt --api-key your-key --base-url https://api.example.com --model your-model \"Your prompt\"")
42
42
 
43
- print(" 5. Use --config-index to specify which configuration to use:")
43
+ print(" 5. Use --config-index to specify which configuration to use or edit:")
44
44
  print(" ngpt --config-index 1 \"Your prompt\"")
45
45
 
46
- print(" 6. Use --config without arguments to add or edit a configuration:")
46
+ print(" 6. Use --config without arguments to add a new configuration:")
47
+ print(" ngpt --config")
48
+ print(" Or specify an index to edit an existing configuration:")
47
49
  print(" ngpt --config --config-index 1")
50
+ print(" 7. Remove a configuration at a specific index:")
51
+ print(" ngpt --config --remove --config-index 1")
48
52
 
49
53
  def check_config(config):
50
54
  """Check config for common issues and provide guidance."""
@@ -68,8 +72,9 @@ def main():
68
72
 
69
73
  # Config options
70
74
  config_group = parser.add_argument_group('Configuration Options')
71
- config_group.add_argument('--config', nargs='?', const=True, help='Path to a custom config file or, if no value provided, enter interactive configuration mode')
72
- config_group.add_argument('--config-index', type=int, default=0, help='Index of the configuration to use (default: 0)')
75
+ 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')
76
+ config_group.add_argument('--config-index', type=int, default=0, help='Index of the configuration to use or edit (default: 0)')
77
+ config_group.add_argument('--remove', action='store_true', help='Remove the configuration at the specified index (requires --config and --config-index)')
73
78
  config_group.add_argument('--show-config', action='store_true', help='Show the current configuration(s) and exit')
74
79
  config_group.add_argument('--all', action='store_true', help='Show details for all configurations (requires --show-config)')
75
80
 
@@ -103,7 +108,56 @@ def main():
103
108
  # Handle interactive configuration mode
104
109
  if args.config is True: # --config was used without a value
105
110
  config_path = get_config_path()
106
- add_config_entry(config_path, args.config_index)
111
+
112
+ # Handle configuration removal if --remove flag is present
113
+ if args.remove:
114
+ # Validate that config_index is explicitly provided
115
+ if '--config-index' not in sys.argv:
116
+ parser.error("--remove requires explicitly specifying --config-index")
117
+
118
+ # Show config details before asking for confirmation
119
+ configs = load_configs(str(config_path))
120
+
121
+ # Check if index is valid
122
+ if args.config_index < 0 or args.config_index >= len(configs):
123
+ print(f"Error: Configuration index {args.config_index} is out of range. Valid range: 0-{len(configs)-1}")
124
+ return
125
+
126
+ # Show the configuration that will be removed
127
+ config = configs[args.config_index]
128
+ print(f"Configuration to remove (index {args.config_index}):")
129
+ print(f" Provider: {config.get('provider', 'N/A')}")
130
+ print(f" Model: {config.get('model', 'N/A')}")
131
+ print(f" Base URL: {config.get('base_url', 'N/A')}")
132
+ print(f" API Key: {'[Set]' if config.get('api_key') else '[Not Set]'}")
133
+
134
+ # Ask for confirmation
135
+ try:
136
+ print("\nAre you sure you want to remove this configuration? [y/N] ", end='')
137
+ response = input().lower()
138
+ if response in ('y', 'yes'):
139
+ remove_config_entry(config_path, args.config_index)
140
+ else:
141
+ print("Configuration removal cancelled.")
142
+ except KeyboardInterrupt:
143
+ print("\nConfiguration removal cancelled by user.")
144
+
145
+ return
146
+
147
+ # Regular config addition/editing (existing code)
148
+ # If --config-index was not explicitly specified, create a new entry by passing None
149
+ # This will cause add_config_entry to create a new entry at the end of the list
150
+ # Otherwise, edit the existing config at the specified index
151
+ config_index = None if args.config_index == 0 and '--config-index' not in sys.argv else args.config_index
152
+
153
+ # Load existing configs to determine the new index if creating a new config
154
+ configs = load_configs(str(config_path))
155
+ if config_index is None:
156
+ print(f"Creating new configuration at index {len(configs)}")
157
+ else:
158
+ print(f"Editing existing configuration at index {config_index}")
159
+
160
+ add_config_entry(config_path, config_index)
107
161
  return
108
162
 
109
163
  # Load configuration using the specified index (needed for active config display)
ngpt/client.py CHANGED
@@ -10,7 +10,7 @@ class NGPTClient:
10
10
  self,
11
11
  api_key: str = "",
12
12
  base_url: str = "https://api.openai.com/v1/",
13
- provider: str = "OpenAI", # Provider is now just a label, kept for potential future use/logging
13
+ provider: str = "OpenAI",
14
14
  model: str = "gpt-3.5-turbo"
15
15
  ):
16
16
  self.api_key = api_key
ngpt/config.py CHANGED
@@ -51,23 +51,44 @@ def add_config_entry(config_path: Path, config_index: Optional[int] = None) -> N
51
51
  """Add a new configuration entry or update existing one at the specified index."""
52
52
  configs = load_configs(custom_path=str(config_path))
53
53
 
54
- # Create a new entry based on the default
55
- new_entry = DEFAULT_CONFIG_ENTRY.copy()
54
+ # Determine if we're editing an existing config or creating a new one
55
+ is_existing_config = config_index is not None and config_index < len(configs)
56
+
57
+ # Set up entry based on whether we're editing or creating
58
+ if is_existing_config:
59
+ # Use existing config as the base when editing
60
+ entry = configs[config_index].copy()
61
+ print("Enter configuration details (press Enter to keep current values):")
62
+ else:
63
+ # Use default config as the base when creating new
64
+ entry = DEFAULT_CONFIG_ENTRY.copy()
65
+ print("Enter configuration details (press Enter to use default values):")
56
66
 
57
- # Interactive configuration
58
- print("Enter configuration details (press Enter to use default values):")
59
67
  try:
60
- new_entry["api_key"] = input(f"API Key: ") or new_entry["api_key"]
61
- new_entry["base_url"] = input(f"Base URL [{new_entry['base_url']}]: ") or new_entry["base_url"]
62
- new_entry["provider"] = input(f"Provider [{new_entry['provider']}]: ") or new_entry["provider"]
63
- new_entry["model"] = input(f"Model [{new_entry['model']}]: ") or new_entry["model"]
68
+ # For API key, just show the prompt without the current value for security
69
+ user_input = input(f"API Key: ")
70
+ if user_input:
71
+ entry["api_key"] = user_input
72
+
73
+ # For other fields, show current/default value and keep it if Enter is pressed
74
+ user_input = input(f"Base URL [{entry['base_url']}]: ")
75
+ if user_input:
76
+ entry["base_url"] = user_input
77
+
78
+ user_input = input(f"Provider [{entry['provider']}]: ")
79
+ if user_input:
80
+ entry["provider"] = user_input
81
+
82
+ user_input = input(f"Model [{entry['model']}]: ")
83
+ if user_input:
84
+ entry["model"] = user_input
64
85
 
65
86
  # Add or update the entry
66
- if config_index is not None and config_index < len(configs):
67
- configs[config_index] = new_entry
87
+ if is_existing_config:
88
+ configs[config_index] = entry
68
89
  print(f"Updated configuration at index {config_index}")
69
90
  else:
70
- configs.append(new_entry)
91
+ configs.append(entry)
71
92
  print(f"Added new configuration at index {len(configs)-1}")
72
93
 
73
94
  # Save the updated configs
@@ -128,8 +149,7 @@ def load_config(custom_path: Optional[str] = None, config_index: int = 0) -> Dic
128
149
  # Override with environment variables if they exist
129
150
  env_mapping = {
130
151
  "OPENAI_API_KEY": "api_key",
131
- "OPENAI_BASE_URL": "base_url",
132
- "OPENAI_PROVIDER": "provider",
152
+ "OPENAI_BASE_URL": "base_url",
133
153
  "OPENAI_MODEL": "model"
134
154
  }
135
155
 
@@ -137,4 +157,29 @@ def load_config(custom_path: Optional[str] = None, config_index: int = 0) -> Dic
137
157
  if env_var in os.environ and os.environ[env_var]:
138
158
  config[config_key] = os.environ[env_var]
139
159
 
140
- return config
160
+ return config
161
+
162
+ def remove_config_entry(config_path: Path, config_index: int) -> bool:
163
+ """
164
+ Remove a configuration entry at the specified index.
165
+ Returns True if successful, False otherwise.
166
+ """
167
+ configs = load_configs(custom_path=str(config_path))
168
+
169
+ # Check if index is valid
170
+ if config_index < 0 or config_index >= len(configs):
171
+ print(f"Error: Configuration index {config_index} is out of range. Valid range: 0-{len(configs)-1}")
172
+ return False
173
+
174
+ # Remove the config at the specified index
175
+ removed_config = configs.pop(config_index)
176
+
177
+ try:
178
+ # Save the updated configs
179
+ with open(config_path, "w") as f:
180
+ json.dump(configs, f, indent=2)
181
+ print(f"Removed configuration at index {config_index} for provider '{removed_config.get('provider', 'Unknown')}'")
182
+ return True
183
+ except Exception as e:
184
+ print(f"Error saving configuration: {e}")
185
+ return False
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: ngpt
3
- Version: 1.1.3
3
+ Version: 1.2.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
@@ -48,8 +48,10 @@ A lightweight Python CLI and library for interacting with OpenAI-compatible APIs
48
48
  - [Python Library](#as-a-library)
49
49
  - [Configuration](#configuration)
50
50
  - [Command Line Options](#command-line-options)
51
+ - [Interactive Configuration](#interactive-configuration)
51
52
  - [Configuration File](#configuration-file)
52
53
  - [Configuration Priority](#configuration-priority)
54
+ - [Contributing](#contributing)
53
55
  - [License](#license)
54
56
 
55
57
  ## Quick Start
@@ -191,14 +193,36 @@ You can configure the client using the following options:
191
193
  | `--base-url` | Base URL for the API |
192
194
  | `--model` | Model to use |
193
195
  | `--web-search` | Enable web search capability |
194
- | `--config` | Path to a custom configuration file |
196
+ | `--config` | Path to a custom configuration file or, when used without a value, enters interactive configuration mode |
195
197
  | `--config-index` | Index of the configuration to use (default: 0) |
198
+ | `--remove` | Remove the configuration at the specified index (requires --config and --config-index) |
196
199
  | `--show-config` | Show configuration details and exit |
197
200
  | `--all` | Used with `--show-config` to display all configurations |
198
201
  | `-s, --shell` | Generate and execute shell commands |
199
202
  | `-c, --code` | Generate clean code output |
200
203
  | `-v, --version` | Show version information |
201
204
 
205
+ ### Interactive Configuration
206
+
207
+ The `--config` option without arguments enters interactive configuration mode, allowing you to add or edit configurations:
208
+
209
+ ```bash
210
+ # Add a new configuration
211
+ ngpt --config
212
+
213
+ # Edit an existing configuration at index 1
214
+ ngpt --config --config-index 1
215
+
216
+ # Remove a configuration at index 2
217
+ ngpt --config --remove --config-index 2
218
+ ```
219
+
220
+ In interactive mode:
221
+ - When editing an existing configuration, press Enter to keep the current values
222
+ - When creating a new configuration, press Enter to use default values
223
+ - For security, your API key is not displayed when editing configurations
224
+ - When removing a configuration, you'll be asked to confirm before deletion
225
+
202
226
  ### Configuration File
203
227
 
204
228
  nGPT uses a configuration file stored in the standard user config directory for your operating system:
@@ -242,6 +266,20 @@ nGPT determines configuration values in the following order (highest priority fi
242
266
  3. Configuration file (selected by `--config-index`, defaults to index 0)
243
267
  4. Default values
244
268
 
269
+ ## Contributing
270
+
271
+ We welcome contributions to nGPT! Whether it's bug fixes, feature additions, or documentation improvements, your help is appreciated.
272
+
273
+ To contribute:
274
+
275
+ 1. Fork the repository
276
+ 2. Create a feature branch: `git checkout -b feature/your-feature-name`
277
+ 3. Make your changes
278
+ 4. Commit with clear messages following conventional commit guidelines
279
+ 5. Push to your fork and submit a pull request
280
+
281
+ Please check the [CONTRIBUTING.md](CONTRIBUTING.md) file for detailed guidelines on code style, pull request process, and development setup.
282
+
245
283
  ## License
246
284
 
247
285
  This project is licensed under the MIT License. See the [LICENSE](LICENSE) file for details.
@@ -0,0 +1,9 @@
1
+ ngpt/__init__.py,sha256=ehInP9w0MZlS1vZ1g6Cm4YE1ftmgF72CnEddQ3Le9n4,368
2
+ ngpt/cli.py,sha256=tIKXjKfh4K19UcZ0uG8JqTVz-9Lxg-p16dkYTiOmp7o,13582
3
+ ngpt/client.py,sha256=O0dPYeQCJlpWZWBBsroo-5UxeyBVwqC6o3Pm8lRnDiY,10329
4
+ ngpt/config.py,sha256=BF0G3QeiPma8l7EQyc37bR7LWZog7FHJQNe7uj9cr4w,6896
5
+ ngpt-1.2.0.dist-info/METADATA,sha256=o8iptGfiLJbibZZLKYFrg584Ppw6BMLkZuZQfvOzAFk,9497
6
+ ngpt-1.2.0.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
7
+ ngpt-1.2.0.dist-info/entry_points.txt,sha256=1cnAMujyy34DlOahrJg19lePSnb08bLbkUs_kVerqdk,39
8
+ ngpt-1.2.0.dist-info/licenses/LICENSE,sha256=mQkpWoADxbHqE0HRefYLJdm7OpdrXBr3vNv5bZ8w72M,1065
9
+ ngpt-1.2.0.dist-info/RECORD,,
@@ -1,9 +0,0 @@
1
- ngpt/__init__.py,sha256=ehInP9w0MZlS1vZ1g6Cm4YE1ftmgF72CnEddQ3Le9n4,368
2
- ngpt/cli.py,sha256=_nu7eY76_y4KQ59cKC91VijVvzSASAaSJI1FFfJ9l04,10655
3
- ngpt/client.py,sha256=j7UCX_nkFRQJ_15ynxdu0Tj3HxxsI7Ll4__HdTbD7zE,10400
4
- ngpt/config.py,sha256=JWCEp1aMq96i8owi4z_poKigaA_s2UTfzY0fjBM5MoQ,5295
5
- ngpt-1.1.3.dist-info/METADATA,sha256=QTx1AssxeYEUlGOUpDdvssIVlvh2Xl8tqp9-a4wXwWs,8002
6
- ngpt-1.1.3.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
7
- ngpt-1.1.3.dist-info/entry_points.txt,sha256=1cnAMujyy34DlOahrJg19lePSnb08bLbkUs_kVerqdk,39
8
- ngpt-1.1.3.dist-info/licenses/LICENSE,sha256=mQkpWoADxbHqE0HRefYLJdm7OpdrXBr3vNv5bZ8w72M,1065
9
- ngpt-1.1.3.dist-info/RECORD,,
File without changes