bxlai 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.
- bxlai-1.0.0/PKG-INFO +66 -0
- bxlai-1.0.0/README.md +58 -0
- bxlai-1.0.0/bxAI.py +306 -0
- bxlai-1.0.0/bxlai.egg-info/PKG-INFO +66 -0
- bxlai-1.0.0/bxlai.egg-info/SOURCES.txt +8 -0
- bxlai-1.0.0/bxlai.egg-info/dependency_links.txt +1 -0
- bxlai-1.0.0/bxlai.egg-info/requires.txt +1 -0
- bxlai-1.0.0/bxlai.egg-info/top_level.txt +1 -0
- bxlai-1.0.0/pyproject.toml +14 -0
- bxlai-1.0.0/setup.cfg +4 -0
bxlai-1.0.0/PKG-INFO
ADDED
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: bxlai
|
|
3
|
+
Version: 1.0.0
|
|
4
|
+
Summary: BX AI library for Kilo AI models
|
|
5
|
+
Requires-Python: >=3.8
|
|
6
|
+
Description-Content-Type: text/markdown
|
|
7
|
+
Requires-Dist: requests
|
|
8
|
+
|
|
9
|
+
# bxAI
|
|
10
|
+
|
|
11
|
+
Python library built from the supplied Kilo model catalog.
|
|
12
|
+
|
|
13
|
+
Snapshot:
|
|
14
|
+
- Total models: 400
|
|
15
|
+
- Free models: 18
|
|
16
|
+
- Token models: 382
|
|
17
|
+
|
|
18
|
+
## Install
|
|
19
|
+
|
|
20
|
+
```bash
|
|
21
|
+
pip install requests
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
Copy `bxAI.py` and `models.json` into the same folder.
|
|
25
|
+
|
|
26
|
+
## Usage
|
|
27
|
+
|
|
28
|
+
Free model:
|
|
29
|
+
|
|
30
|
+
```python
|
|
31
|
+
from bxAI import Ai
|
|
32
|
+
|
|
33
|
+
print(Ai(1, "هلو شلونك؟"))
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
Paid/token model:
|
|
37
|
+
|
|
38
|
+
```python
|
|
39
|
+
from bxAI import AIT
|
|
40
|
+
|
|
41
|
+
print(AIT("YOUR_KILO_TOKEN", 25, "اكتبلي برنامج بايثون"))
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
Show models:
|
|
45
|
+
|
|
46
|
+
```python
|
|
47
|
+
from bxAI import MODLES
|
|
48
|
+
|
|
49
|
+
MODLES()
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
The terminal shows only BXCode/BXL aliases and model numbers.
|
|
53
|
+
Original provider/model names are not printed by `MODLES()`.
|
|
54
|
+
|
|
55
|
+
## Refresh
|
|
56
|
+
|
|
57
|
+
Kilo's live catalog changes. To replace the bundled snapshot:
|
|
58
|
+
|
|
59
|
+
```python
|
|
60
|
+
from bxAI import refresh_models
|
|
61
|
+
|
|
62
|
+
print(refresh_models())
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
`refresh_models()` fetches Kilo's `/models` endpoint and regenerates the
|
|
66
|
+
BXCode/BXL aliases.
|
bxlai-1.0.0/README.md
ADDED
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
# bxAI
|
|
2
|
+
|
|
3
|
+
Python library built from the supplied Kilo model catalog.
|
|
4
|
+
|
|
5
|
+
Snapshot:
|
|
6
|
+
- Total models: 400
|
|
7
|
+
- Free models: 18
|
|
8
|
+
- Token models: 382
|
|
9
|
+
|
|
10
|
+
## Install
|
|
11
|
+
|
|
12
|
+
```bash
|
|
13
|
+
pip install requests
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
Copy `bxAI.py` and `models.json` into the same folder.
|
|
17
|
+
|
|
18
|
+
## Usage
|
|
19
|
+
|
|
20
|
+
Free model:
|
|
21
|
+
|
|
22
|
+
```python
|
|
23
|
+
from bxAI import Ai
|
|
24
|
+
|
|
25
|
+
print(Ai(1, "هلو شلونك؟"))
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
Paid/token model:
|
|
29
|
+
|
|
30
|
+
```python
|
|
31
|
+
from bxAI import AIT
|
|
32
|
+
|
|
33
|
+
print(AIT("YOUR_KILO_TOKEN", 25, "اكتبلي برنامج بايثون"))
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
Show models:
|
|
37
|
+
|
|
38
|
+
```python
|
|
39
|
+
from bxAI import MODLES
|
|
40
|
+
|
|
41
|
+
MODLES()
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
The terminal shows only BXCode/BXL aliases and model numbers.
|
|
45
|
+
Original provider/model names are not printed by `MODLES()`.
|
|
46
|
+
|
|
47
|
+
## Refresh
|
|
48
|
+
|
|
49
|
+
Kilo's live catalog changes. To replace the bundled snapshot:
|
|
50
|
+
|
|
51
|
+
```python
|
|
52
|
+
from bxAI import refresh_models
|
|
53
|
+
|
|
54
|
+
print(refresh_models())
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
`refresh_models()` fetches Kilo's `/models` endpoint and regenerates the
|
|
58
|
+
BXCode/BXL aliases.
|
bxlai-1.0.0/bxAI.py
ADDED
|
@@ -0,0 +1,306 @@
|
|
|
1
|
+
"""
|
|
2
|
+
bxAI - Lightweight Python wrapper for the Kilo AI Gateway.
|
|
3
|
+
|
|
4
|
+
Public API:
|
|
5
|
+
Ai(model_number, question)
|
|
6
|
+
AIT(token, model_number, question)
|
|
7
|
+
MODLES()
|
|
8
|
+
|
|
9
|
+
The display name of every model is intentionally replaced with a BXCode/BXL alias.
|
|
10
|
+
The real Kilo model IDs are kept internally in models.json because the gateway
|
|
11
|
+
needs the actual ID to make a request.
|
|
12
|
+
|
|
13
|
+
The bundled models.json is a snapshot of the Kilo model catalog supplied with
|
|
14
|
+
this package. Kilo's live catalog can change, so use refresh_models() when you
|
|
15
|
+
want to replace it with the current catalog.
|
|
16
|
+
"""
|
|
17
|
+
|
|
18
|
+
from __future__ import annotations
|
|
19
|
+
|
|
20
|
+
import json
|
|
21
|
+
import os
|
|
22
|
+
import shutil
|
|
23
|
+
from typing import Any, Dict, List, Optional
|
|
24
|
+
|
|
25
|
+
import requests
|
|
26
|
+
|
|
27
|
+
BASE_URL = "https://api.kilo.ai/api/gateway"
|
|
28
|
+
CHAT_URL = f"{BASE_URL}/chat/completions"
|
|
29
|
+
MODELS_URL = f"{BASE_URL}/models"
|
|
30
|
+
|
|
31
|
+
_PAGE_SIZE = 18
|
|
32
|
+
_HERE = os.path.dirname(os.path.abspath(__file__))
|
|
33
|
+
_MODELS_FILE = os.path.join(_HERE, "models.json")
|
|
34
|
+
|
|
35
|
+
# Keep the catalog private to the module.
|
|
36
|
+
_MODELS: List[Dict[str, Any]] = []
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def _load_models() -> None:
|
|
40
|
+
global _MODELS
|
|
41
|
+
with open(_MODELS_FILE, "r", encoding="utf-8") as f:
|
|
42
|
+
_MODELS = json.load(f)
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
_load_models()
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
def _find_model(number: int) -> Dict[str, Any]:
|
|
49
|
+
try:
|
|
50
|
+
number = int(number)
|
|
51
|
+
except (TypeError, ValueError):
|
|
52
|
+
raise ValueError("رقم النموذج يجب أن يكون رقمًا.")
|
|
53
|
+
|
|
54
|
+
for model in _MODELS:
|
|
55
|
+
if model["number"] == number:
|
|
56
|
+
return model
|
|
57
|
+
|
|
58
|
+
raise ValueError(f"ماكو نموذج بالرقم {number}.")
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
def _request(model: Dict[str, Any], question: str, token: Optional[str] = None) -> str:
|
|
62
|
+
if not isinstance(question, str) or not question.strip():
|
|
63
|
+
raise ValueError("السؤال فارغ.")
|
|
64
|
+
|
|
65
|
+
headers = {
|
|
66
|
+
"Content-Type": "application/json",
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
# Kilo allows unauthenticated requests for free models.
|
|
70
|
+
if token:
|
|
71
|
+
headers["Authorization"] = f"Bearer {token.strip()}"
|
|
72
|
+
|
|
73
|
+
payload = {
|
|
74
|
+
"model": model["id"],
|
|
75
|
+
"messages": [
|
|
76
|
+
{
|
|
77
|
+
"role": "user",
|
|
78
|
+
"content": question,
|
|
79
|
+
}
|
|
80
|
+
],
|
|
81
|
+
"stream": False,
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
response = requests.post(
|
|
85
|
+
CHAT_URL,
|
|
86
|
+
headers=headers,
|
|
87
|
+
json=payload,
|
|
88
|
+
timeout=90,
|
|
89
|
+
)
|
|
90
|
+
|
|
91
|
+
if response.status_code >= 400:
|
|
92
|
+
try:
|
|
93
|
+
detail = response.json()
|
|
94
|
+
except Exception:
|
|
95
|
+
detail = response.text
|
|
96
|
+
|
|
97
|
+
raise RuntimeError(
|
|
98
|
+
f"Kilo API HTTP {response.status_code}: {detail}"
|
|
99
|
+
)
|
|
100
|
+
|
|
101
|
+
data = response.json()
|
|
102
|
+
|
|
103
|
+
try:
|
|
104
|
+
content = data["choices"][0]["message"]["content"]
|
|
105
|
+
except (KeyError, IndexError, TypeError):
|
|
106
|
+
raise RuntimeError(f"استجابة غير متوقعة من Kilo: {data}")
|
|
107
|
+
|
|
108
|
+
# Some gateways/providers may return structured content.
|
|
109
|
+
if isinstance(content, list):
|
|
110
|
+
parts = []
|
|
111
|
+
for item in content:
|
|
112
|
+
if isinstance(item, dict) and "text" in item:
|
|
113
|
+
parts.append(str(item["text"]))
|
|
114
|
+
elif isinstance(item, str):
|
|
115
|
+
parts.append(item)
|
|
116
|
+
return "".join(parts)
|
|
117
|
+
|
|
118
|
+
return str(content)
|
|
119
|
+
|
|
120
|
+
|
|
121
|
+
def Ai(model_number: int, question: str) -> str:
|
|
122
|
+
"""
|
|
123
|
+
Ask a FREE model without a token.
|
|
124
|
+
|
|
125
|
+
Example:
|
|
126
|
+
print(Ai(1, "هلو شلونك؟"))
|
|
127
|
+
"""
|
|
128
|
+
model = _find_model(model_number)
|
|
129
|
+
|
|
130
|
+
if not model["free"]:
|
|
131
|
+
raise PermissionError(
|
|
132
|
+
f"{model['alias']} نموذج غير مجاني. استخدم:\n"
|
|
133
|
+
f'AIT("YOUR_KILO_TOKEN", {model["number"]}, "السؤال")'
|
|
134
|
+
)
|
|
135
|
+
|
|
136
|
+
return _request(model, question)
|
|
137
|
+
|
|
138
|
+
|
|
139
|
+
def AIT(token: str, model_number: int, question: str) -> str:
|
|
140
|
+
"""
|
|
141
|
+
Ask a model using a Kilo API token.
|
|
142
|
+
|
|
143
|
+
Intended for paid/non-free models.
|
|
144
|
+
|
|
145
|
+
Example:
|
|
146
|
+
print(AIT("YOUR_TOKEN", 25, "اكتبلي كود بايثون"))
|
|
147
|
+
"""
|
|
148
|
+
if not isinstance(token, str) or not token.strip():
|
|
149
|
+
raise ValueError("توكن Kilo فارغ.")
|
|
150
|
+
|
|
151
|
+
model = _find_model(model_number)
|
|
152
|
+
return _request(model, question, token=token)
|
|
153
|
+
|
|
154
|
+
|
|
155
|
+
def _print_page(items: List[Dict[str, Any]], title: str, page: int, pages: int) -> None:
|
|
156
|
+
width = shutil.get_terminal_size((80, 24)).columns
|
|
157
|
+
width = max(50, min(width, 110))
|
|
158
|
+
|
|
159
|
+
print("\n" + "=" * width)
|
|
160
|
+
print(f" bxAI | {title} | صفحة {page}/{pages}")
|
|
161
|
+
print("=" * width)
|
|
162
|
+
|
|
163
|
+
for model in items:
|
|
164
|
+
status = "FREE" if model["free"] else "TOKEN"
|
|
165
|
+
print(
|
|
166
|
+
f'{model["number"]:>3} | '
|
|
167
|
+
f'{model["alias"]:<14} | '
|
|
168
|
+
f'{status}'
|
|
169
|
+
)
|
|
170
|
+
|
|
171
|
+
print("-" * width)
|
|
172
|
+
|
|
173
|
+
|
|
174
|
+
def MODLES() -> None:
|
|
175
|
+
"""
|
|
176
|
+
Display the complete catalog in terminal pages.
|
|
177
|
+
|
|
178
|
+
Free models are shown first.
|
|
179
|
+
Press Enter for the next page.
|
|
180
|
+
After the free section, the paid/token section is shown.
|
|
181
|
+
Press q to exit.
|
|
182
|
+
"""
|
|
183
|
+
if not _MODELS:
|
|
184
|
+
print("ماكو نماذج محملة.")
|
|
185
|
+
return
|
|
186
|
+
|
|
187
|
+
sections = [
|
|
188
|
+
("FREE MODELS", [m for m in _MODELS if m["free"]]),
|
|
189
|
+
("TOKEN MODELS", [m for m in _MODELS if not m["free"]]),
|
|
190
|
+
]
|
|
191
|
+
|
|
192
|
+
for section_title, items in sections:
|
|
193
|
+
if not items:
|
|
194
|
+
continue
|
|
195
|
+
|
|
196
|
+
pages = (len(items) + _PAGE_SIZE - 1) // _PAGE_SIZE
|
|
197
|
+
|
|
198
|
+
for page in range(1, pages + 1):
|
|
199
|
+
start = (page - 1) * _PAGE_SIZE
|
|
200
|
+
chunk = items[start:start + _PAGE_SIZE]
|
|
201
|
+
|
|
202
|
+
_print_page(chunk, section_title, page, pages)
|
|
203
|
+
|
|
204
|
+
if page < pages:
|
|
205
|
+
choice = input("Enter = التالي | q = خروج: ").strip().lower()
|
|
206
|
+
if choice == "q":
|
|
207
|
+
return
|
|
208
|
+
else:
|
|
209
|
+
choice = input("Enter = القسم التالي | q = خروج: ").strip().lower()
|
|
210
|
+
if choice == "q":
|
|
211
|
+
return
|
|
212
|
+
|
|
213
|
+
print("\nانتهت قائمة bxAI.")
|
|
214
|
+
|
|
215
|
+
|
|
216
|
+
def get_model(number: int) -> Dict[str, Any]:
|
|
217
|
+
"""Return internal model metadata for advanced users."""
|
|
218
|
+
return dict(_find_model(number))
|
|
219
|
+
|
|
220
|
+
|
|
221
|
+
def count_models() -> Dict[str, int]:
|
|
222
|
+
"""Return counts without exposing original model names."""
|
|
223
|
+
return {
|
|
224
|
+
"total": len(_MODELS),
|
|
225
|
+
"free": sum(1 for m in _MODELS if m["free"]),
|
|
226
|
+
"token": sum(1 for m in _MODELS if not m["free"]),
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
|
|
230
|
+
def refresh_models(token: Optional[str] = None) -> Dict[str, int]:
|
|
231
|
+
"""
|
|
232
|
+
Replace models.json with Kilo's current /models catalog.
|
|
233
|
+
|
|
234
|
+
GET /models does not require authentication. A token is accepted only
|
|
235
|
+
for compatibility with callers that want to pass one.
|
|
236
|
+
|
|
237
|
+
Display aliases are regenerated as BXCode-### / BXL-### and original
|
|
238
|
+
model names are never used in the MODLES() display.
|
|
239
|
+
"""
|
|
240
|
+
headers = {"Content-Type": "application/json"}
|
|
241
|
+
if token:
|
|
242
|
+
headers["Authorization"] = f"Bearer {token.strip()}"
|
|
243
|
+
|
|
244
|
+
response = requests.get(MODELS_URL, headers=headers, timeout=30)
|
|
245
|
+
response.raise_for_status()
|
|
246
|
+
|
|
247
|
+
data = response.json()
|
|
248
|
+
raw = data.get("data")
|
|
249
|
+
|
|
250
|
+
if not isinstance(raw, list):
|
|
251
|
+
raise RuntimeError("Kilo /models لم يرجع قائمة نماذج.")
|
|
252
|
+
|
|
253
|
+
# Prefer Kilo's explicit isFree flag. Also accept zero prompt+completion
|
|
254
|
+
# pricing as free if the flag is absent.
|
|
255
|
+
def is_free(m: Dict[str, Any]) -> bool:
|
|
256
|
+
if m.get("isFree") is True:
|
|
257
|
+
return True
|
|
258
|
+
pricing = m.get("pricing") or {}
|
|
259
|
+
return str(pricing.get("prompt")) == "0" and str(pricing.get("completion")) == "0"
|
|
260
|
+
|
|
261
|
+
free = [m for m in raw if is_free(m)]
|
|
262
|
+
paid = [m for m in raw if not is_free(m)]
|
|
263
|
+
|
|
264
|
+
new_models = []
|
|
265
|
+
|
|
266
|
+
for idx, m in enumerate(free, 1):
|
|
267
|
+
number = idx
|
|
268
|
+
alias = f"BXCode-{number:03d}" if number % 2 else f"BXL-{number:03d}"
|
|
269
|
+
new_models.append({
|
|
270
|
+
"number": number,
|
|
271
|
+
"alias": alias,
|
|
272
|
+
"id": m["id"],
|
|
273
|
+
"free": True,
|
|
274
|
+
"context_length": m.get("context_length")
|
|
275
|
+
or m.get("top_provider", {}).get("context_length"),
|
|
276
|
+
})
|
|
277
|
+
|
|
278
|
+
start = len(free) + 1
|
|
279
|
+
for offset, m in enumerate(paid):
|
|
280
|
+
number = start + offset
|
|
281
|
+
alias = f"BXCode-{number:03d}" if number % 2 else f"BXL-{number:03d}"
|
|
282
|
+
new_models.append({
|
|
283
|
+
"number": number,
|
|
284
|
+
"alias": alias,
|
|
285
|
+
"id": m["id"],
|
|
286
|
+
"free": False,
|
|
287
|
+
"context_length": m.get("context_length")
|
|
288
|
+
or m.get("top_provider", {}).get("context_length"),
|
|
289
|
+
})
|
|
290
|
+
|
|
291
|
+
with open(_MODELS_FILE, "w", encoding="utf-8") as f:
|
|
292
|
+
json.dump(new_models, f, ensure_ascii=False, indent=2)
|
|
293
|
+
|
|
294
|
+
_load_models()
|
|
295
|
+
|
|
296
|
+
return count_models()
|
|
297
|
+
|
|
298
|
+
|
|
299
|
+
__all__ = [
|
|
300
|
+
"Ai",
|
|
301
|
+
"AIT",
|
|
302
|
+
"MODLES",
|
|
303
|
+
"get_model",
|
|
304
|
+
"count_models",
|
|
305
|
+
"refresh_models",
|
|
306
|
+
]
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: bxlai
|
|
3
|
+
Version: 1.0.0
|
|
4
|
+
Summary: BX AI library for Kilo AI models
|
|
5
|
+
Requires-Python: >=3.8
|
|
6
|
+
Description-Content-Type: text/markdown
|
|
7
|
+
Requires-Dist: requests
|
|
8
|
+
|
|
9
|
+
# bxAI
|
|
10
|
+
|
|
11
|
+
Python library built from the supplied Kilo model catalog.
|
|
12
|
+
|
|
13
|
+
Snapshot:
|
|
14
|
+
- Total models: 400
|
|
15
|
+
- Free models: 18
|
|
16
|
+
- Token models: 382
|
|
17
|
+
|
|
18
|
+
## Install
|
|
19
|
+
|
|
20
|
+
```bash
|
|
21
|
+
pip install requests
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
Copy `bxAI.py` and `models.json` into the same folder.
|
|
25
|
+
|
|
26
|
+
## Usage
|
|
27
|
+
|
|
28
|
+
Free model:
|
|
29
|
+
|
|
30
|
+
```python
|
|
31
|
+
from bxAI import Ai
|
|
32
|
+
|
|
33
|
+
print(Ai(1, "هلو شلونك؟"))
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
Paid/token model:
|
|
37
|
+
|
|
38
|
+
```python
|
|
39
|
+
from bxAI import AIT
|
|
40
|
+
|
|
41
|
+
print(AIT("YOUR_KILO_TOKEN", 25, "اكتبلي برنامج بايثون"))
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
Show models:
|
|
45
|
+
|
|
46
|
+
```python
|
|
47
|
+
from bxAI import MODLES
|
|
48
|
+
|
|
49
|
+
MODLES()
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
The terminal shows only BXCode/BXL aliases and model numbers.
|
|
53
|
+
Original provider/model names are not printed by `MODLES()`.
|
|
54
|
+
|
|
55
|
+
## Refresh
|
|
56
|
+
|
|
57
|
+
Kilo's live catalog changes. To replace the bundled snapshot:
|
|
58
|
+
|
|
59
|
+
```python
|
|
60
|
+
from bxAI import refresh_models
|
|
61
|
+
|
|
62
|
+
print(refresh_models())
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
`refresh_models()` fetches Kilo's `/models` endpoint and regenerates the
|
|
66
|
+
BXCode/BXL aliases.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
requests
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
bxAI
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=61", "wheel"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "bxlai"
|
|
7
|
+
version = "1.0.0"
|
|
8
|
+
description = "BX AI library for Kilo AI models"
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
requires-python = ">=3.8"
|
|
11
|
+
dependencies = ["requests"]
|
|
12
|
+
|
|
13
|
+
[tool.setuptools]
|
|
14
|
+
py-modules = ["bxAI"]
|
bxlai-1.0.0/setup.cfg
ADDED