ngpt 1.0.0__tar.gz

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-1.0.0/.gitignore ADDED
@@ -0,0 +1,13 @@
1
+ # Python-generated files
2
+ __pycache__/
3
+ *.py[oc]
4
+ build/
5
+ dist/
6
+ wheels/
7
+ *.egg-info
8
+
9
+ # Virtual environments
10
+ .venv
11
+
12
+ # Environment variables
13
+ .env
@@ -0,0 +1 @@
1
+ 3.13
ngpt-1.0.0/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 nazdridoy
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
ngpt-1.0.0/PKG-INFO ADDED
@@ -0,0 +1,180 @@
1
+ Metadata-Version: 2.4
2
+ Name: ngpt
3
+ Version: 1.0.0
4
+ Summary: A Python CLI and library for interacting with custom OpenAI API endpoints
5
+ Project-URL: Homepage, https://github.com/nazdridoy/ngpt
6
+ Project-URL: Repository, https://github.com/nazdridoy/ngpt
7
+ Project-URL: Bug Tracker, https://github.com/nazdridoy/ngpt/issues
8
+ Author-email: nazDridoy <nazdridoy399@gmail.com>
9
+ License: MIT
10
+ License-File: LICENSE
11
+ Keywords: ai,api-client,chatgpt,cli,gpt,gpt4free,llm,ngpt,openai
12
+ Classifier: Environment :: Console
13
+ Classifier: Intended Audience :: Developers
14
+ Classifier: Intended Audience :: End Users/Desktop
15
+ Classifier: Intended Audience :: System Administrators
16
+ Classifier: License :: OSI Approved :: MIT License
17
+ Classifier: Operating System :: OS Independent
18
+ Classifier: Programming Language :: Python :: 3
19
+ Classifier: Programming Language :: Python :: 3.8
20
+ Classifier: Programming Language :: Python :: 3.9
21
+ Classifier: Programming Language :: Python :: 3.10
22
+ Classifier: Programming Language :: Python :: 3.11
23
+ Classifier: Programming Language :: Python :: 3.12
24
+ Classifier: Programming Language :: Python :: 3.13
25
+ Classifier: Topic :: Communications :: Chat
26
+ Classifier: Topic :: Internet :: WWW/HTTP
27
+ Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
28
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
29
+ Classifier: Topic :: Utilities
30
+ Requires-Python: >=3.8
31
+ Requires-Dist: requests>=2.28.0
32
+ Description-Content-Type: text/markdown
33
+
34
+ # nGPT
35
+
36
+ A lightweight Python CLI and library for interacting with custom OpenAI API endpoints.
37
+
38
+ ## Features
39
+
40
+ - Dual mode: Use as a CLI tool or import as a library
41
+ - Minimal dependencies
42
+ - Customizable API endpoints and providers
43
+ - Streaming responses
44
+ - Web search capability (supported by compatible API endpoints)
45
+ - Cross-platform configuration system
46
+ - Experimental features:
47
+ - Shell command generation and execution (OS-aware)
48
+ - Code generation with clean output
49
+
50
+ ## Installation
51
+
52
+ ```bash
53
+ pip install ngpt
54
+ ```
55
+
56
+ ## Usage
57
+
58
+ ### As a CLI Tool
59
+
60
+ ```bash
61
+ # Basic chat (default mode)
62
+ ngpt "Hello, how are you?"
63
+
64
+ # Show version information
65
+ ngpt -v
66
+
67
+ # With custom options
68
+ ngpt --api-key your-key --base-url http://your-endpoint "Hello"
69
+
70
+ # Enable web search (if your API endpoint supports it)
71
+ ngpt --web-search "What's the latest news about AI?"
72
+
73
+ # Generate and execute shell commands (using -s or --shell flag)
74
+ ngpt -s "list all files in current directory"
75
+
76
+ # Generate code (using -c or --code flag)
77
+ ngpt -c "create a python function that calculates fibonacci numbers"
78
+ ```
79
+
80
+ ### As a Library
81
+
82
+ ```python
83
+ from ngpt import NGPTClient, load_config
84
+
85
+ # Load from config file
86
+ config = load_config()
87
+
88
+ # Initialize the client with config
89
+ client = NGPTClient(**config)
90
+
91
+ # Or initialize with custom parameters
92
+ client = NGPTClient(
93
+ api_key="your-key",
94
+ base_url="http://your-endpoint",
95
+ provider="openai",
96
+ model="o3-mini"
97
+ )
98
+
99
+ # Chat
100
+ response = client.chat("Hello, how are you?")
101
+
102
+ # Chat with web search (if your API endpoint supports it)
103
+ response = client.chat("What's the latest news about AI?", web_search=True)
104
+
105
+ # Generate shell command
106
+ command = client.generate_shell_command("list all files")
107
+
108
+ # Generate code
109
+ code = client.generate_code("create a python function that calculates fibonacci numbers")
110
+ ```
111
+
112
+ ## Configuration
113
+
114
+ ### Command Line Options
115
+
116
+ You can configure the client using the following options:
117
+
118
+ - `--api-key`: API key for the service
119
+ - `--base-url`: Base URL for the API
120
+ - `--provider`: Provider name
121
+ - `--model`: Model to use
122
+ - `--web-search`: Enable web search capability (Note: Your API endpoint must support this feature)
123
+ - `--config`: Path to a custom configuration file
124
+
125
+ ### Configuration File
126
+
127
+ nGPT uses a configuration file stored in the standard user config directory for your operating system:
128
+
129
+ - **Linux**: `~/.config/ngpt/ngpt.conf` or `$XDG_CONFIG_HOME/ngpt/ngpt.conf`
130
+ - **macOS**: `~/Library/Application Support/ngpt/ngpt.conf`
131
+ - **Windows**: `%APPDATA%\ngpt\ngpt.conf`
132
+
133
+ The configuration file uses JSON format:
134
+
135
+ #### OpenAI API Example
136
+ ```json
137
+ {
138
+ "api_key": "your_openai_api_key_here",
139
+ "base_url": "https://api.openai.com/v1/",
140
+ "provider": "OpenAI",
141
+ "model": "gpt-3.5-turbo"
142
+ }
143
+ ```
144
+
145
+ #### Custom Endpoint Example
146
+ ```json
147
+ {
148
+ "api_key": "your_api_key_here",
149
+ "base_url": "http://127.0.0.1:1337/v1/",
150
+ "provider": "Blackbox",
151
+ "model": "DeepSeek-V3"
152
+ }
153
+ ```
154
+
155
+ ### Configuration Priority
156
+
157
+ nGPT determines configuration values in the following order (highest priority first):
158
+
159
+ 1. Command line arguments
160
+ 2. Environment variables (`OPENAI_API_KEY`, `OPENAI_BASE_URL`, `OPENAI_PROVIDER`, `OPENAI_MODEL`)
161
+ 3. Configuration file
162
+ 4. Default values
163
+
164
+ ## Special Features
165
+
166
+ ### OS-Aware Shell Commands
167
+
168
+ Shell command generation is OS-aware, providing appropriate commands for your operating system (Windows, macOS, or Linux) and shell type (bash, powershell, etc.).
169
+
170
+ ### Clean Code Generation
171
+
172
+ Code generation uses an improved prompt that ensures only clean code is returned, without markdown formatting or unnecessary explanations.
173
+
174
+ ## Implementation Notes
175
+
176
+ This library uses direct HTTP requests instead of the OpenAI client library, allowing it to work with custom API endpoints that support additional parameters like `provider` and `web_search`. All parameters are sent directly in the request body, similar to the format shown in the curl example.
177
+
178
+ ## License
179
+
180
+ This project is licensed under the MIT License. See the [LICENSE](LICENSE) file for details.
ngpt-1.0.0/README.md ADDED
@@ -0,0 +1,147 @@
1
+ # nGPT
2
+
3
+ A lightweight Python CLI and library for interacting with custom OpenAI API endpoints.
4
+
5
+ ## Features
6
+
7
+ - Dual mode: Use as a CLI tool or import as a library
8
+ - Minimal dependencies
9
+ - Customizable API endpoints and providers
10
+ - Streaming responses
11
+ - Web search capability (supported by compatible API endpoints)
12
+ - Cross-platform configuration system
13
+ - Experimental features:
14
+ - Shell command generation and execution (OS-aware)
15
+ - Code generation with clean output
16
+
17
+ ## Installation
18
+
19
+ ```bash
20
+ pip install ngpt
21
+ ```
22
+
23
+ ## Usage
24
+
25
+ ### As a CLI Tool
26
+
27
+ ```bash
28
+ # Basic chat (default mode)
29
+ ngpt "Hello, how are you?"
30
+
31
+ # Show version information
32
+ ngpt -v
33
+
34
+ # With custom options
35
+ ngpt --api-key your-key --base-url http://your-endpoint "Hello"
36
+
37
+ # Enable web search (if your API endpoint supports it)
38
+ ngpt --web-search "What's the latest news about AI?"
39
+
40
+ # Generate and execute shell commands (using -s or --shell flag)
41
+ ngpt -s "list all files in current directory"
42
+
43
+ # Generate code (using -c or --code flag)
44
+ ngpt -c "create a python function that calculates fibonacci numbers"
45
+ ```
46
+
47
+ ### As a Library
48
+
49
+ ```python
50
+ from ngpt import NGPTClient, load_config
51
+
52
+ # Load from config file
53
+ config = load_config()
54
+
55
+ # Initialize the client with config
56
+ client = NGPTClient(**config)
57
+
58
+ # Or initialize with custom parameters
59
+ client = NGPTClient(
60
+ api_key="your-key",
61
+ base_url="http://your-endpoint",
62
+ provider="openai",
63
+ model="o3-mini"
64
+ )
65
+
66
+ # Chat
67
+ response = client.chat("Hello, how are you?")
68
+
69
+ # Chat with web search (if your API endpoint supports it)
70
+ response = client.chat("What's the latest news about AI?", web_search=True)
71
+
72
+ # Generate shell command
73
+ command = client.generate_shell_command("list all files")
74
+
75
+ # Generate code
76
+ code = client.generate_code("create a python function that calculates fibonacci numbers")
77
+ ```
78
+
79
+ ## Configuration
80
+
81
+ ### Command Line Options
82
+
83
+ You can configure the client using the following options:
84
+
85
+ - `--api-key`: API key for the service
86
+ - `--base-url`: Base URL for the API
87
+ - `--provider`: Provider name
88
+ - `--model`: Model to use
89
+ - `--web-search`: Enable web search capability (Note: Your API endpoint must support this feature)
90
+ - `--config`: Path to a custom configuration file
91
+
92
+ ### Configuration File
93
+
94
+ nGPT uses a configuration file stored in the standard user config directory for your operating system:
95
+
96
+ - **Linux**: `~/.config/ngpt/ngpt.conf` or `$XDG_CONFIG_HOME/ngpt/ngpt.conf`
97
+ - **macOS**: `~/Library/Application Support/ngpt/ngpt.conf`
98
+ - **Windows**: `%APPDATA%\ngpt\ngpt.conf`
99
+
100
+ The configuration file uses JSON format:
101
+
102
+ #### OpenAI API Example
103
+ ```json
104
+ {
105
+ "api_key": "your_openai_api_key_here",
106
+ "base_url": "https://api.openai.com/v1/",
107
+ "provider": "OpenAI",
108
+ "model": "gpt-3.5-turbo"
109
+ }
110
+ ```
111
+
112
+ #### Custom Endpoint Example
113
+ ```json
114
+ {
115
+ "api_key": "your_api_key_here",
116
+ "base_url": "http://127.0.0.1:1337/v1/",
117
+ "provider": "Blackbox",
118
+ "model": "DeepSeek-V3"
119
+ }
120
+ ```
121
+
122
+ ### Configuration Priority
123
+
124
+ nGPT determines configuration values in the following order (highest priority first):
125
+
126
+ 1. Command line arguments
127
+ 2. Environment variables (`OPENAI_API_KEY`, `OPENAI_BASE_URL`, `OPENAI_PROVIDER`, `OPENAI_MODEL`)
128
+ 3. Configuration file
129
+ 4. Default values
130
+
131
+ ## Special Features
132
+
133
+ ### OS-Aware Shell Commands
134
+
135
+ Shell command generation is OS-aware, providing appropriate commands for your operating system (Windows, macOS, or Linux) and shell type (bash, powershell, etc.).
136
+
137
+ ### Clean Code Generation
138
+
139
+ Code generation uses an improved prompt that ensures only clean code is returned, without markdown formatting or unnecessary explanations.
140
+
141
+ ## Implementation Notes
142
+
143
+ This library uses direct HTTP requests instead of the OpenAI client library, allowing it to work with custom API endpoints that support additional parameters like `provider` and `web_search`. All parameters are sent directly in the request body, similar to the format shown in the curl example.
144
+
145
+ ## License
146
+
147
+ This project is licensed under the MIT License. See the [LICENSE](LICENSE) file for details.
@@ -0,0 +1,15 @@
1
+ try:
2
+ from importlib.metadata import version as get_version
3
+ __version__ = get_version("ngpt")
4
+ except ImportError:
5
+ # For Python < 3.8 or package not installed
6
+ __version__ = "1.0.0" # fallback version
7
+
8
+ from .client import NGPTClient
9
+ from .config import load_config, get_config_path, get_config_dir
10
+
11
+ __all__ = ["NGPTClient", "__version__", "load_config", "get_config_path", "get_config_dir"]
12
+
13
+ # Import cli last to avoid circular imports
14
+ from .cli import main
15
+ __all__.append("main")
ngpt-1.0.0/ngpt/cli.py ADDED
@@ -0,0 +1,167 @@
1
+ import argparse
2
+ import sys
3
+ import os
4
+ from .client import NGPTClient
5
+ from .config import load_config, get_config_path
6
+ from . import __version__
7
+
8
+ def show_config_help():
9
+ """Display help information about configuration."""
10
+ print("\nConfiguration Help:")
11
+ print(" 1. Create a config file at one of these locations:")
12
+ if sys.platform == "win32":
13
+ print(f" - %APPDATA%\\ngpt\\ngpt.conf")
14
+ elif sys.platform == "darwin":
15
+ print(f" - ~/Library/Application Support/ngpt/ngpt.conf")
16
+ else:
17
+ print(f" - ~/.config/ngpt/ngpt.conf")
18
+
19
+ print(" 2. Format your config file as JSON:")
20
+ print(""" {
21
+ "api_key": "your-api-key-here",
22
+ "base_url": "https://api.openai.com/v1/",
23
+ "provider": "OpenAI",
24
+ "model": "gpt-3.5-turbo"
25
+ }""")
26
+
27
+ print(" 3. Or set environment variables:")
28
+ print(" - OPENAI_API_KEY")
29
+ print(" - OPENAI_BASE_URL")
30
+ print(" - OPENAI_PROVIDER")
31
+ print(" - OPENAI_MODEL")
32
+
33
+ print(" 4. Or provide command line arguments:")
34
+ print(" ngpt --api-key your-key --base-url https://api.example.com \"Your prompt\"")
35
+
36
+ def check_config(config):
37
+ """Check config for common issues and provide guidance."""
38
+ if not config.get("api_key"):
39
+ print("Error: API key is not set.")
40
+ show_config_help()
41
+ return False
42
+
43
+ # Check for common URL mistakes
44
+ base_url = config.get("base_url", "")
45
+ if base_url and not (base_url.startswith("http://") or base_url.startswith("https://")):
46
+ print(f"Warning: Base URL '{base_url}' doesn't start with http:// or https://")
47
+
48
+ return True
49
+
50
+ def main():
51
+ parser = argparse.ArgumentParser(description="nGPT - A CLI tool for interacting with custom OpenAI API endpoints")
52
+
53
+ # Version flag
54
+ parser.add_argument('-v', '--version', action='version', version=f'nGPT {__version__}', help='Show version information and exit')
55
+
56
+ # Config option
57
+ parser.add_argument('--config', help='Path to a custom configuration file')
58
+
59
+ # Global options
60
+ parser.add_argument('--api-key', help='API key for the service')
61
+ parser.add_argument('--base-url', help='Base URL for the API')
62
+ parser.add_argument('--provider', help='Provider name')
63
+ parser.add_argument('--model', help='Model to use')
64
+ parser.add_argument('--web-search', action='store_true',
65
+ help='Enable web search capability (Note: Your API endpoint must support this feature)')
66
+
67
+ # Mode flags (mutually exclusive)
68
+ mode_group = parser.add_mutually_exclusive_group()
69
+ mode_group.add_argument('-s', '--shell', action='store_true', help='Generate and execute shell commands')
70
+ mode_group.add_argument('-c', '--code', action='store_true', help='Generate code')
71
+ mode_group.add_argument('--show-config', action='store_true', help='Show the current configuration and exit')
72
+
73
+ # Language option for code mode
74
+ parser.add_argument('--language', default="python", help='Programming language to generate code in (for code mode)')
75
+
76
+ # Prompt argument
77
+ parser.add_argument('prompt', nargs='?', default=None, help='The prompt to send')
78
+
79
+ args = parser.parse_args()
80
+
81
+ # Load configuration
82
+ config = load_config(args.config)
83
+
84
+ # Command-line arguments override config settings
85
+ if args.api_key:
86
+ config["api_key"] = args.api_key
87
+ if args.base_url:
88
+ config["base_url"] = args.base_url
89
+ if args.provider:
90
+ config["provider"] = args.provider
91
+ if args.model:
92
+ config["model"] = args.model
93
+
94
+ # Show config if requested
95
+ if args.show_config:
96
+ config_path = get_config_path(args.config)
97
+ print(f"Configuration file: {config_path}")
98
+ print(f"API Key: {'[Set]' if config['api_key'] else '[Not Set]'}")
99
+ print(f"Base URL: {config['base_url']}")
100
+ print(f"Provider: {config['provider']}")
101
+ print(f"Model: {config['model']}")
102
+ return
103
+
104
+ # Check if prompt is required but not provided
105
+ if not args.prompt and not (args.shell or args.code):
106
+ parser.print_help()
107
+ return
108
+
109
+ # Check configuration
110
+ if not check_config(config):
111
+ return
112
+
113
+ # Initialize client
114
+ client = NGPTClient(**config)
115
+
116
+ try:
117
+ # Handle modes
118
+ if args.shell:
119
+ if args.prompt is None:
120
+ print("Enter shell command description: ", end='')
121
+ prompt = input()
122
+ else:
123
+ prompt = args.prompt
124
+
125
+ command = client.generate_shell_command(prompt, web_search=args.web_search)
126
+ if not command:
127
+ return # Error already printed by client
128
+
129
+ print(f"\nGenerated command: {command}")
130
+
131
+ print("Do you want to execute this command? [y/N] ", end='')
132
+ response = input().lower()
133
+ if response == 'y' or response == 'yes':
134
+ import subprocess
135
+ try:
136
+ result = subprocess.run(command, shell=True, check=True, capture_output=True, text=True)
137
+ print(f"\nOutput:\n{result.stdout}")
138
+ except subprocess.CalledProcessError as e:
139
+ print(f"\nError:\n{e.stderr}")
140
+
141
+ elif args.code:
142
+ if args.prompt is None:
143
+ print("Enter code description: ", end='')
144
+ prompt = input()
145
+ else:
146
+ prompt = args.prompt
147
+
148
+ generated_code = client.generate_code(prompt, args.language, web_search=args.web_search)
149
+ if generated_code:
150
+ print(f"\nGenerated code:\n{generated_code}")
151
+
152
+ else:
153
+ # Default to chat mode
154
+ if args.prompt is None:
155
+ print("Enter your prompt: ", end='')
156
+ prompt = input()
157
+ else:
158
+ prompt = args.prompt
159
+ client.chat(prompt, web_search=args.web_search)
160
+
161
+ except KeyboardInterrupt:
162
+ print("\nOperation cancelled by user.")
163
+ except Exception as e:
164
+ print(f"Error: {e}")
165
+
166
+ if __name__ == "__main__":
167
+ main()
@@ -0,0 +1,253 @@
1
+ from typing import Optional, Dict, Any, List
2
+ import os
3
+ import json
4
+ import requests
5
+ import platform
6
+ import subprocess
7
+
8
+ class NGPTClient:
9
+ def __init__(
10
+ self,
11
+ api_key: str = "",
12
+ base_url: str = "https://api.openai.com/v1/",
13
+ provider: str = "OpenAI",
14
+ model: str = "gpt-3.5-turbo"
15
+ ):
16
+ self.api_key = api_key
17
+ # Ensure base_url ends with /
18
+ self.base_url = base_url if base_url.endswith('/') else base_url + '/'
19
+ self.provider = provider
20
+ self.model = model
21
+
22
+ # Default headers
23
+ self.headers = {
24
+ "Content-Type": "application/json",
25
+ "Authorization": f"Bearer {self.api_key}"
26
+ }
27
+
28
+ def chat(
29
+ self,
30
+ prompt: str,
31
+ stream: bool = True,
32
+ temperature: float = 0.7,
33
+ max_tokens: Optional[int] = None,
34
+ messages: Optional[List[Dict[str, str]]] = None,
35
+ web_search: bool = False,
36
+ **kwargs
37
+ ) -> str:
38
+ """
39
+ Send a chat message to the API and get a response.
40
+
41
+ Args:
42
+ prompt: The user's message
43
+ stream: Whether to stream the response
44
+ temperature: Controls randomness in the response
45
+ max_tokens: Maximum number of tokens to generate
46
+ messages: Optional list of message objects to override default behavior
47
+ web_search: Whether to enable web search capability
48
+ **kwargs: Additional arguments to pass to the API
49
+
50
+ Returns:
51
+ The generated response as a string
52
+ """
53
+ if not self.api_key:
54
+ print("Error: API key is not set. Please configure your API key in the config file or provide it with --api-key.")
55
+ return ""
56
+
57
+ if messages is None:
58
+ messages = [{"role": "user", "content": prompt}]
59
+
60
+ # Prepare API parameters
61
+ payload = {
62
+ "model": self.model,
63
+ "messages": messages,
64
+ "stream": stream,
65
+ "temperature": temperature,
66
+ "provider": self.provider,
67
+ "web_search": web_search
68
+ }
69
+
70
+ # Add max_tokens if provided
71
+ if max_tokens is not None:
72
+ payload["max_tokens"] = max_tokens
73
+
74
+ # Add any additional parameters
75
+ payload.update(kwargs)
76
+
77
+ # Endpoint for chat completions
78
+ endpoint = "chat/completions"
79
+ url = f"{self.base_url}{endpoint}"
80
+
81
+ try:
82
+ if not stream:
83
+ # Regular request
84
+ response = requests.post(url, headers=self.headers, json=payload)
85
+ response.raise_for_status() # Raise exception for HTTP errors
86
+ result = response.json()
87
+
88
+ # Extract content from response
89
+ if "choices" in result and len(result["choices"]) > 0:
90
+ return result["choices"][0]["message"]["content"]
91
+ return ""
92
+ else:
93
+ # Streaming request
94
+ collected_content = ""
95
+ with requests.post(url, headers=self.headers, json=payload, stream=True) as response:
96
+ response.raise_for_status() # Raise exception for HTTP errors
97
+
98
+ for line in response.iter_lines():
99
+ if not line:
100
+ continue
101
+
102
+ # Handle SSE format
103
+ line = line.decode('utf-8')
104
+ if line.startswith('data: '):
105
+ line = line[6:] # Remove 'data: ' prefix
106
+
107
+ # Skip keep-alive lines
108
+ if line == "[DONE]":
109
+ break
110
+
111
+ try:
112
+ chunk = json.loads(line)
113
+ if "choices" in chunk and len(chunk["choices"]) > 0:
114
+ delta = chunk["choices"][0].get("delta", {})
115
+ content = delta.get("content", "")
116
+ if content:
117
+ print(content, end="", flush=True)
118
+ collected_content += content
119
+ except json.JSONDecodeError:
120
+ pass # Skip invalid JSON
121
+
122
+ print() # Add a final newline
123
+ return collected_content
124
+
125
+ except requests.exceptions.HTTPError as e:
126
+ if e.response.status_code == 401:
127
+ print("Error: Authentication failed. Please check your API key.")
128
+ elif e.response.status_code == 404:
129
+ print(f"Error: Endpoint not found at {url}")
130
+ elif e.response.status_code == 429:
131
+ print("Error: Rate limit exceeded. Please try again later.")
132
+ else:
133
+ print(f"HTTP Error: {e}")
134
+ return ""
135
+
136
+ except requests.exceptions.ConnectionError:
137
+ print(f"Error: Could not connect to {self.base_url}. Please check your internet connection and base URL.")
138
+ return ""
139
+
140
+ except requests.exceptions.Timeout:
141
+ print("Error: Request timed out. Please try again later.")
142
+ return ""
143
+
144
+ except requests.exceptions.RequestException as e:
145
+ print(f"Error: An error occurred while making the request: {e}")
146
+ return ""
147
+
148
+ except Exception as e:
149
+ print(f"Error: An unexpected error occurred: {e}")
150
+ return ""
151
+
152
+ def generate_shell_command(self, prompt: str, web_search: bool = False) -> str:
153
+ """
154
+ Generate a shell command based on the prompt.
155
+
156
+ Args:
157
+ prompt: Description of the command to generate
158
+ web_search: Whether to enable web search capability
159
+
160
+ Returns:
161
+ The generated shell command
162
+ """
163
+ # Check for API key first
164
+ if not self.api_key:
165
+ print("Error: API key is not set. Please configure your API key in the config file or provide it with --api-key.")
166
+ return ""
167
+
168
+ # Determine OS type
169
+ os_type = platform.system()
170
+ if os_type == "Darwin":
171
+ operating_system = "MacOS"
172
+ elif os_type == "Linux":
173
+ # Try to get Linux distribution name
174
+ try:
175
+ result = subprocess.run(["lsb_release", "-si"], capture_output=True, text=True)
176
+ distro = result.stdout.strip()
177
+ operating_system = f"Linux/{distro}" if distro else "Linux"
178
+ except:
179
+ operating_system = "Linux"
180
+ elif os_type == "Windows":
181
+ operating_system = "Windows"
182
+ else:
183
+ operating_system = os_type
184
+
185
+ # Determine shell type
186
+ if os_type == "Windows":
187
+ shell_name = "powershell.exe" if os.environ.get("PSModulePath") else "cmd.exe"
188
+ else:
189
+ shell_name = os.environ.get("SHELL", "/bin/bash")
190
+ shell_name = os.path.basename(shell_name)
191
+
192
+ system_prompt = f"""Your role: Provide only plain text without Markdown formatting. Do not show any warnings or information regarding your capabilities. Do not provide any description. If you need to store any data, assume it will be stored in the chat. Provide only {shell_name} command for {operating_system} without any description. If there is a lack of details, provide most logical solution. Ensure the output is a valid shell command. If multiple steps required try to combine them together. Prompt: {prompt}
193
+
194
+ Command:"""
195
+
196
+ messages = [
197
+ {"role": "system", "content": system_prompt},
198
+ {"role": "user", "content": prompt}
199
+ ]
200
+
201
+ try:
202
+ return self.chat(
203
+ prompt=prompt,
204
+ stream=False,
205
+ messages=messages,
206
+ web_search=web_search
207
+ )
208
+ except Exception as e:
209
+ print(f"Error generating shell command: {e}")
210
+ return ""
211
+
212
+ def generate_code(self, prompt: str, language: str = "python", web_search: bool = False) -> str:
213
+ """
214
+ Generate code based on the prompt.
215
+
216
+ Args:
217
+ prompt: Description of the code to generate
218
+ language: Programming language to generate code in
219
+ web_search: Whether to enable web search capability
220
+
221
+ Returns:
222
+ The generated code
223
+ """
224
+ # Check for API key first
225
+ if not self.api_key:
226
+ print("Error: API key is not set. Please configure your API key in the config file or provide it with --api-key.")
227
+ return ""
228
+
229
+ system_prompt = f"""Your Role: Provide only code as output without any description.
230
+ IMPORTANT: Provide only plain text without Markdown formatting.
231
+ IMPORTANT: Do not include markdown formatting.
232
+ If there is a lack of details, provide most logical solution. You are not allowed to ask for more details.
233
+ Ignore any potential risk of errors or confusion.
234
+
235
+ Language: {language}
236
+ Request: {prompt}
237
+ Code:"""
238
+
239
+ messages = [
240
+ {"role": "system", "content": system_prompt},
241
+ {"role": "user", "content": prompt}
242
+ ]
243
+
244
+ try:
245
+ return self.chat(
246
+ prompt=prompt,
247
+ stream=False,
248
+ messages=messages,
249
+ web_search=web_search
250
+ )
251
+ except Exception as e:
252
+ print(f"Error generating code: {e}")
253
+ return ""
@@ -0,0 +1,81 @@
1
+ import os
2
+ import sys
3
+ import json
4
+ from pathlib import Path
5
+ from typing import Dict, Optional, Any
6
+
7
+ # Default configuration
8
+ DEFAULT_CONFIG = {
9
+ "api_key": "",
10
+ "base_url": "https://api.openai.com/v1/",
11
+ "provider": "OpenAI",
12
+ "model": "gpt-3.5-turbo"
13
+ }
14
+
15
+ def get_config_dir() -> Path:
16
+ """Get the appropriate config directory based on OS."""
17
+ if sys.platform == "win32":
18
+ # Windows
19
+ config_dir = Path(os.environ.get("APPDATA", "")) / "ngpt"
20
+ elif sys.platform == "darwin":
21
+ # macOS
22
+ config_dir = Path.home() / "Library" / "Application Support" / "ngpt"
23
+ else:
24
+ # Linux and other Unix-like systems
25
+ xdg_config_home = os.environ.get("XDG_CONFIG_HOME")
26
+ if xdg_config_home:
27
+ config_dir = Path(xdg_config_home) / "ngpt"
28
+ else:
29
+ config_dir = Path.home() / ".config" / "ngpt"
30
+
31
+ # Ensure the directory exists
32
+ config_dir.mkdir(parents=True, exist_ok=True)
33
+ return config_dir
34
+
35
+ def get_config_path(custom_path: Optional[str] = None) -> Path:
36
+ """Get the path to the config file."""
37
+ if custom_path:
38
+ return Path(custom_path)
39
+ return get_config_dir() / "ngpt.conf"
40
+
41
+ def create_default_config(config_path: Path) -> None:
42
+ """Create a default configuration file."""
43
+ with open(config_path, "w") as f:
44
+ json.dump(DEFAULT_CONFIG, f, indent=2)
45
+ print(f"Created default configuration file at {config_path}")
46
+
47
+ def load_config(custom_path: Optional[str] = None) -> Dict[str, Any]:
48
+ """
49
+ Load configuration from file and environment variables.
50
+ Environment variables take precedence over the config file.
51
+ """
52
+ config_path = get_config_path(custom_path)
53
+
54
+ # Start with default config
55
+ config = DEFAULT_CONFIG.copy()
56
+
57
+ # Load from config file if it exists
58
+ if config_path.exists():
59
+ try:
60
+ with open(config_path, "r") as f:
61
+ file_config = json.load(f)
62
+ config.update(file_config)
63
+ except (json.JSONDecodeError, IOError) as e:
64
+ print(f"Warning: Could not read config file: {e}", file=sys.stderr)
65
+ else:
66
+ # Create default config file if it doesn't exist
67
+ create_default_config(config_path)
68
+
69
+ # Override with environment variables if they exist
70
+ env_mapping = {
71
+ "OPENAI_API_KEY": "api_key",
72
+ "OPENAI_BASE_URL": "base_url",
73
+ "OPENAI_PROVIDER": "provider",
74
+ "OPENAI_MODEL": "model"
75
+ }
76
+
77
+ for env_var, config_key in env_mapping.items():
78
+ if env_var in os.environ and os.environ[env_var]:
79
+ config[config_key] = os.environ[env_var]
80
+
81
+ return config
@@ -0,0 +1,49 @@
1
+ [project]
2
+ name = "ngpt"
3
+ version = "1.0.0"
4
+ description = "A Python CLI and library for interacting with custom OpenAI API endpoints"
5
+ authors = [
6
+ {name = "nazDridoy", email = "nazdridoy399@gmail.com"},
7
+ ]
8
+ dependencies = [
9
+ "requests>=2.28.0",
10
+ ]
11
+ requires-python = ">=3.8"
12
+ readme = "README.md"
13
+ license = {text = "MIT"}
14
+ keywords = ["openai", "cli", "api-client", "llm", "ai", "gpt", "chatgpt", "gpt4free", "ngpt"]
15
+ classifiers = [
16
+ "Programming Language :: Python :: 3",
17
+ "Programming Language :: Python :: 3.8",
18
+ "Programming Language :: Python :: 3.9",
19
+ "Programming Language :: Python :: 3.10",
20
+ "Programming Language :: Python :: 3.11",
21
+ "Programming Language :: Python :: 3.12",
22
+ "Programming Language :: Python :: 3.13",
23
+ "License :: OSI Approved :: MIT License",
24
+ "Operating System :: OS Independent",
25
+ "Topic :: Software Development :: Libraries :: Python Modules",
26
+ "Topic :: Utilities",
27
+ "Topic :: Communications :: Chat",
28
+ "Topic :: Scientific/Engineering :: Artificial Intelligence",
29
+ "Topic :: Internet :: WWW/HTTP",
30
+ "Environment :: Console",
31
+ "Intended Audience :: Developers",
32
+ "Intended Audience :: End Users/Desktop",
33
+ "Intended Audience :: System Administrators",
34
+ ]
35
+
36
+ [project.urls]
37
+ "Homepage" = "https://github.com/nazdridoy/ngpt"
38
+ "Repository" = "https://github.com/nazdridoy/ngpt"
39
+ "Bug Tracker" = "https://github.com/nazdridoy/ngpt/issues"
40
+
41
+ [project.scripts]
42
+ ngpt = "ngpt.cli:main"
43
+
44
+ [build-system]
45
+ requires = ["hatchling"]
46
+ build-backend = "hatchling.build"
47
+
48
+ [dependency-groups]
49
+ dev = []
ngpt-1.0.0/uv.lock ADDED
@@ -0,0 +1,166 @@
1
+ version = 1
2
+ revision = 1
3
+ requires-python = ">=3.8"
4
+ resolution-markers = [
5
+ "python_full_version >= '3.9'",
6
+ "python_full_version < '3.9'",
7
+ ]
8
+
9
+ [[package]]
10
+ name = "certifi"
11
+ version = "2025.1.31"
12
+ source = { registry = "https://pypi.org/simple" }
13
+ sdist = { url = "https://files.pythonhosted.org/packages/1c/ab/c9f1e32b7b1bf505bf26f0ef697775960db7932abeb7b516de930ba2705f/certifi-2025.1.31.tar.gz", hash = "sha256:3d5da6925056f6f18f119200434a4780a94263f10d1c21d032a6f6b2baa20651", size = 167577 }
14
+ wheels = [
15
+ { url = "https://files.pythonhosted.org/packages/38/fc/bce832fd4fd99766c04d1ee0eead6b0ec6486fb100ae5e74c1d91292b982/certifi-2025.1.31-py3-none-any.whl", hash = "sha256:ca78db4565a652026a4db2bcdf68f2fb589ea80d0be70e03929ed730746b84fe", size = 166393 },
16
+ ]
17
+
18
+ [[package]]
19
+ name = "charset-normalizer"
20
+ version = "3.4.1"
21
+ source = { registry = "https://pypi.org/simple" }
22
+ sdist = { url = "https://files.pythonhosted.org/packages/16/b0/572805e227f01586461c80e0fd25d65a2115599cc9dad142fee4b747c357/charset_normalizer-3.4.1.tar.gz", hash = "sha256:44251f18cd68a75b56585dd00dae26183e102cd5e0f9f1466e6df5da2ed64ea3", size = 123188 }
23
+ wheels = [
24
+ { url = "https://files.pythonhosted.org/packages/0d/58/5580c1716040bc89206c77d8f74418caf82ce519aae06450393ca73475d1/charset_normalizer-3.4.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:91b36a978b5ae0ee86c394f5a54d6ef44db1de0815eb43de826d41d21e4af3de", size = 198013 },
25
+ { url = "https://files.pythonhosted.org/packages/d0/11/00341177ae71c6f5159a08168bcb98c6e6d196d372c94511f9f6c9afe0c6/charset_normalizer-3.4.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7461baadb4dc00fd9e0acbe254e3d7d2112e7f92ced2adc96e54ef6501c5f176", size = 141285 },
26
+ { url = "https://files.pythonhosted.org/packages/01/09/11d684ea5819e5a8f5100fb0b38cf8d02b514746607934134d31233e02c8/charset_normalizer-3.4.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e218488cd232553829be0664c2292d3af2eeeb94b32bea483cf79ac6a694e037", size = 151449 },
27
+ { url = "https://files.pythonhosted.org/packages/08/06/9f5a12939db324d905dc1f70591ae7d7898d030d7662f0d426e2286f68c9/charset_normalizer-3.4.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:80ed5e856eb7f30115aaf94e4a08114ccc8813e6ed1b5efa74f9f82e8509858f", size = 143892 },
28
+ { url = "https://files.pythonhosted.org/packages/93/62/5e89cdfe04584cb7f4d36003ffa2936681b03ecc0754f8e969c2becb7e24/charset_normalizer-3.4.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b010a7a4fd316c3c484d482922d13044979e78d1861f0e0650423144c616a46a", size = 146123 },
29
+ { url = "https://files.pythonhosted.org/packages/a9/ac/ab729a15c516da2ab70a05f8722ecfccc3f04ed7a18e45c75bbbaa347d61/charset_normalizer-3.4.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4532bff1b8421fd0a320463030c7520f56a79c9024a4e88f01c537316019005a", size = 147943 },
30
+ { url = "https://files.pythonhosted.org/packages/03/d2/3f392f23f042615689456e9a274640c1d2e5dd1d52de36ab8f7955f8f050/charset_normalizer-3.4.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:d973f03c0cb71c5ed99037b870f2be986c3c05e63622c017ea9816881d2dd247", size = 142063 },
31
+ { url = "https://files.pythonhosted.org/packages/f2/e3/e20aae5e1039a2cd9b08d9205f52142329f887f8cf70da3650326670bddf/charset_normalizer-3.4.1-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:3a3bd0dcd373514dcec91c411ddb9632c0d7d92aed7093b8c3bbb6d69ca74408", size = 150578 },
32
+ { url = "https://files.pythonhosted.org/packages/8d/af/779ad72a4da0aed925e1139d458adc486e61076d7ecdcc09e610ea8678db/charset_normalizer-3.4.1-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:d9c3cdf5390dcd29aa8056d13e8e99526cda0305acc038b96b30352aff5ff2bb", size = 153629 },
33
+ { url = "https://files.pythonhosted.org/packages/c2/b6/7aa450b278e7aa92cf7732140bfd8be21f5f29d5bf334ae987c945276639/charset_normalizer-3.4.1-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:2bdfe3ac2e1bbe5b59a1a63721eb3b95fc9b6817ae4a46debbb4e11f6232428d", size = 150778 },
34
+ { url = "https://files.pythonhosted.org/packages/39/f4/d9f4f712d0951dcbfd42920d3db81b00dd23b6ab520419626f4023334056/charset_normalizer-3.4.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:eab677309cdb30d047996b36d34caeda1dc91149e4fdca0b1a039b3f79d9a807", size = 146453 },
35
+ { url = "https://files.pythonhosted.org/packages/49/2b/999d0314e4ee0cff3cb83e6bc9aeddd397eeed693edb4facb901eb8fbb69/charset_normalizer-3.4.1-cp310-cp310-win32.whl", hash = "sha256:c0429126cf75e16c4f0ad00ee0eae4242dc652290f940152ca8c75c3a4b6ee8f", size = 95479 },
36
+ { url = "https://files.pythonhosted.org/packages/2d/ce/3cbed41cff67e455a386fb5e5dd8906cdda2ed92fbc6297921f2e4419309/charset_normalizer-3.4.1-cp310-cp310-win_amd64.whl", hash = "sha256:9f0b8b1c6d84c8034a44893aba5e767bf9c7a211e313a9605d9c617d7083829f", size = 102790 },
37
+ { url = "https://files.pythonhosted.org/packages/72/80/41ef5d5a7935d2d3a773e3eaebf0a9350542f2cab4eac59a7a4741fbbbbe/charset_normalizer-3.4.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:8bfa33f4f2672964266e940dd22a195989ba31669bd84629f05fab3ef4e2d125", size = 194995 },
38
+ { url = "https://files.pythonhosted.org/packages/7a/28/0b9fefa7b8b080ec492110af6d88aa3dea91c464b17d53474b6e9ba5d2c5/charset_normalizer-3.4.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:28bf57629c75e810b6ae989f03c0828d64d6b26a5e205535585f96093e405ed1", size = 139471 },
39
+ { url = "https://files.pythonhosted.org/packages/71/64/d24ab1a997efb06402e3fc07317e94da358e2585165930d9d59ad45fcae2/charset_normalizer-3.4.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f08ff5e948271dc7e18a35641d2f11a4cd8dfd5634f55228b691e62b37125eb3", size = 149831 },
40
+ { url = "https://files.pythonhosted.org/packages/37/ed/be39e5258e198655240db5e19e0b11379163ad7070962d6b0c87ed2c4d39/charset_normalizer-3.4.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:234ac59ea147c59ee4da87a0c0f098e9c8d169f4dc2a159ef720f1a61bbe27cd", size = 142335 },
41
+ { url = "https://files.pythonhosted.org/packages/88/83/489e9504711fa05d8dde1574996408026bdbdbd938f23be67deebb5eca92/charset_normalizer-3.4.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fd4ec41f914fa74ad1b8304bbc634b3de73d2a0889bd32076342a573e0779e00", size = 143862 },
42
+ { url = "https://files.pythonhosted.org/packages/c6/c7/32da20821cf387b759ad24627a9aca289d2822de929b8a41b6241767b461/charset_normalizer-3.4.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:eea6ee1db730b3483adf394ea72f808b6e18cf3cb6454b4d86e04fa8c4327a12", size = 145673 },
43
+ { url = "https://files.pythonhosted.org/packages/68/85/f4288e96039abdd5aeb5c546fa20a37b50da71b5cf01e75e87f16cd43304/charset_normalizer-3.4.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:c96836c97b1238e9c9e3fe90844c947d5afbf4f4c92762679acfe19927d81d77", size = 140211 },
44
+ { url = "https://files.pythonhosted.org/packages/28/a3/a42e70d03cbdabc18997baf4f0227c73591a08041c149e710045c281f97b/charset_normalizer-3.4.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:4d86f7aff21ee58f26dcf5ae81a9addbd914115cdebcbb2217e4f0ed8982e146", size = 148039 },
45
+ { url = "https://files.pythonhosted.org/packages/85/e4/65699e8ab3014ecbe6f5c71d1a55d810fb716bbfd74f6283d5c2aa87febf/charset_normalizer-3.4.1-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:09b5e6733cbd160dcc09589227187e242a30a49ca5cefa5a7edd3f9d19ed53fd", size = 151939 },
46
+ { url = "https://files.pythonhosted.org/packages/b1/82/8e9fe624cc5374193de6860aba3ea8070f584c8565ee77c168ec13274bd2/charset_normalizer-3.4.1-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:5777ee0881f9499ed0f71cc82cf873d9a0ca8af166dfa0af8ec4e675b7df48e6", size = 149075 },
47
+ { url = "https://files.pythonhosted.org/packages/3d/7b/82865ba54c765560c8433f65e8acb9217cb839a9e32b42af4aa8e945870f/charset_normalizer-3.4.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:237bdbe6159cff53b4f24f397d43c6336c6b0b42affbe857970cefbb620911c8", size = 144340 },
48
+ { url = "https://files.pythonhosted.org/packages/b5/b6/9674a4b7d4d99a0d2df9b215da766ee682718f88055751e1e5e753c82db0/charset_normalizer-3.4.1-cp311-cp311-win32.whl", hash = "sha256:8417cb1f36cc0bc7eaba8ccb0e04d55f0ee52df06df3ad55259b9a323555fc8b", size = 95205 },
49
+ { url = "https://files.pythonhosted.org/packages/1e/ab/45b180e175de4402dcf7547e4fb617283bae54ce35c27930a6f35b6bef15/charset_normalizer-3.4.1-cp311-cp311-win_amd64.whl", hash = "sha256:d7f50a1f8c450f3925cb367d011448c39239bb3eb4117c36a6d354794de4ce76", size = 102441 },
50
+ { url = "https://files.pythonhosted.org/packages/0a/9a/dd1e1cdceb841925b7798369a09279bd1cf183cef0f9ddf15a3a6502ee45/charset_normalizer-3.4.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:73d94b58ec7fecbc7366247d3b0b10a21681004153238750bb67bd9012414545", size = 196105 },
51
+ { url = "https://files.pythonhosted.org/packages/d3/8c/90bfabf8c4809ecb648f39794cf2a84ff2e7d2a6cf159fe68d9a26160467/charset_normalizer-3.4.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dad3e487649f498dd991eeb901125411559b22e8d7ab25d3aeb1af367df5efd7", size = 140404 },
52
+ { url = "https://files.pythonhosted.org/packages/ad/8f/e410d57c721945ea3b4f1a04b74f70ce8fa800d393d72899f0a40526401f/charset_normalizer-3.4.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c30197aa96e8eed02200a83fba2657b4c3acd0f0aa4bdc9f6c1af8e8962e0757", size = 150423 },
53
+ { url = "https://files.pythonhosted.org/packages/f0/b8/e6825e25deb691ff98cf5c9072ee0605dc2acfca98af70c2d1b1bc75190d/charset_normalizer-3.4.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2369eea1ee4a7610a860d88f268eb39b95cb588acd7235e02fd5a5601773d4fa", size = 143184 },
54
+ { url = "https://files.pythonhosted.org/packages/3e/a2/513f6cbe752421f16d969e32f3583762bfd583848b763913ddab8d9bfd4f/charset_normalizer-3.4.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bc2722592d8998c870fa4e290c2eec2c1569b87fe58618e67d38b4665dfa680d", size = 145268 },
55
+ { url = "https://files.pythonhosted.org/packages/74/94/8a5277664f27c3c438546f3eb53b33f5b19568eb7424736bdc440a88a31f/charset_normalizer-3.4.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ffc9202a29ab3920fa812879e95a9e78b2465fd10be7fcbd042899695d75e616", size = 147601 },
56
+ { url = "https://files.pythonhosted.org/packages/7c/5f/6d352c51ee763623a98e31194823518e09bfa48be2a7e8383cf691bbb3d0/charset_normalizer-3.4.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:804a4d582ba6e5b747c625bf1255e6b1507465494a40a2130978bda7b932c90b", size = 141098 },
57
+ { url = "https://files.pythonhosted.org/packages/78/d4/f5704cb629ba5ab16d1d3d741396aec6dc3ca2b67757c45b0599bb010478/charset_normalizer-3.4.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:0f55e69f030f7163dffe9fd0752b32f070566451afe180f99dbeeb81f511ad8d", size = 149520 },
58
+ { url = "https://files.pythonhosted.org/packages/c5/96/64120b1d02b81785f222b976c0fb79a35875457fa9bb40827678e54d1bc8/charset_normalizer-3.4.1-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:c4c3e6da02df6fa1410a7680bd3f63d4f710232d3139089536310d027950696a", size = 152852 },
59
+ { url = "https://files.pythonhosted.org/packages/84/c9/98e3732278a99f47d487fd3468bc60b882920cef29d1fa6ca460a1fdf4e6/charset_normalizer-3.4.1-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:5df196eb874dae23dcfb968c83d4f8fdccb333330fe1fc278ac5ceeb101003a9", size = 150488 },
60
+ { url = "https://files.pythonhosted.org/packages/13/0e/9c8d4cb99c98c1007cc11eda969ebfe837bbbd0acdb4736d228ccaabcd22/charset_normalizer-3.4.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:e358e64305fe12299a08e08978f51fc21fac060dcfcddd95453eabe5b93ed0e1", size = 146192 },
61
+ { url = "https://files.pythonhosted.org/packages/b2/21/2b6b5b860781a0b49427309cb8670785aa543fb2178de875b87b9cc97746/charset_normalizer-3.4.1-cp312-cp312-win32.whl", hash = "sha256:9b23ca7ef998bc739bf6ffc077c2116917eabcc901f88da1b9856b210ef63f35", size = 95550 },
62
+ { url = "https://files.pythonhosted.org/packages/21/5b/1b390b03b1d16c7e382b561c5329f83cc06623916aab983e8ab9239c7d5c/charset_normalizer-3.4.1-cp312-cp312-win_amd64.whl", hash = "sha256:6ff8a4a60c227ad87030d76e99cd1698345d4491638dfa6673027c48b3cd395f", size = 102785 },
63
+ { url = "https://files.pythonhosted.org/packages/38/94/ce8e6f63d18049672c76d07d119304e1e2d7c6098f0841b51c666e9f44a0/charset_normalizer-3.4.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:aabfa34badd18f1da5ec1bc2715cadc8dca465868a4e73a0173466b688f29dda", size = 195698 },
64
+ { url = "https://files.pythonhosted.org/packages/24/2e/dfdd9770664aae179a96561cc6952ff08f9a8cd09a908f259a9dfa063568/charset_normalizer-3.4.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:22e14b5d70560b8dd51ec22863f370d1e595ac3d024cb8ad7d308b4cd95f8313", size = 140162 },
65
+ { url = "https://files.pythonhosted.org/packages/24/4e/f646b9093cff8fc86f2d60af2de4dc17c759de9d554f130b140ea4738ca6/charset_normalizer-3.4.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8436c508b408b82d87dc5f62496973a1805cd46727c34440b0d29d8a2f50a6c9", size = 150263 },
66
+ { url = "https://files.pythonhosted.org/packages/5e/67/2937f8d548c3ef6e2f9aab0f6e21001056f692d43282b165e7c56023e6dd/charset_normalizer-3.4.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2d074908e1aecee37a7635990b2c6d504cd4766c7bc9fc86d63f9c09af3fa11b", size = 142966 },
67
+ { url = "https://files.pythonhosted.org/packages/52/ed/b7f4f07de100bdb95c1756d3a4d17b90c1a3c53715c1a476f8738058e0fa/charset_normalizer-3.4.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:955f8851919303c92343d2f66165294848d57e9bba6cf6e3625485a70a038d11", size = 144992 },
68
+ { url = "https://files.pythonhosted.org/packages/96/2c/d49710a6dbcd3776265f4c923bb73ebe83933dfbaa841c5da850fe0fd20b/charset_normalizer-3.4.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:44ecbf16649486d4aebafeaa7ec4c9fed8b88101f4dd612dcaf65d5e815f837f", size = 147162 },
69
+ { url = "https://files.pythonhosted.org/packages/b4/41/35ff1f9a6bd380303dea55e44c4933b4cc3c4850988927d4082ada230273/charset_normalizer-3.4.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:0924e81d3d5e70f8126529951dac65c1010cdf117bb75eb02dd12339b57749dd", size = 140972 },
70
+ { url = "https://files.pythonhosted.org/packages/fb/43/c6a0b685fe6910d08ba971f62cd9c3e862a85770395ba5d9cad4fede33ab/charset_normalizer-3.4.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:2967f74ad52c3b98de4c3b32e1a44e32975e008a9cd2a8cc8966d6a5218c5cb2", size = 149095 },
71
+ { url = "https://files.pythonhosted.org/packages/4c/ff/a9a504662452e2d2878512115638966e75633519ec11f25fca3d2049a94a/charset_normalizer-3.4.1-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:c75cb2a3e389853835e84a2d8fb2b81a10645b503eca9bcb98df6b5a43eb8886", size = 152668 },
72
+ { url = "https://files.pythonhosted.org/packages/6c/71/189996b6d9a4b932564701628af5cee6716733e9165af1d5e1b285c530ed/charset_normalizer-3.4.1-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:09b26ae6b1abf0d27570633b2b078a2a20419c99d66fb2823173d73f188ce601", size = 150073 },
73
+ { url = "https://files.pythonhosted.org/packages/e4/93/946a86ce20790e11312c87c75ba68d5f6ad2208cfb52b2d6a2c32840d922/charset_normalizer-3.4.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:fa88b843d6e211393a37219e6a1c1df99d35e8fd90446f1118f4216e307e48cd", size = 145732 },
74
+ { url = "https://files.pythonhosted.org/packages/cd/e5/131d2fb1b0dddafc37be4f3a2fa79aa4c037368be9423061dccadfd90091/charset_normalizer-3.4.1-cp313-cp313-win32.whl", hash = "sha256:eb8178fe3dba6450a3e024e95ac49ed3400e506fd4e9e5c32d30adda88cbd407", size = 95391 },
75
+ { url = "https://files.pythonhosted.org/packages/27/f2/4f9a69cc7712b9b5ad8fdb87039fd89abba997ad5cbe690d1835d40405b0/charset_normalizer-3.4.1-cp313-cp313-win_amd64.whl", hash = "sha256:b1ac5992a838106edb89654e0aebfc24f5848ae2547d22c2c3f66454daa11971", size = 102702 },
76
+ { url = "https://files.pythonhosted.org/packages/10/bd/6517ea94f2672e801011d50b5d06be2a0deaf566aea27bcdcd47e5195357/charset_normalizer-3.4.1-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:ecddf25bee22fe4fe3737a399d0d177d72bc22be6913acfab364b40bce1ba83c", size = 195653 },
77
+ { url = "https://files.pythonhosted.org/packages/e5/0d/815a2ba3f283b4eeaa5ece57acade365c5b4135f65a807a083c818716582/charset_normalizer-3.4.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8c60ca7339acd497a55b0ea5d506b2a2612afb2826560416f6894e8b5770d4a9", size = 140701 },
78
+ { url = "https://files.pythonhosted.org/packages/aa/17/c94be7ee0d142687e047fe1de72060f6d6837f40eedc26e87e6e124a3fc6/charset_normalizer-3.4.1-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b7b2d86dd06bfc2ade3312a83a5c364c7ec2e3498f8734282c6c3d4b07b346b8", size = 150495 },
79
+ { url = "https://files.pythonhosted.org/packages/f7/33/557ac796c47165fc141e4fb71d7b0310f67e05cb420756f3a82e0a0068e0/charset_normalizer-3.4.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:dd78cfcda14a1ef52584dbb008f7ac81c1328c0f58184bf9a84c49c605002da6", size = 142946 },
80
+ { url = "https://files.pythonhosted.org/packages/1e/0d/38ef4ae41e9248d63fc4998d933cae22473b1b2ac4122cf908d0f5eb32aa/charset_normalizer-3.4.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6e27f48bcd0957c6d4cb9d6fa6b61d192d0b13d5ef563e5f2ae35feafc0d179c", size = 144737 },
81
+ { url = "https://files.pythonhosted.org/packages/43/01/754cdb29dd0560f58290aaaa284d43eea343ad0512e6ad3b8b5c11f08592/charset_normalizer-3.4.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:01ad647cdd609225c5350561d084b42ddf732f4eeefe6e678765636791e78b9a", size = 147471 },
82
+ { url = "https://files.pythonhosted.org/packages/ba/cd/861883ba5160c7a9bd242c30b2c71074cda2aefcc0addc91118e0d4e0765/charset_normalizer-3.4.1-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:619a609aa74ae43d90ed2e89bdd784765de0a25ca761b93e196d938b8fd1dbbd", size = 140801 },
83
+ { url = "https://files.pythonhosted.org/packages/6f/7f/0c0dad447819e90b93f8ed238cc8f11b91353c23c19e70fa80483a155bed/charset_normalizer-3.4.1-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:89149166622f4db9b4b6a449256291dc87a99ee53151c74cbd82a53c8c2f6ccd", size = 149312 },
84
+ { url = "https://files.pythonhosted.org/packages/8e/09/9f8abcc6fff60fb727268b63c376c8c79cc37b833c2dfe1f535dfb59523b/charset_normalizer-3.4.1-cp38-cp38-musllinux_1_2_ppc64le.whl", hash = "sha256:7709f51f5f7c853f0fb938bcd3bc59cdfdc5203635ffd18bf354f6967ea0f824", size = 152347 },
85
+ { url = "https://files.pythonhosted.org/packages/be/e5/3f363dad2e24378f88ccf63ecc39e817c29f32e308ef21a7a6d9c1201165/charset_normalizer-3.4.1-cp38-cp38-musllinux_1_2_s390x.whl", hash = "sha256:345b0426edd4e18138d6528aed636de7a9ed169b4aaf9d61a8c19e39d26838ca", size = 149888 },
86
+ { url = "https://files.pythonhosted.org/packages/e4/10/a78c0e91f487b4ad0ef7480ac765e15b774f83de2597f1b6ef0eaf7a2f99/charset_normalizer-3.4.1-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:0907f11d019260cdc3f94fbdb23ff9125f6b5d1039b76003b5b0ac9d6a6c9d5b", size = 145169 },
87
+ { url = "https://files.pythonhosted.org/packages/d3/81/396e7d7f5d7420da8273c91175d2e9a3f569288e3611d521685e4b9ac9cc/charset_normalizer-3.4.1-cp38-cp38-win32.whl", hash = "sha256:ea0d8d539afa5eb2728aa1932a988a9a7af94f18582ffae4bc10b3fbdad0626e", size = 95094 },
88
+ { url = "https://files.pythonhosted.org/packages/40/bb/20affbbd9ea29c71ea123769dc568a6d42052ff5089c5fe23e21e21084a6/charset_normalizer-3.4.1-cp38-cp38-win_amd64.whl", hash = "sha256:329ce159e82018d646c7ac45b01a430369d526569ec08516081727a20e9e4af4", size = 102139 },
89
+ { url = "https://files.pythonhosted.org/packages/7f/c0/b913f8f02836ed9ab32ea643c6fe4d3325c3d8627cf6e78098671cafff86/charset_normalizer-3.4.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:b97e690a2118911e39b4042088092771b4ae3fc3aa86518f84b8cf6888dbdb41", size = 197867 },
90
+ { url = "https://files.pythonhosted.org/packages/0f/6c/2bee440303d705b6fb1e2ec789543edec83d32d258299b16eed28aad48e0/charset_normalizer-3.4.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:78baa6d91634dfb69ec52a463534bc0df05dbd546209b79a3880a34487f4b84f", size = 141385 },
91
+ { url = "https://files.pythonhosted.org/packages/3d/04/cb42585f07f6f9fd3219ffb6f37d5a39b4fd2db2355b23683060029c35f7/charset_normalizer-3.4.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1a2bc9f351a75ef49d664206d51f8e5ede9da246602dc2d2726837620ea034b2", size = 151367 },
92
+ { url = "https://files.pythonhosted.org/packages/54/54/2412a5b093acb17f0222de007cc129ec0e0df198b5ad2ce5699355269dfe/charset_normalizer-3.4.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:75832c08354f595c760a804588b9357d34ec00ba1c940c15e31e96d902093770", size = 143928 },
93
+ { url = "https://files.pythonhosted.org/packages/5a/6d/e2773862b043dcf8a221342954f375392bb2ce6487bcd9f2c1b34e1d6781/charset_normalizer-3.4.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0af291f4fe114be0280cdd29d533696a77b5b49cfde5467176ecab32353395c4", size = 146203 },
94
+ { url = "https://files.pythonhosted.org/packages/b9/f8/ca440ef60d8f8916022859885f231abb07ada3c347c03d63f283bec32ef5/charset_normalizer-3.4.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0167ddc8ab6508fe81860a57dd472b2ef4060e8d378f0cc555707126830f2537", size = 148082 },
95
+ { url = "https://files.pythonhosted.org/packages/04/d2/42fd330901aaa4b805a1097856c2edf5095e260a597f65def493f4b8c833/charset_normalizer-3.4.1-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:2a75d49014d118e4198bcee5ee0a6f25856b29b12dbf7cd012791f8a6cc5c496", size = 142053 },
96
+ { url = "https://files.pythonhosted.org/packages/9e/af/3a97a4fa3c53586f1910dadfc916e9c4f35eeada36de4108f5096cb7215f/charset_normalizer-3.4.1-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:363e2f92b0f0174b2f8238240a1a30142e3db7b957a5dd5689b0e75fb717cc78", size = 150625 },
97
+ { url = "https://files.pythonhosted.org/packages/26/ae/23d6041322a3556e4da139663d02fb1b3c59a23ab2e2b56432bd2ad63ded/charset_normalizer-3.4.1-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:ab36c8eb7e454e34e60eb55ca5d241a5d18b2c6244f6827a30e451c42410b5f7", size = 153549 },
98
+ { url = "https://files.pythonhosted.org/packages/94/22/b8f2081c6a77cb20d97e57e0b385b481887aa08019d2459dc2858ed64871/charset_normalizer-3.4.1-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:4c0907b1928a36d5a998d72d64d8eaa7244989f7aaaf947500d3a800c83a3fd6", size = 150945 },
99
+ { url = "https://files.pythonhosted.org/packages/c7/0b/c5ec5092747f801b8b093cdf5610e732b809d6cb11f4c51e35fc28d1d389/charset_normalizer-3.4.1-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:04432ad9479fa40ec0f387795ddad4437a2b50417c69fa275e212933519ff294", size = 146595 },
100
+ { url = "https://files.pythonhosted.org/packages/0c/5a/0b59704c38470df6768aa154cc87b1ac7c9bb687990a1559dc8765e8627e/charset_normalizer-3.4.1-cp39-cp39-win32.whl", hash = "sha256:3bed14e9c89dcb10e8f3a29f9ccac4955aebe93c71ae803af79265c9ca5644c5", size = 95453 },
101
+ { url = "https://files.pythonhosted.org/packages/85/2d/a9790237cb4d01a6d57afadc8573c8b73c609ade20b80f4cda30802009ee/charset_normalizer-3.4.1-cp39-cp39-win_amd64.whl", hash = "sha256:49402233c892a461407c512a19435d1ce275543138294f7ef013f0b63d5d3765", size = 102811 },
102
+ { url = "https://files.pythonhosted.org/packages/0e/f6/65ecc6878a89bb1c23a086ea335ad4bf21a588990c3f535a227b9eea9108/charset_normalizer-3.4.1-py3-none-any.whl", hash = "sha256:d98b1668f06378c6dbefec3b92299716b931cd4e6061f3c875a71ced1780ab85", size = 49767 },
103
+ ]
104
+
105
+ [[package]]
106
+ name = "idna"
107
+ version = "3.10"
108
+ source = { registry = "https://pypi.org/simple" }
109
+ sdist = { url = "https://files.pythonhosted.org/packages/f1/70/7703c29685631f5a7590aa73f1f1d3fa9a380e654b86af429e0934a32f7d/idna-3.10.tar.gz", hash = "sha256:12f65c9b470abda6dc35cf8e63cc574b1c52b11df2c86030af0ac09b01b13ea9", size = 190490 }
110
+ wheels = [
111
+ { url = "https://files.pythonhosted.org/packages/76/c6/c88e154df9c4e1a2a66ccf0005a88dfb2650c1dffb6f5ce603dfbd452ce3/idna-3.10-py3-none-any.whl", hash = "sha256:946d195a0d259cbba61165e88e65941f16e9b36ea6ddb97f00452bae8b1287d3", size = 70442 },
112
+ ]
113
+
114
+ [[package]]
115
+ name = "ngpt"
116
+ version = "1.0.0"
117
+ source = { editable = "." }
118
+ dependencies = [
119
+ { name = "requests" },
120
+ ]
121
+
122
+ [package.metadata]
123
+ requires-dist = [{ name = "requests", specifier = ">=2.28.0" }]
124
+
125
+ [package.metadata.requires-dev]
126
+ dev = []
127
+
128
+ [[package]]
129
+ name = "requests"
130
+ version = "2.32.3"
131
+ source = { registry = "https://pypi.org/simple" }
132
+ dependencies = [
133
+ { name = "certifi" },
134
+ { name = "charset-normalizer" },
135
+ { name = "idna" },
136
+ { name = "urllib3", version = "2.2.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.9'" },
137
+ { name = "urllib3", version = "2.4.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.9'" },
138
+ ]
139
+ sdist = { url = "https://files.pythonhosted.org/packages/63/70/2bf7780ad2d390a8d301ad0b550f1581eadbd9a20f896afe06353c2a2913/requests-2.32.3.tar.gz", hash = "sha256:55365417734eb18255590a9ff9eb97e9e1da868d4ccd6402399eaf68af20a760", size = 131218 }
140
+ wheels = [
141
+ { url = "https://files.pythonhosted.org/packages/f9/9b/335f9764261e915ed497fcdeb11df5dfd6f7bf257d4a6a2a686d80da4d54/requests-2.32.3-py3-none-any.whl", hash = "sha256:70761cfe03c773ceb22aa2f671b4757976145175cdfca038c02654d061d6dcc6", size = 64928 },
142
+ ]
143
+
144
+ [[package]]
145
+ name = "urllib3"
146
+ version = "2.2.3"
147
+ source = { registry = "https://pypi.org/simple" }
148
+ resolution-markers = [
149
+ "python_full_version < '3.9'",
150
+ ]
151
+ sdist = { url = "https://files.pythonhosted.org/packages/ed/63/22ba4ebfe7430b76388e7cd448d5478814d3032121827c12a2cc287e2260/urllib3-2.2.3.tar.gz", hash = "sha256:e7d814a81dad81e6caf2ec9fdedb284ecc9c73076b62654547cc64ccdcae26e9", size = 300677 }
152
+ wheels = [
153
+ { url = "https://files.pythonhosted.org/packages/ce/d9/5f4c13cecde62396b0d3fe530a50ccea91e7dfc1ccf0e09c228841bb5ba8/urllib3-2.2.3-py3-none-any.whl", hash = "sha256:ca899ca043dcb1bafa3e262d73aa25c465bfb49e0bd9dd5d59f1d0acba2f8fac", size = 126338 },
154
+ ]
155
+
156
+ [[package]]
157
+ name = "urllib3"
158
+ version = "2.4.0"
159
+ source = { registry = "https://pypi.org/simple" }
160
+ resolution-markers = [
161
+ "python_full_version >= '3.9'",
162
+ ]
163
+ sdist = { url = "https://files.pythonhosted.org/packages/8a/78/16493d9c386d8e60e442a35feac5e00f0913c0f4b7c217c11e8ec2ff53e0/urllib3-2.4.0.tar.gz", hash = "sha256:414bc6535b787febd7567804cc015fee39daab8ad86268f1310a9250697de466", size = 390672 }
164
+ wheels = [
165
+ { url = "https://files.pythonhosted.org/packages/6b/11/cc635220681e93a0183390e26485430ca2c7b5f9d33b15c74c2861cb8091/urllib3-2.4.0-py3-none-any.whl", hash = "sha256:4e16665048960a0900c702d4a66415956a584919c03361cac9f1df5c5dd7e813", size = 128680 },
166
+ ]