unrestrictable 0.1.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
"""Python client for Unrestrictable: uncensored AI chat, images and video on one prepaid key.
|
|
2
|
+
|
|
3
|
+
The API speaks the OpenAI format, so the official `openai` SDK works against it as-is. When the package is installed
|
|
4
|
+
with the [openai] extra, openai_client() returns one pointed at the right base URL.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from __future__ import annotations
|
|
8
|
+
|
|
9
|
+
import json
|
|
10
|
+
import os
|
|
11
|
+
import urllib.error
|
|
12
|
+
import urllib.request
|
|
13
|
+
|
|
14
|
+
__version__ = "0.1.0"
|
|
15
|
+
|
|
16
|
+
BASE_URL = "https://api.unrestrictable.ai/v1"
|
|
17
|
+
API_KEY_ENV = "UNRESTRICTABLE_API_KEY"
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
class UnrestrictableError(RuntimeError):
|
|
21
|
+
"""An error response from the API, with its HTTP status and body."""
|
|
22
|
+
|
|
23
|
+
def __init__(self, status: int, body: str) -> None:
|
|
24
|
+
super().__init__(f"Unrestrictable API {status}: {body[:500]}")
|
|
25
|
+
self.status = status
|
|
26
|
+
self.body = body
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def _key(api_key: str | None) -> str:
|
|
30
|
+
key = api_key or os.environ.get(API_KEY_ENV, "")
|
|
31
|
+
if not key:
|
|
32
|
+
raise ValueError(
|
|
33
|
+
f"No API key. Set {API_KEY_ENV} or pass api_key=. "
|
|
34
|
+
"Get one at https://app.unrestrictable.ai/console"
|
|
35
|
+
)
|
|
36
|
+
return key
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def _request(path: str, api_key: str | None, method: str = "GET", body: dict | None = None) -> dict:
|
|
40
|
+
url = BASE_URL + path
|
|
41
|
+
data = json.dumps(body).encode() if body is not None else None
|
|
42
|
+
req = urllib.request.Request(
|
|
43
|
+
url,
|
|
44
|
+
data=data,
|
|
45
|
+
method=method,
|
|
46
|
+
headers={
|
|
47
|
+
"Authorization": f"Bearer {_key(api_key)}",
|
|
48
|
+
"Content-Type": "application/json",
|
|
49
|
+
"User-Agent": "unrestrictable-python/0.1.0",
|
|
50
|
+
},
|
|
51
|
+
)
|
|
52
|
+
try:
|
|
53
|
+
with urllib.request.urlopen(req, timeout=120) as r:
|
|
54
|
+
return json.load(r)
|
|
55
|
+
except urllib.error.HTTPError as e: # the API's own error body is the useful part
|
|
56
|
+
raise UnrestrictableError(e.code, e.read().decode("utf-8", "replace")) from None
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
def models(api_key: str | None = None) -> list[dict]:
|
|
60
|
+
"""Every model this key may call, with the developer's name. No key needed for the public list."""
|
|
61
|
+
url = BASE_URL + "/models"
|
|
62
|
+
req = urllib.request.Request(url, headers={"User-Agent": "unrestrictable-python/0.1.0"})
|
|
63
|
+
with urllib.request.urlopen(req, timeout=60) as r:
|
|
64
|
+
return json.load(r).get("data", [])
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
def catalog(api_key: str | None = None) -> dict:
|
|
68
|
+
"""The public catalog: every model, its price per million tokens and the endpoints it answers. No key needed."""
|
|
69
|
+
req = urllib.request.Request(BASE_URL + "/catalog", headers={"User-Agent": "unrestrictable-python/0.1.0"})
|
|
70
|
+
with urllib.request.urlopen(req, timeout=60) as r:
|
|
71
|
+
return json.load(r)
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
def chat(model: str, prompt: str, api_key: str | None = None, **params) -> str:
|
|
75
|
+
"""One turn, returned as text. `params` go into the request body (temperature, max_tokens, ...)."""
|
|
76
|
+
body = {"model": model, "messages": [{"role": "user", "content": prompt}], **params}
|
|
77
|
+
answer = _request("/chat/completions", api_key, "POST", body)
|
|
78
|
+
return answer["choices"][0]["message"]["content"]
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
def openai_client(api_key: str | None = None):
|
|
82
|
+
"""An openai.OpenAI client pointed at Unrestrictable. Needs the [openai] extra."""
|
|
83
|
+
from openai import OpenAI
|
|
84
|
+
|
|
85
|
+
return OpenAI(base_url=BASE_URL, api_key=_key(api_key))
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
def anthropic_client(api_key: str | None = None):
|
|
89
|
+
"""An anthropic.Anthropic client pointed at Unrestrictable. Needs the [anthropic] extra."""
|
|
90
|
+
from anthropic import Anthropic
|
|
91
|
+
|
|
92
|
+
return Anthropic(base_url=BASE_URL, api_key=_key(api_key))
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: unrestrictable
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Python client for Unrestrictable: uncensored AI chat, images and video on one prepaid key, through the OpenAI and Anthropic SDKs.
|
|
5
|
+
Author-email: Uplink Switch LLC <hello@unrestrictable.ai>
|
|
6
|
+
License: MIT
|
|
7
|
+
Project-URL: Homepage, https://unrestrictable.ai
|
|
8
|
+
Project-URL: Models, https://unrestrictable.ai/models
|
|
9
|
+
Project-URL: Media, https://unrestrictable.ai/media
|
|
10
|
+
Project-URL: Pricing, https://unrestrictable.ai/pricing
|
|
11
|
+
Project-URL: UncensoredLLM, https://uncensoredllm.ai
|
|
12
|
+
Project-URL: PentestLLM, https://pentestllm.ai
|
|
13
|
+
Project-URL: RelaySwitch, https://relayswitch.io
|
|
14
|
+
Project-URL: UplinkSwitch, https://uplinkswitch.ai
|
|
15
|
+
Keywords: ai,uncensored,llm,api,openai-compatible,anthropic,image-generation,video-generation
|
|
16
|
+
Classifier: Development Status :: 4 - Beta
|
|
17
|
+
Classifier: Intended Audience :: Developers
|
|
18
|
+
Classifier: Programming Language :: Python :: 3
|
|
19
|
+
Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
|
|
20
|
+
Classifier: Topic :: Software Development :: Libraries
|
|
21
|
+
Requires-Python: >=3.9
|
|
22
|
+
Description-Content-Type: text/markdown
|
|
23
|
+
License-File: LICENSE
|
|
24
|
+
Provides-Extra: openai
|
|
25
|
+
Requires-Dist: openai>=1.40; extra == "openai"
|
|
26
|
+
Provides-Extra: anthropic
|
|
27
|
+
Requires-Dist: anthropic>=0.34; extra == "anthropic"
|
|
28
|
+
Provides-Extra: all
|
|
29
|
+
Requires-Dist: openai>=1.40; extra == "all"
|
|
30
|
+
Requires-Dist: anthropic>=0.34; extra == "all"
|
|
31
|
+
Dynamic: license-file
|
|
32
|
+
|
|
33
|
+
# unrestrictable
|
|
34
|
+
|
|
35
|
+
Python client for [Unrestrictable](https://unrestrictable.ai): uncensored AI chat, images and video, with no ID, no phone and no prompt logs. Try it free with no sign-up, then pay by card, WeChat Pay or crypto from $10. The API speaks the OpenAI and Anthropic formats, so you keep the official SDKs.
|
|
36
|
+
|
|
37
|
+
```bash
|
|
38
|
+
pip install "unrestrictable[openai]" # or [anthropic], or [all]
|
|
39
|
+
export UNRESTRICTABLE_API_KEY="..."
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
Get a key at [app.unrestrictable.ai/console](https://app.unrestrictable.ai/console). Credit is prepaid from $10, with no subscription.
|
|
43
|
+
|
|
44
|
+
## OpenAI SDK
|
|
45
|
+
|
|
46
|
+
```python
|
|
47
|
+
import unrestrictable
|
|
48
|
+
|
|
49
|
+
client = unrestrictable.openai_client()
|
|
50
|
+
reply = client.chat.completions.create(
|
|
51
|
+
model="glm-5.3-uncensored",
|
|
52
|
+
messages=[{"role": "user", "content": "Say hi in five words."}],
|
|
53
|
+
)
|
|
54
|
+
print(reply.choices[0].message.content)
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
## Anthropic SDK
|
|
58
|
+
|
|
59
|
+
```python
|
|
60
|
+
import unrestrictable
|
|
61
|
+
|
|
62
|
+
client = unrestrictable.anthropic_client()
|
|
63
|
+
msg = client.messages.create(
|
|
64
|
+
model="deepseek-v4-pro-uncensored",
|
|
65
|
+
max_tokens=200,
|
|
66
|
+
messages=[{"role": "user", "content": "Say hi in five words."}],
|
|
67
|
+
)
|
|
68
|
+
print(msg.content[0].text)
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
## No SDK at all
|
|
72
|
+
|
|
73
|
+
```python
|
|
74
|
+
import unrestrictable
|
|
75
|
+
|
|
76
|
+
print(unrestrictable.chat("glm-5.3-uncensored", "Say hi in five words."))
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
## Prices, no key needed
|
|
80
|
+
|
|
81
|
+
```python
|
|
82
|
+
import unrestrictable
|
|
83
|
+
|
|
84
|
+
for m in unrestrictable.models():
|
|
85
|
+
p = m["pricing"]
|
|
86
|
+
print(f'{m["id"]:<40} ${p["input"]}/${p["output"]} per 1M tokens')
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
The public catalog at [api.unrestrictable.ai/v1/catalog](https://api.unrestrictable.ai/v1/catalog) carries every model, every price and every endpoint with no key and no rate limit worth worrying about.
|
|
90
|
+
|
|
91
|
+
## Without this package
|
|
92
|
+
|
|
93
|
+
Set `OPENAI_BASE_URL` or `ANTHROPIC_BASE_URL` to `https://api.unrestrictable.ai/v1` and use your key.
|
|
94
|
+
|
|
95
|
+
- Chat in the browser, no key: [unrestrictable.ai/chat](https://unrestrictable.ai/chat)
|
|
96
|
+
- Models and prices: [unrestrictable.ai/models](https://unrestrictable.ai/models)
|
|
97
|
+
- Images and video: [unrestrictable.ai/media](https://unrestrictable.ai/media)
|
|
98
|
+
- Daily real-model check: [unrestrictable.ai/proof](https://unrestrictable.ai/proof)
|
|
99
|
+
|
|
100
|
+
Run by Uplink Switch LLC.
|
|
101
|
+
|
|
102
|
+
## Sister sites
|
|
103
|
+
|
|
104
|
+
The same gateway, sold each on its own key and console:
|
|
105
|
+
|
|
106
|
+
- [UncensoredLLM](https://uncensoredllm.ai): the uncensored and abliterated builds as an API, priced per token.
|
|
107
|
+
- [PentestLLM](https://pentestllm.ai): uncensored AI for hacking and pentesting.
|
|
108
|
+
- [RelaySwitch](https://relayswitch.io): one API key for coding agents, paid by card or crypto.
|
|
109
|
+
- [UplinkSwitch](https://uplinkswitch.ai): the gateway itself: one key and one base URL for every model.
|
|
110
|
+
|
|
111
|
+
All of them: [UplinkSwitch](https://uplinkswitch.ai) for every model, [UncensoredLLM](https://uncensoredllm.ai) for abliterated builds, [Unrestrictable](https://unrestrictable.ai) for chat in the browser, [PentestLLM](https://pentestllm.ai) for security work, [RelaySwitch](https://relayswitch.io) for coding agents.
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
unrestrictable/__init__.py,sha256=Bdp94g6-mPU6I3CJNfiLlPV01g4u98ePeJFwTgfixX8,3467
|
|
2
|
+
unrestrictable-0.1.0.dist-info/licenses/LICENSE,sha256=C2_RBVnAJ42gW7RtTKadkuVLxoGLhq3j7Tu2gWuGIo4,50
|
|
3
|
+
unrestrictable-0.1.0.dist-info/METADATA,sha256=F6aoBrQRdBxaMqSYm5hN4fATQJ6kiTnZNsAWcpQyjPE,4338
|
|
4
|
+
unrestrictable-0.1.0.dist-info/WHEEL,sha256=mOdXR9C3wMVov7cSY8PY-XCzo3SmmWCHvGQr5csJoao,91
|
|
5
|
+
unrestrictable-0.1.0.dist-info/top_level.txt,sha256=8qc9m3gRgT7nS2MY7Uq2FIsZATE2a_GVzxCHlU_1rLc,15
|
|
6
|
+
unrestrictable-0.1.0.dist-info/RECORD,,
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
unrestrictable
|