neurocli-sdk 0.2.1__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.
- neurocli_sdk-0.2.1/PKG-INFO +61 -0
- neurocli_sdk-0.2.1/README.md +38 -0
- neurocli_sdk-0.2.1/neurocli/__init__.py +3 -0
- neurocli_sdk-0.2.1/neurocli/client.py +234 -0
- neurocli_sdk-0.2.1/neurocli_sdk.egg-info/PKG-INFO +61 -0
- neurocli_sdk-0.2.1/neurocli_sdk.egg-info/SOURCES.txt +10 -0
- neurocli_sdk-0.2.1/neurocli_sdk.egg-info/dependency_links.txt +1 -0
- neurocli_sdk-0.2.1/neurocli_sdk.egg-info/requires.txt +3 -0
- neurocli_sdk-0.2.1/neurocli_sdk.egg-info/top_level.txt +1 -0
- neurocli_sdk-0.2.1/pyproject.toml +29 -0
- neurocli_sdk-0.2.1/setup.cfg +4 -0
- neurocli_sdk-0.2.1/setup.py +28 -0
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: neurocli-sdk
|
|
3
|
+
Version: 0.2.1
|
|
4
|
+
Summary: The official Python SDK for the NeuroCLI API
|
|
5
|
+
Home-page: https://www.neurocli.in
|
|
6
|
+
Author: NeuroCLI
|
|
7
|
+
Author-email: NeuroCLI <info@neurocli.in>
|
|
8
|
+
License: MIT
|
|
9
|
+
Project-URL: Homepage, https://www.neurocli.in
|
|
10
|
+
Project-URL: Documentation, https://www.neurocli.in/docs
|
|
11
|
+
Classifier: Programming Language :: Python :: 3
|
|
12
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
13
|
+
Classifier: Operating System :: OS Independent
|
|
14
|
+
Classifier: Intended Audience :: Developers
|
|
15
|
+
Requires-Python: >=3.7
|
|
16
|
+
Description-Content-Type: text/markdown
|
|
17
|
+
Requires-Dist: httpx>=0.24.0
|
|
18
|
+
Requires-Dist: tenacity>=8.0.0
|
|
19
|
+
Requires-Dist: pydantic>=2.0.0
|
|
20
|
+
Dynamic: author
|
|
21
|
+
Dynamic: home-page
|
|
22
|
+
Dynamic: requires-python
|
|
23
|
+
|
|
24
|
+
# NeuroCLI Python SDK
|
|
25
|
+
|
|
26
|
+
The official Python library for the NeuroCLI API.
|
|
27
|
+
|
|
28
|
+
## Installation
|
|
29
|
+
|
|
30
|
+
You can install this SDK locally:
|
|
31
|
+
```bash
|
|
32
|
+
pip install -e .
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
## Usage
|
|
36
|
+
|
|
37
|
+
The SDK mimics the standard OpenAI Python client interface, making it extremely easy to drop into existing AI applications.
|
|
38
|
+
|
|
39
|
+
### Basic Chat Completion
|
|
40
|
+
|
|
41
|
+
```python
|
|
42
|
+
import neurocli
|
|
43
|
+
|
|
44
|
+
# Initialize the client
|
|
45
|
+
client = neurocli.NeuroCLI(
|
|
46
|
+
api_key="ncli_YOUR_SECRET_KEY",
|
|
47
|
+
# During local testing, point this to your local server:
|
|
48
|
+
# base_url="http://127.0.0.1:8000/api/ai"
|
|
49
|
+
)
|
|
50
|
+
|
|
51
|
+
# Generate a response
|
|
52
|
+
response = client.chat.completions.create(
|
|
53
|
+
model="meta/llama-3.1-8b-instruct",
|
|
54
|
+
messages=[
|
|
55
|
+
{"role": "user", "content": "Explain quantum computing in simple terms."}
|
|
56
|
+
],
|
|
57
|
+
temperature=0.7
|
|
58
|
+
)
|
|
59
|
+
|
|
60
|
+
print(response.choices[0].message.content)
|
|
61
|
+
```
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
# NeuroCLI Python SDK
|
|
2
|
+
|
|
3
|
+
The official Python library for the NeuroCLI API.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
You can install this SDK locally:
|
|
8
|
+
```bash
|
|
9
|
+
pip install -e .
|
|
10
|
+
```
|
|
11
|
+
|
|
12
|
+
## Usage
|
|
13
|
+
|
|
14
|
+
The SDK mimics the standard OpenAI Python client interface, making it extremely easy to drop into existing AI applications.
|
|
15
|
+
|
|
16
|
+
### Basic Chat Completion
|
|
17
|
+
|
|
18
|
+
```python
|
|
19
|
+
import neurocli
|
|
20
|
+
|
|
21
|
+
# Initialize the client
|
|
22
|
+
client = neurocli.NeuroCLI(
|
|
23
|
+
api_key="ncli_YOUR_SECRET_KEY",
|
|
24
|
+
# During local testing, point this to your local server:
|
|
25
|
+
# base_url="http://127.0.0.1:8000/api/ai"
|
|
26
|
+
)
|
|
27
|
+
|
|
28
|
+
# Generate a response
|
|
29
|
+
response = client.chat.completions.create(
|
|
30
|
+
model="meta/llama-3.1-8b-instruct",
|
|
31
|
+
messages=[
|
|
32
|
+
{"role": "user", "content": "Explain quantum computing in simple terms."}
|
|
33
|
+
],
|
|
34
|
+
temperature=0.7
|
|
35
|
+
)
|
|
36
|
+
|
|
37
|
+
print(response.choices[0].message.content)
|
|
38
|
+
```
|
|
@@ -0,0 +1,234 @@
|
|
|
1
|
+
import json
|
|
2
|
+
import inspect
|
|
3
|
+
from typing import List, Dict, Optional, Any, Callable, Union, Iterator, AsyncIterator
|
|
4
|
+
import httpx
|
|
5
|
+
from pydantic import BaseModel, Field
|
|
6
|
+
from tenacity import retry, stop_after_attempt, wait_exponential, retry_if_exception_type
|
|
7
|
+
|
|
8
|
+
# --- Types ---
|
|
9
|
+
class Message(BaseModel):
|
|
10
|
+
role: str
|
|
11
|
+
content: str
|
|
12
|
+
|
|
13
|
+
class Choice(BaseModel):
|
|
14
|
+
message: Message
|
|
15
|
+
finish_reason: Optional[str] = None
|
|
16
|
+
|
|
17
|
+
class ChatCompletionResponse(BaseModel):
|
|
18
|
+
id: Optional[str] = None
|
|
19
|
+
choices: List[Choice]
|
|
20
|
+
|
|
21
|
+
# Streaming chunk model
|
|
22
|
+
class ChoiceDelta(BaseModel):
|
|
23
|
+
content: Optional[str] = None
|
|
24
|
+
|
|
25
|
+
class ChunkChoice(BaseModel):
|
|
26
|
+
delta: ChoiceDelta
|
|
27
|
+
finish_reason: Optional[str] = None
|
|
28
|
+
|
|
29
|
+
class ChatCompletionChunk(BaseModel):
|
|
30
|
+
id: Optional[str] = None
|
|
31
|
+
choices: List[ChunkChoice]
|
|
32
|
+
|
|
33
|
+
# --- Error Handling ---
|
|
34
|
+
class NeuroCLIError(Exception):
|
|
35
|
+
pass
|
|
36
|
+
|
|
37
|
+
class RateLimitError(NeuroCLIError):
|
|
38
|
+
pass
|
|
39
|
+
|
|
40
|
+
class APIError(NeuroCLIError):
|
|
41
|
+
pass
|
|
42
|
+
|
|
43
|
+
# Retry decorator for rate limits (429) or temporary server errors (502, 503, 504)
|
|
44
|
+
def create_retry_decorator():
|
|
45
|
+
return retry(
|
|
46
|
+
stop=stop_after_attempt(3),
|
|
47
|
+
wait=wait_exponential(multiplier=1, min=2, max=10),
|
|
48
|
+
retry=retry_if_exception_type((RateLimitError, httpx.ReadTimeout, httpx.ConnectError)),
|
|
49
|
+
reraise=True
|
|
50
|
+
)
|
|
51
|
+
|
|
52
|
+
# --- Function Calling Wrapper ---
|
|
53
|
+
def wrap_function(func: Callable) -> Dict[str, Any]:
|
|
54
|
+
"""
|
|
55
|
+
Automatically converts a Python function with docstrings and type hints
|
|
56
|
+
into an OpenAI/Nvidia NIM compatible JSON Schema for function calling.
|
|
57
|
+
"""
|
|
58
|
+
sig = inspect.signature(func)
|
|
59
|
+
doc = inspect.getdoc(func) or ""
|
|
60
|
+
|
|
61
|
+
properties = {}
|
|
62
|
+
required = []
|
|
63
|
+
|
|
64
|
+
for name, param in sig.parameters.items():
|
|
65
|
+
param_type = "string"
|
|
66
|
+
if param.annotation == int:
|
|
67
|
+
param_type = "integer"
|
|
68
|
+
elif param.annotation == float:
|
|
69
|
+
param_type = "number"
|
|
70
|
+
elif param.annotation == bool:
|
|
71
|
+
param_type = "boolean"
|
|
72
|
+
|
|
73
|
+
properties[name] = {"type": param_type}
|
|
74
|
+
|
|
75
|
+
if param.default == inspect.Parameter.empty:
|
|
76
|
+
required.append(name)
|
|
77
|
+
|
|
78
|
+
return {
|
|
79
|
+
"type": "function",
|
|
80
|
+
"function": {
|
|
81
|
+
"name": func.__name__,
|
|
82
|
+
"description": doc,
|
|
83
|
+
"parameters": {
|
|
84
|
+
"type": "object",
|
|
85
|
+
"properties": properties,
|
|
86
|
+
"required": required
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
# --- Core Logic ---
|
|
93
|
+
|
|
94
|
+
def _handle_response(response: httpx.Response) -> ChatCompletionResponse:
|
|
95
|
+
if not response.is_success:
|
|
96
|
+
if response.status_code == 429:
|
|
97
|
+
raise RateLimitError("Rate Limit Exceeded. Retrying automatically...")
|
|
98
|
+
try:
|
|
99
|
+
error_data = response.json()
|
|
100
|
+
error_msg = error_data.get('message') or error_data.get('error') or response.text
|
|
101
|
+
except Exception:
|
|
102
|
+
error_msg = response.text
|
|
103
|
+
raise APIError(f"NeuroCLI API Error ({response.status_code}): {error_msg}")
|
|
104
|
+
|
|
105
|
+
data = response.json()
|
|
106
|
+
return ChatCompletionResponse(**data)
|
|
107
|
+
|
|
108
|
+
def _stream_iterator(response: httpx.Response) -> Iterator[ChatCompletionChunk]:
|
|
109
|
+
if not response.is_success:
|
|
110
|
+
if response.status_code == 429:
|
|
111
|
+
raise RateLimitError("Rate Limit Exceeded. Retrying automatically...")
|
|
112
|
+
raise APIError(f"NeuroCLI API Error ({response.status_code})")
|
|
113
|
+
|
|
114
|
+
for line in response.iter_lines():
|
|
115
|
+
if line.startswith("data: ") and line != "data: [DONE]":
|
|
116
|
+
data = json.loads(line[6:])
|
|
117
|
+
yield ChatCompletionChunk(**data)
|
|
118
|
+
|
|
119
|
+
async def _astream_iterator(response: httpx.Response) -> AsyncIterator[ChatCompletionChunk]:
|
|
120
|
+
if not response.is_success:
|
|
121
|
+
if response.status_code == 429:
|
|
122
|
+
raise RateLimitError("Rate Limit Exceeded. Retrying automatically...")
|
|
123
|
+
raise APIError(f"NeuroCLI API Error ({response.status_code})")
|
|
124
|
+
|
|
125
|
+
async for line in response.aiter_lines():
|
|
126
|
+
if line.startswith("data: ") and line != "data: [DONE]":
|
|
127
|
+
data = json.loads(line[6:])
|
|
128
|
+
yield ChatCompletionChunk(**data)
|
|
129
|
+
|
|
130
|
+
|
|
131
|
+
# --- Sync Client ---
|
|
132
|
+
class ChatCompletions:
|
|
133
|
+
def __init__(self, client):
|
|
134
|
+
self._client = client
|
|
135
|
+
|
|
136
|
+
@create_retry_decorator()
|
|
137
|
+
def create(
|
|
138
|
+
self,
|
|
139
|
+
model: str,
|
|
140
|
+
messages: List[Dict[str, str]],
|
|
141
|
+
temperature: Optional[float] = None,
|
|
142
|
+
max_tokens: Optional[int] = None,
|
|
143
|
+
stream: bool = False,
|
|
144
|
+
tools: Optional[List[Dict[str, Any]]] = None,
|
|
145
|
+
**kwargs
|
|
146
|
+
) -> Union[ChatCompletionResponse, Iterator[ChatCompletionChunk]]:
|
|
147
|
+
|
|
148
|
+
payload = {"model": model, "messages": messages}
|
|
149
|
+
if temperature is not None: payload["temperature"] = temperature
|
|
150
|
+
if max_tokens is not None: payload["max_tokens"] = max_tokens
|
|
151
|
+
if tools is not None: payload["tools"] = tools
|
|
152
|
+
if stream: payload["stream"] = True
|
|
153
|
+
payload.update(kwargs)
|
|
154
|
+
|
|
155
|
+
headers = {
|
|
156
|
+
"Content-Type": "application/json",
|
|
157
|
+
"Authorization": f"Bearer {self._client.api_key}"
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
if stream:
|
|
161
|
+
request = self._client._http.build_request(
|
|
162
|
+
"POST", f"{self._client.base_url}/chat/completions", json=payload, headers=headers
|
|
163
|
+
)
|
|
164
|
+
response = self._client._http.send(request, stream=True)
|
|
165
|
+
return _stream_iterator(response)
|
|
166
|
+
else:
|
|
167
|
+
response = self._client._http.post(
|
|
168
|
+
f"{self._client.base_url}/chat/completions", json=payload, headers=headers
|
|
169
|
+
)
|
|
170
|
+
return _handle_response(response)
|
|
171
|
+
|
|
172
|
+
class Chat:
|
|
173
|
+
def __init__(self, client):
|
|
174
|
+
self.completions = ChatCompletions(client)
|
|
175
|
+
|
|
176
|
+
class NeuroCLI:
|
|
177
|
+
def __init__(self, api_key: str, base_url: str = "https://www.neurocli.in/v1"):
|
|
178
|
+
self.api_key = api_key
|
|
179
|
+
self.base_url = base_url.rstrip("/")
|
|
180
|
+
self._http = httpx.Client(timeout=60.0)
|
|
181
|
+
self.chat = Chat(self)
|
|
182
|
+
|
|
183
|
+
|
|
184
|
+
# --- Async Client ---
|
|
185
|
+
class AsyncChatCompletions:
|
|
186
|
+
def __init__(self, client):
|
|
187
|
+
self._client = client
|
|
188
|
+
|
|
189
|
+
@create_retry_decorator()
|
|
190
|
+
async def create(
|
|
191
|
+
self,
|
|
192
|
+
model: str,
|
|
193
|
+
messages: List[Dict[str, str]],
|
|
194
|
+
temperature: Optional[float] = None,
|
|
195
|
+
max_tokens: Optional[int] = None,
|
|
196
|
+
stream: bool = False,
|
|
197
|
+
tools: Optional[List[Dict[str, Any]]] = None,
|
|
198
|
+
**kwargs
|
|
199
|
+
) -> Union[ChatCompletionResponse, AsyncIterator[ChatCompletionChunk]]:
|
|
200
|
+
|
|
201
|
+
payload = {"model": model, "messages": messages}
|
|
202
|
+
if temperature is not None: payload["temperature"] = temperature
|
|
203
|
+
if max_tokens is not None: payload["max_tokens"] = max_tokens
|
|
204
|
+
if tools is not None: payload["tools"] = tools
|
|
205
|
+
if stream: payload["stream"] = True
|
|
206
|
+
payload.update(kwargs)
|
|
207
|
+
|
|
208
|
+
headers = {
|
|
209
|
+
"Content-Type": "application/json",
|
|
210
|
+
"Authorization": f"Bearer {self._client.api_key}"
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
if stream:
|
|
214
|
+
request = self._client._http.build_request(
|
|
215
|
+
"POST", f"{self._client.base_url}/chat/completions", json=payload, headers=headers
|
|
216
|
+
)
|
|
217
|
+
response = await self._client._http.send(request, stream=True)
|
|
218
|
+
return _astream_iterator(response)
|
|
219
|
+
else:
|
|
220
|
+
response = await self._client._http.post(
|
|
221
|
+
f"{self._client.base_url}/chat/completions", json=payload, headers=headers
|
|
222
|
+
)
|
|
223
|
+
return _handle_response(response)
|
|
224
|
+
|
|
225
|
+
class AsyncChat:
|
|
226
|
+
def __init__(self, client):
|
|
227
|
+
self.completions = AsyncChatCompletions(client)
|
|
228
|
+
|
|
229
|
+
class AsyncNeuroCLI:
|
|
230
|
+
def __init__(self, api_key: str, base_url: str = "https://www.neurocli.in/v1"):
|
|
231
|
+
self.api_key = api_key
|
|
232
|
+
self.base_url = base_url.rstrip("/")
|
|
233
|
+
self._http = httpx.AsyncClient(timeout=60.0)
|
|
234
|
+
self.chat = AsyncChat(self)
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: neurocli-sdk
|
|
3
|
+
Version: 0.2.1
|
|
4
|
+
Summary: The official Python SDK for the NeuroCLI API
|
|
5
|
+
Home-page: https://www.neurocli.in
|
|
6
|
+
Author: NeuroCLI
|
|
7
|
+
Author-email: NeuroCLI <info@neurocli.in>
|
|
8
|
+
License: MIT
|
|
9
|
+
Project-URL: Homepage, https://www.neurocli.in
|
|
10
|
+
Project-URL: Documentation, https://www.neurocli.in/docs
|
|
11
|
+
Classifier: Programming Language :: Python :: 3
|
|
12
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
13
|
+
Classifier: Operating System :: OS Independent
|
|
14
|
+
Classifier: Intended Audience :: Developers
|
|
15
|
+
Requires-Python: >=3.7
|
|
16
|
+
Description-Content-Type: text/markdown
|
|
17
|
+
Requires-Dist: httpx>=0.24.0
|
|
18
|
+
Requires-Dist: tenacity>=8.0.0
|
|
19
|
+
Requires-Dist: pydantic>=2.0.0
|
|
20
|
+
Dynamic: author
|
|
21
|
+
Dynamic: home-page
|
|
22
|
+
Dynamic: requires-python
|
|
23
|
+
|
|
24
|
+
# NeuroCLI Python SDK
|
|
25
|
+
|
|
26
|
+
The official Python library for the NeuroCLI API.
|
|
27
|
+
|
|
28
|
+
## Installation
|
|
29
|
+
|
|
30
|
+
You can install this SDK locally:
|
|
31
|
+
```bash
|
|
32
|
+
pip install -e .
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
## Usage
|
|
36
|
+
|
|
37
|
+
The SDK mimics the standard OpenAI Python client interface, making it extremely easy to drop into existing AI applications.
|
|
38
|
+
|
|
39
|
+
### Basic Chat Completion
|
|
40
|
+
|
|
41
|
+
```python
|
|
42
|
+
import neurocli
|
|
43
|
+
|
|
44
|
+
# Initialize the client
|
|
45
|
+
client = neurocli.NeuroCLI(
|
|
46
|
+
api_key="ncli_YOUR_SECRET_KEY",
|
|
47
|
+
# During local testing, point this to your local server:
|
|
48
|
+
# base_url="http://127.0.0.1:8000/api/ai"
|
|
49
|
+
)
|
|
50
|
+
|
|
51
|
+
# Generate a response
|
|
52
|
+
response = client.chat.completions.create(
|
|
53
|
+
model="meta/llama-3.1-8b-instruct",
|
|
54
|
+
messages=[
|
|
55
|
+
{"role": "user", "content": "Explain quantum computing in simple terms."}
|
|
56
|
+
],
|
|
57
|
+
temperature=0.7
|
|
58
|
+
)
|
|
59
|
+
|
|
60
|
+
print(response.choices[0].message.content)
|
|
61
|
+
```
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
README.md
|
|
2
|
+
pyproject.toml
|
|
3
|
+
setup.py
|
|
4
|
+
neurocli/__init__.py
|
|
5
|
+
neurocli/client.py
|
|
6
|
+
neurocli_sdk.egg-info/PKG-INFO
|
|
7
|
+
neurocli_sdk.egg-info/SOURCES.txt
|
|
8
|
+
neurocli_sdk.egg-info/dependency_links.txt
|
|
9
|
+
neurocli_sdk.egg-info/requires.txt
|
|
10
|
+
neurocli_sdk.egg-info/top_level.txt
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
neurocli
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=61.0", "wheel"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "neurocli-sdk"
|
|
7
|
+
version = "0.2.1"
|
|
8
|
+
description = "The official Python SDK for the NeuroCLI API"
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
requires-python = ">=3.7"
|
|
11
|
+
license = { text = "MIT" }
|
|
12
|
+
authors = [
|
|
13
|
+
{ name="NeuroCLI", email="info@neurocli.in" },
|
|
14
|
+
]
|
|
15
|
+
classifiers = [
|
|
16
|
+
"Programming Language :: Python :: 3",
|
|
17
|
+
"License :: OSI Approved :: MIT License",
|
|
18
|
+
"Operating System :: OS Independent",
|
|
19
|
+
"Intended Audience :: Developers",
|
|
20
|
+
]
|
|
21
|
+
dependencies = [
|
|
22
|
+
"httpx>=0.24.0",
|
|
23
|
+
"tenacity>=8.0.0",
|
|
24
|
+
"pydantic>=2.0.0",
|
|
25
|
+
]
|
|
26
|
+
|
|
27
|
+
[project.urls]
|
|
28
|
+
"Homepage" = "https://www.neurocli.in"
|
|
29
|
+
"Documentation" = "https://www.neurocli.in/docs"
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
from setuptools import setup, find_packages
|
|
2
|
+
|
|
3
|
+
with open("README.md", "r", encoding="utf-8") as fh:
|
|
4
|
+
long_description = fh.read()
|
|
5
|
+
|
|
6
|
+
setup(
|
|
7
|
+
name="neurocli-sdk",
|
|
8
|
+
version="0.2.1",
|
|
9
|
+
description="The official Python SDK for the NeuroCLI API.",
|
|
10
|
+
long_description=long_description,
|
|
11
|
+
long_description_content_type="text/markdown",
|
|
12
|
+
author="NeuroCLI",
|
|
13
|
+
author_email="info@neurocli.in",
|
|
14
|
+
url="https://www.neurocli.in",
|
|
15
|
+
packages=find_packages(),
|
|
16
|
+
install_requires=[
|
|
17
|
+
"httpx>=0.24.0",
|
|
18
|
+
"tenacity>=8.0.0",
|
|
19
|
+
"pydantic>=2.0.0"
|
|
20
|
+
],
|
|
21
|
+
classifiers=[
|
|
22
|
+
"Programming Language :: Python :: 3",
|
|
23
|
+
"License :: OSI Approved :: MIT License",
|
|
24
|
+
"Operating System :: OS Independent",
|
|
25
|
+
"Intended Audience :: Developers",
|
|
26
|
+
],
|
|
27
|
+
python_requires=">=3.7",
|
|
28
|
+
)
|