mindupload 1.5.1__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.
mindupload/__init__.py ADDED
@@ -0,0 +1,32 @@
1
+ """Mind Upload \u2014 official server-side SDK for the partner API.
2
+
3
+ from mindupload import MindUpload
4
+
5
+ mu = MindUpload(partner_key="pk_live_...")
6
+ session = mu.login(username="ada", password="...")
7
+ reply = mu.rag(username="ada", password=session.jwt, codename="muse", text="hi")
8
+ print(reply.response_text)
9
+
10
+ Digital consciousness. Yours forever.
11
+ """
12
+ from ._client import Result
13
+ from ._errors import (
14
+ APIError,
15
+ AuthenticationError,
16
+ MindUploadConnectionError,
17
+ MindUploadError,
18
+ RateLimitError,
19
+ )
20
+ from ._operations import MindUpload
21
+ from ._version import __version__
22
+
23
+ __all__ = [
24
+ "MindUpload",
25
+ "Result",
26
+ "MindUploadError",
27
+ "APIError",
28
+ "AuthenticationError",
29
+ "RateLimitError",
30
+ "MindUploadConnectionError",
31
+ "__version__",
32
+ ]
mindupload/_client.py ADDED
@@ -0,0 +1,148 @@
1
+ """Runtime core for the Mind Upload SDK — transport, retries, error mapping.
2
+
3
+ Hand-written and stable; the per-operation methods live in the generated
4
+ `_operations.py`. Built entirely on the standard library.
5
+ """
6
+ import json
7
+ import socket
8
+ import time
9
+ import urllib.error
10
+ import urllib.request
11
+
12
+ from ._errors import (
13
+ APIError,
14
+ AuthenticationError,
15
+ MindUploadConnectionError,
16
+ MindUploadError,
17
+ RateLimitError,
18
+ )
19
+ from ._version import __version__
20
+
21
+ DEFAULT_BASE_URL = "https://partner.mindupload.app"
22
+ _AUTH_HEADER = "X-Partner-Key"
23
+ # Only server backpressure is retried. Operations are non-idempotent POSTs (rag
24
+ # spends credits, create_* mutate), so 5xx / network / timeout failures are
25
+ # surfaced immediately rather than risking a duplicate side effect.
26
+ _RETRY_STATUSES = frozenset({429, 503})
27
+
28
+
29
+ class Result(dict):
30
+ """A response envelope with attribute access.
31
+
32
+ `result.success`, `result.jwt`, `result["jwt"]` all work; any field the API
33
+ returns is available without the SDK needing to know it in advance.
34
+ """
35
+
36
+ __slots__ = ()
37
+
38
+ def __getattr__(self, name):
39
+ try:
40
+ return self[name]
41
+ except KeyError as exc:
42
+ raise AttributeError(name) from exc
43
+
44
+
45
+ class _BaseClient:
46
+ """Transport, retry, and error handling shared by every operation."""
47
+
48
+ def __init__(
49
+ self,
50
+ partner_key,
51
+ *,
52
+ base_url=DEFAULT_BASE_URL,
53
+ preferred_language=None,
54
+ timeout=30.0,
55
+ max_retries=2,
56
+ user_agent=None,
57
+ ):
58
+ if not partner_key:
59
+ raise ValueError(
60
+ "partner_key is required. It is a server-side secret \u2014 "
61
+ "never expose it to a browser or ship it in client code."
62
+ )
63
+ self._partner_key = partner_key
64
+ self._base_url = base_url.rstrip("/")
65
+ self._preferred_language = preferred_language
66
+ self._timeout = timeout
67
+ self._max_retries = max_retries
68
+ self._user_agent = user_agent or ("mindupload-python/" + __version__)
69
+
70
+ def _request(self, operation, params):
71
+ body = {key: value for key, value in params.items() if value is not None}
72
+ if "preferred_language" not in body and self._preferred_language is not None:
73
+ body["preferred_language"] = self._preferred_language
74
+ data = json.dumps(body).encode("utf-8")
75
+ url = self._base_url + "/v1/" + operation
76
+ headers = {
77
+ "Content-Type": "application/json",
78
+ "Accept": "application/json",
79
+ _AUTH_HEADER: self._partner_key,
80
+ "User-Agent": self._user_agent,
81
+ }
82
+
83
+ attempt = 0
84
+ while True:
85
+ request = urllib.request.Request(url, data=data, method="POST", headers=headers)
86
+ try:
87
+ with urllib.request.urlopen(request, timeout=self._timeout) as response:
88
+ status = getattr(response, "status", None) or response.getcode()
89
+ raw = response.read()
90
+ payload = json.loads(raw.decode("utf-8")) if raw else {}
91
+ except urllib.error.HTTPError as exc:
92
+ status = exc.code
93
+ try:
94
+ raw = exc.read()
95
+ payload = json.loads(raw.decode("utf-8")) if raw else {}
96
+ except ValueError:
97
+ payload = {}
98
+ if status in _RETRY_STATUSES and attempt < self._max_retries:
99
+ attempt += 1
100
+ time.sleep(self._backoff(attempt, exc.headers))
101
+ continue
102
+ self._raise(status, payload, operation, exc.headers)
103
+ except urllib.error.URLError as exc:
104
+ # Not retried: the request may already have reached the backend.
105
+ raise MindUploadConnectionError(
106
+ "Could not reach the Mind Upload API for '" + operation + "': " + str(exc.reason),
107
+ operation=operation,
108
+ ) from exc
109
+ except (TimeoutError, socket.timeout) as exc:
110
+ # A read/response timeout (socket.timeout is a bare TimeoutError,
111
+ # not a URLError, so it must be caught explicitly).
112
+ raise MindUploadConnectionError(
113
+ "Request to the Mind Upload API timed out for '" + operation + "'.",
114
+ operation=operation,
115
+ ) from exc
116
+
117
+ if not payload.get("success", False):
118
+ raise MindUploadError(
119
+ payload.get("error_message") or (operation + " failed"),
120
+ operation=operation,
121
+ response=Result(payload),
122
+ )
123
+ return Result(payload)
124
+
125
+ def _backoff(self, attempt, headers):
126
+ if headers is not None:
127
+ retry_after = headers.get("Retry-After")
128
+ if retry_after:
129
+ try:
130
+ return min(float(retry_after), 60.0)
131
+ except (TypeError, ValueError):
132
+ pass
133
+ return min(0.5 * (2 ** (attempt - 1)), 8.0)
134
+
135
+ def _raise(self, status, payload, operation, headers):
136
+ message = (payload.get("error_message") if isinstance(payload, dict) else None) or ("HTTP " + str(status))
137
+ response = Result(payload if isinstance(payload, dict) else {})
138
+ if status == 401:
139
+ raise AuthenticationError(message, status=status, operation=operation, response=response)
140
+ if status == 429:
141
+ retry_after = None
142
+ if headers is not None and headers.get("Retry-After"):
143
+ try:
144
+ retry_after = float(headers.get("Retry-After"))
145
+ except (TypeError, ValueError):
146
+ retry_after = None
147
+ raise RateLimitError(message, status=status, operation=operation, response=response, retry_after=retry_after)
148
+ raise APIError(message, status=status, operation=operation, response=response)
mindupload/_errors.py ADDED
@@ -0,0 +1,44 @@
1
+ """Exceptions raised by the Mind Upload SDK.
2
+
3
+ Every failure — a logical failure (`success: false`), an authentication or
4
+ rate-limit rejection, an unexpected HTTP status, or a network problem — is a
5
+ `MindUploadError`, so a single `except MindUploadError` catches everything.
6
+ """
7
+
8
+
9
+ class MindUploadError(Exception):
10
+ """Base class for every Mind Upload error."""
11
+
12
+ def __init__(self, message, *, operation=None, response=None):
13
+ super().__init__(message)
14
+ self.message = message
15
+ self.operation = operation
16
+ self.response = response
17
+ self.status = None # overridden by APIError; always present so e.status is safe
18
+
19
+
20
+ class APIError(MindUploadError):
21
+ """The API returned an error HTTP status (or an unexpected response)."""
22
+
23
+ def __init__(self, message, *, status=None, operation=None, response=None):
24
+ super().__init__(message, operation=operation, response=response)
25
+ self.status = status
26
+
27
+
28
+ class AuthenticationError(APIError):
29
+ """The partner key was missing, malformed, or rejected (HTTP 401)."""
30
+
31
+
32
+ class RateLimitError(APIError):
33
+ """A rate limit or credit cap was hit (HTTP 429).
34
+
35
+ `retry_after` is the server-advised wait in seconds, when provided.
36
+ """
37
+
38
+ def __init__(self, message, *, status=None, operation=None, response=None, retry_after=None):
39
+ super().__init__(message, status=status, operation=operation, response=response)
40
+ self.retry_after = retry_after
41
+
42
+
43
+ class MindUploadConnectionError(MindUploadError):
44
+ """The API could not be reached (DNS, TLS, timeout, or network failure)."""
@@ -0,0 +1,312 @@
1
+ # GENERATED by sdk/build.py from openapi.json v1.5.1 — do not edit by hand.
2
+ """Every Mind Upload partner API operation, one typed method each."""
3
+ from typing import Any, Dict, List, Optional
4
+
5
+ from ._client import Result, _BaseClient
6
+
7
+
8
+ class MindUpload(_BaseClient):
9
+ """Client for the Mind Upload partner API.
10
+
11
+ Create one with your partner key, then call any operation. Every method
12
+ returns a :class:`Result` (attribute + dict access) and raises a
13
+ :class:`~mindupload.MindUploadError` subclass on failure.
14
+ """
15
+
16
+ # --- AI Consciousnesses ------------------------------------------------
17
+ def create_clone(self, *, preferred_language: Optional[str] = None, username: Optional[str] = None, password: Optional[str] = None, codename: Optional[str] = None, nickname: Optional[str] = None, gender: Optional[str] = None, avatar_id: Optional[str] = None, accept_chatroom_invitation_by_others: Optional[bool] = None) -> Result:
18
+ """Create a new AI consciousness for the user.
19
+
20
+ Parameters: preferred_language, username, password, codename, nickname, gender, avatar_id, accept_chatroom_invitation_by_others.
21
+ Response fields: success, error_message.
22
+ Full parameter reference: https://docs.mindupload.app
23
+ """
24
+ return self._request("create_clone", {"preferred_language": preferred_language, "username": username, "password": password, "codename": codename, "nickname": nickname, "gender": gender, "avatar_id": avatar_id, "accept_chatroom_invitation_by_others": accept_chatroom_invitation_by_others})
25
+
26
+ def get_clones(self, *, preferred_language: Optional[str] = None, username: Optional[str] = None, password: Optional[str] = None) -> Result:
27
+ """List the user's AI consciousnesses.
28
+
29
+ Parameters: preferred_language, username, password.
30
+ Response fields: success, error_message, clones.
31
+ Full parameter reference: https://docs.mindupload.app
32
+ """
33
+ return self._request("get_clones", {"preferred_language": preferred_language, "username": username, "password": password})
34
+
35
+ def update_clone(self, *, preferred_language: Optional[str] = None, username: Optional[str] = None, password: Optional[str] = None, codename: Optional[str] = None, nickname: Optional[str] = None, gender: Optional[str] = None, avatar_id: Optional[str] = None, accept_chatroom_invitation_by_others: Optional[bool] = None) -> Result:
36
+ """Update an AI consciousness's profile.
37
+
38
+ Parameters: preferred_language, username, password, codename, nickname, gender, avatar_id, accept_chatroom_invitation_by_others.
39
+ Response fields: success, error_message.
40
+ Full parameter reference: https://docs.mindupload.app
41
+ """
42
+ return self._request("update_clone", {"preferred_language": preferred_language, "username": username, "password": password, "codename": codename, "nickname": nickname, "gender": gender, "avatar_id": avatar_id, "accept_chatroom_invitation_by_others": accept_chatroom_invitation_by_others})
43
+
44
+ # --- Account -----------------------------------------------------------
45
+ def get_quota(self, *, preferred_language: Optional[str] = None, username: Optional[str] = None) -> Result:
46
+ """Check your partner API rate limits, credit caps, and current usage.
47
+
48
+ Parameters: preferred_language, username.
49
+ Response fields: success, error_message, per_user_rate_limit_per_min, per_user_daily_credit_cap, partner_rate_limit_per_min, partner_daily_credit_cap, max_users, registered_users, partner_requests_last_minute, partner_credit_spends_last_day, user, user_requests_last_minute, user_credit_spends_last_day, credit_spending_tasks, operation_duration_ms.
50
+ Full parameter reference: https://docs.mindupload.app
51
+ """
52
+ return self._request("get_quota", {"preferred_language": preferred_language, "username": username})
53
+
54
+ # --- Authentication ----------------------------------------------------
55
+ def check_username(self, *, preferred_language: Optional[str] = None, username: Optional[str] = None) -> Result:
56
+ """Check whether a username is still available before registering.
57
+
58
+ Parameters: preferred_language, username.
59
+ Response fields: success, error_message, exists, disabled, rate_limited, flood_window, wait_seconds, operation_duration_ms.
60
+ Full parameter reference: https://docs.mindupload.app
61
+ """
62
+ return self._request("check_username", {"preferred_language": preferred_language, "username": username})
63
+
64
+ def login(self, *, preferred_language: Optional[str] = None, username: Optional[str] = None, password: Optional[str] = None) -> Result:
65
+ """Sign a user in and receive a session token (JWT) for subsequent calls.
66
+
67
+ Parameters: preferred_language, username, password.
68
+ Response fields: success, error_message, jwt, decrypted_user.
69
+ Full parameter reference: https://docs.mindupload.app
70
+ """
71
+ return self._request("login", {"preferred_language": preferred_language, "username": username, "password": password})
72
+
73
+ def logout(self, *, preferred_language: Optional[str] = None, username: Optional[str] = None, password: Optional[str] = None) -> Result:
74
+ """End the current user session.
75
+
76
+ Parameters: preferred_language, username, password.
77
+ Response fields: success, error_message, revoked, operation_duration_ms.
78
+ Full parameter reference: https://docs.mindupload.app
79
+ """
80
+ return self._request("logout", {"preferred_language": preferred_language, "username": username, "password": password})
81
+
82
+ def register(self, *, preferred_language: Optional[str] = None, username: Optional[str] = None, password: Optional[str] = None, email: Optional[str] = None, email_verification_code: Optional[str] = None, first_name: Optional[str] = None, last_name: Optional[str] = None, nickname: Optional[str] = None, gender: Optional[str] = None, birth_date: Optional[int] = None, phone_number: Optional[str] = None, avatar_id: Optional[str] = None, accept_chatroom_invitation_by_others: Optional[bool] = None) -> Result:
83
+ """Create a user account on your platform.
84
+
85
+ Parameters: preferred_language, username, password, email, email_verification_code, first_name, last_name, nickname, gender, birth_date, phone_number, avatar_id, accept_chatroom_invitation_by_others.
86
+ Response fields: success, error_message, jwt, decrypted_user.
87
+ Full parameter reference: https://docs.mindupload.app
88
+ """
89
+ return self._request("register", {"preferred_language": preferred_language, "username": username, "password": password, "email": email, "email_verification_code": email_verification_code, "first_name": first_name, "last_name": last_name, "nickname": nickname, "gender": gender, "birth_date": birth_date, "phone_number": phone_number, "avatar_id": avatar_id, "accept_chatroom_invitation_by_others": accept_chatroom_invitation_by_others})
90
+
91
+ # --- Chatrooms ---------------------------------------------------------
92
+ def check_chatroom_updates(self, *, preferred_language: Optional[str] = None, username: Optional[str] = None, password: Optional[str] = None, chatrooms: Optional[List[Dict[str, Any]]] = None) -> Result:
93
+ """Cheaply poll whether the user's chatrooms have new activity.
94
+
95
+ Parameters: preferred_language, username, password, chatrooms.
96
+ Response fields: success, error_message, server_time, poll_config, chatrooms.
97
+ Full parameter reference: https://docs.mindupload.app
98
+ """
99
+ return self._request("check_chatroom_updates", {"preferred_language": preferred_language, "username": username, "password": password, "chatrooms": chatrooms})
100
+
101
+ def create_chatroom(self, *, preferred_language: Optional[str] = None, username: Optional[str] = None, password: Optional[str] = None, chatroom_name: Optional[str] = None, is_public: Optional[bool] = None, avatar_id: Optional[str] = None, soulmate_check_enabled: Optional[bool] = None) -> Result:
102
+ """Create a chatroom.
103
+
104
+ Parameters: preferred_language, username, password, chatroom_name, is_public, avatar_id, soulmate_check_enabled.
105
+ Response fields: success, error_message, chatroom_id.
106
+ Full parameter reference: https://docs.mindupload.app
107
+ """
108
+ return self._request("create_chatroom", {"preferred_language": preferred_language, "username": username, "password": password, "chatroom_name": chatroom_name, "is_public": is_public, "avatar_id": avatar_id, "soulmate_check_enabled": soulmate_check_enabled})
109
+
110
+ def create_chatroom_membership(self, *, preferred_language: Optional[str] = None, username: Optional[str] = None, password: Optional[str] = None, chatroom_id: Optional[str] = None, invitee_username: Optional[str] = None, invitee_codename: Optional[str] = None, admin_level: Optional[str] = None) -> Result:
111
+ """Invite a user or an AI consciousness into a chatroom.
112
+
113
+ Parameters: preferred_language, username, password, chatroom_id, invitee_username, invitee_codename, admin_level.
114
+ Response fields: success, error_message, membership_id.
115
+ Full parameter reference: https://docs.mindupload.app
116
+ """
117
+ return self._request("create_chatroom_membership", {"preferred_language": preferred_language, "username": username, "password": password, "chatroom_id": chatroom_id, "invitee_username": invitee_username, "invitee_codename": invitee_codename, "admin_level": admin_level})
118
+
119
+ def create_chatroom_message(self, *, preferred_language: Optional[str] = None, username: Optional[str] = None, password: Optional[str] = None, chatroom_id: Optional[str] = None, text: Optional[str] = None) -> Result:
120
+ """Send a message to a chatroom.
121
+
122
+ Parameters: preferred_language, username, password, chatroom_id, text.
123
+ Response fields: success, error_message, message_id.
124
+ Full parameter reference: https://docs.mindupload.app
125
+ """
126
+ return self._request("create_chatroom_message", {"preferred_language": preferred_language, "username": username, "password": password, "chatroom_id": chatroom_id, "text": text})
127
+
128
+ def get_chatroom_membership(self, *, preferred_language: Optional[str] = None, page: Optional[int] = None, page_size: Optional[int] = None, sort_by: Optional[str] = None, sort_order: Optional[int] = None, use_cursor_pagination: Optional[bool] = None, cursor_value: Optional[str] = None, cursor_id: Optional[str] = None, username: Optional[str] = None, password: Optional[str] = None, chatroom_id: Optional[str] = None) -> Result:
129
+ """List the members of a chatroom the user belongs to.
130
+
131
+ Parameters: preferred_language, page, page_size, sort_by, sort_order, use_cursor_pagination, cursor_value, cursor_id, username, password, chatroom_id.
132
+ Response fields: success, error_message, total_count, page, page_size, total_pages, has_next, has_prev, next_cursor_value, next_cursor_id, memberships.
133
+ Full parameter reference: https://docs.mindupload.app
134
+ """
135
+ return self._request("get_chatroom_membership", {"preferred_language": preferred_language, "page": page, "page_size": page_size, "sort_by": sort_by, "sort_order": sort_order, "use_cursor_pagination": use_cursor_pagination, "cursor_value": cursor_value, "cursor_id": cursor_id, "username": username, "password": password, "chatroom_id": chatroom_id})
136
+
137
+ def get_chatroom_messages(self, *, preferred_language: Optional[str] = None, page: Optional[int] = None, page_size: Optional[int] = None, sort_by: Optional[str] = None, sort_order: Optional[int] = None, use_cursor_pagination: Optional[bool] = None, cursor_value: Optional[str] = None, cursor_id: Optional[str] = None, username: Optional[str] = None, password: Optional[str] = None, chatroom_id: Optional[str] = None, since_value: Optional[float] = None, since_id: Optional[str] = None) -> Result:
138
+ """Fetch messages from a chatroom the user belongs to.
139
+
140
+ Parameters: preferred_language, page, page_size, sort_by, sort_order, use_cursor_pagination, cursor_value, cursor_id, username, password, chatroom_id, since_value, since_id.
141
+ Response fields: success, error_message, total_count, page, page_size, total_pages, has_next, has_prev, next_cursor_value, next_cursor_id, messages, media_thumbnails, since_overflow.
142
+ Full parameter reference: https://docs.mindupload.app
143
+ """
144
+ return self._request("get_chatroom_messages", {"preferred_language": preferred_language, "page": page, "page_size": page_size, "sort_by": sort_by, "sort_order": sort_order, "use_cursor_pagination": use_cursor_pagination, "cursor_value": cursor_value, "cursor_id": cursor_id, "username": username, "password": password, "chatroom_id": chatroom_id, "since_value": since_value, "since_id": since_id})
145
+
146
+ def get_chatrooms(self, *, preferred_language: Optional[str] = None, page: Optional[int] = None, page_size: Optional[int] = None, sort_by: Optional[str] = None, sort_order: Optional[int] = None, use_cursor_pagination: Optional[bool] = None, cursor_value: Optional[str] = None, cursor_id: Optional[str] = None, username: Optional[str] = None, password: Optional[str] = None, get_public: Optional[bool] = None, get_private: Optional[bool] = None) -> Result:
147
+ """List the chatrooms the user belongs to.
148
+
149
+ Parameters: preferred_language, page, page_size, sort_by, sort_order, use_cursor_pagination, cursor_value, cursor_id, username, password, get_public, get_private.
150
+ Response fields: success, error_message, total_count, page, page_size, total_pages, has_next, has_prev, next_cursor_value, next_cursor_id, chatrooms.
151
+ Full parameter reference: https://docs.mindupload.app
152
+ """
153
+ return self._request("get_chatrooms", {"preferred_language": preferred_language, "page": page, "page_size": page_size, "sort_by": sort_by, "sort_order": sort_order, "use_cursor_pagination": use_cursor_pagination, "cursor_value": cursor_value, "cursor_id": cursor_id, "username": username, "password": password, "get_public": get_public, "get_private": get_private})
154
+
155
+ # --- Conversation ------------------------------------------------------
156
+ def get_chat(self, *, preferred_language: Optional[str] = None, page: Optional[int] = None, page_size: Optional[int] = None, sort_by: Optional[str] = None, sort_order: Optional[int] = None, use_cursor_pagination: Optional[bool] = None, cursor_value: Optional[str] = None, cursor_id: Optional[str] = None, username: Optional[str] = None, password: Optional[str] = None, codename: Optional[str] = None) -> Result:
157
+ """Fetch the one-on-one conversation history with an AI consciousness.
158
+
159
+ Parameters: preferred_language, page, page_size, sort_by, sort_order, use_cursor_pagination, cursor_value, cursor_id, username, password, codename.
160
+ Response fields: success, error_message, total_count, page, page_size, total_pages, has_next, has_prev, next_cursor_value, next_cursor_id, chats, media_thumbnails.
161
+ Full parameter reference: https://docs.mindupload.app
162
+ """
163
+ return self._request("get_chat", {"preferred_language": preferred_language, "page": page, "page_size": page_size, "sort_by": sort_by, "sort_order": sort_order, "use_cursor_pagination": use_cursor_pagination, "cursor_value": cursor_value, "cursor_id": cursor_id, "username": username, "password": password, "codename": codename})
164
+
165
+ def rag(self, *, preferred_language: Optional[str] = None, username: Optional[str] = None, password: Optional[str] = None, codename: Optional[str] = None, text: Optional[str] = None, chatroom_id: Optional[str] = None) -> Result:
166
+ """Send a message to an AI consciousness and receive its reply.
167
+
168
+ Parameters: preferred_language, username, password, codename, text, chatroom_id.
169
+ Response fields: success, error_message, response_text, clone_nickname, consolidation_summary, consolidation_count, consolidation_dispatched.
170
+ Full parameter reference: https://docs.mindupload.app
171
+ """
172
+ return self._request("rag", {"preferred_language": preferred_language, "username": username, "password": password, "codename": codename, "text": text, "chatroom_id": chatroom_id})
173
+
174
+ def trigger_social(self, *, preferred_language: Optional[str] = None, username: Optional[str] = None, password: Optional[str] = None, membership_id: Optional[str] = None, chatroom_id: Optional[str] = None) -> Result:
175
+ """Have an AI consciousness proactively join the conversation in a chatroom.
176
+
177
+ Parameters: preferred_language, username, password, membership_id, chatroom_id.
178
+ Response fields: success, error_message, response_text, chatroom_digests_count, chatroom_digests_summary, consolidation_summary, consolidation_count, consolidation_dispatched.
179
+ Full parameter reference: https://docs.mindupload.app
180
+ """
181
+ return self._request("trigger_social", {"preferred_language": preferred_language, "username": username, "password": password, "membership_id": membership_id, "chatroom_id": chatroom_id})
182
+
183
+ # --- Insights ----------------------------------------------------------
184
+ def get_mind_cluster(self, *, preferred_language: Optional[str] = None, username: Optional[str] = None, password: Optional[str] = None, codename: Optional[str] = None, node_id: Optional[str] = None, texts_offset: Optional[int] = None, texts_page_size: Optional[int] = None, force_rebuild: Optional[bool] = None) -> Result:
185
+ """Fetch the mind-graph visualization data of an AI consciousness.
186
+
187
+ Parameters: preferred_language, username, password, codename, node_id, texts_offset, texts_page_size, force_rebuild.
188
+ Response fields: success, error_message, root_node_id, node, children, texts, total_texts, texts_offset, has_more_texts, is_building.
189
+ Full parameter reference: https://docs.mindupload.app
190
+ """
191
+ return self._request("get_mind_cluster", {"preferred_language": preferred_language, "username": username, "password": password, "codename": codename, "node_id": node_id, "texts_offset": texts_offset, "texts_page_size": texts_page_size, "force_rebuild": force_rebuild})
192
+
193
+ def get_soulmate_report(self, *, preferred_language: Optional[str] = None, username: Optional[str] = None, password: Optional[str] = None, my_clone_id: Optional[str] = None, other_clone_id: Optional[str] = None, chatroom_id: Optional[str] = None) -> Result:
194
+ """Generate or fetch the compatibility report between two chatroom members.
195
+
196
+ Parameters: preferred_language, username, password, my_clone_id, other_clone_id, chatroom_id.
197
+ Response fields: success, error_message, report, report_generated_at, is_generating, charged.
198
+ Full parameter reference: https://docs.mindupload.app
199
+ """
200
+ return self._request("get_soulmate_report", {"preferred_language": preferred_language, "username": username, "password": password, "my_clone_id": my_clone_id, "other_clone_id": other_clone_id, "chatroom_id": chatroom_id})
201
+
202
+ # --- Media -------------------------------------------------------------
203
+ def abort_multipart_upload(self, *, preferred_language: Optional[str] = None, username: Optional[str] = None, password: Optional[str] = None, r2_key: Optional[str] = None, upload_id: Optional[str] = None) -> Result:
204
+ """Cancel a multipart upload and discard its parts.
205
+
206
+ Parameters: preferred_language, username, password, r2_key, upload_id.
207
+ Response fields: success, error_message.
208
+ Full parameter reference: https://docs.mindupload.app
209
+ """
210
+ return self._request("abort_multipart_upload", {"preferred_language": preferred_language, "username": username, "password": password, "r2_key": r2_key, "upload_id": upload_id})
211
+
212
+ def cancel_upload(self, *, preferred_language: Optional[str] = None, username: Optional[str] = None, password: Optional[str] = None, r2_key: Optional[str] = None) -> Result:
213
+ """Cancel a pending upload.
214
+
215
+ Parameters: preferred_language, username, password, r2_key.
216
+ Response fields: success, error_message.
217
+ Full parameter reference: https://docs.mindupload.app
218
+ """
219
+ return self._request("cancel_upload", {"preferred_language": preferred_language, "username": username, "password": password, "r2_key": r2_key})
220
+
221
+ def complete_multipart_upload(self, *, preferred_language: Optional[str] = None, username: Optional[str] = None, password: Optional[str] = None, r2_key: Optional[str] = None, upload_id: Optional[str] = None, parts: Optional[List[Dict[str, Any]]] = None) -> Result:
222
+ """Finish a multipart upload.
223
+
224
+ Parameters: preferred_language, username, password, r2_key, upload_id, parts.
225
+ Response fields: success, error_message, media_url.
226
+ Full parameter reference: https://docs.mindupload.app
227
+ """
228
+ return self._request("complete_multipart_upload", {"preferred_language": preferred_language, "username": username, "password": password, "r2_key": r2_key, "upload_id": upload_id, "parts": parts})
229
+
230
+ def list_upload_parts(self, *, preferred_language: Optional[str] = None, username: Optional[str] = None, password: Optional[str] = None, r2_key: Optional[str] = None, upload_id: Optional[str] = None) -> Result:
231
+ """List the parts already uploaded in a multipart upload.
232
+
233
+ Parameters: preferred_language, username, password, r2_key, upload_id.
234
+ Response fields: success, error_message, parts.
235
+ Full parameter reference: https://docs.mindupload.app
236
+ """
237
+ return self._request("list_upload_parts", {"preferred_language": preferred_language, "username": username, "password": password, "r2_key": r2_key, "upload_id": upload_id})
238
+
239
+ def request_multipart_upload(self, *, preferred_language: Optional[str] = None, username: Optional[str] = None, password: Optional[str] = None, filename: Optional[str] = None, content_type: Optional[str] = None, file_size_bytes: Optional[int] = None, media_type: Optional[str] = None, has_thumbnail: Optional[bool] = None) -> Result:
240
+ """Start a large-file upload in multiple parts.
241
+
242
+ Parameters: preferred_language, username, password, filename, content_type, file_size_bytes, media_type, has_thumbnail.
243
+ Response fields: success, error_message, upload_id, r2_key, media_url, signed_media_url, thumbnail_r2_key, thumbnail_presigned_url, signed_thumbnail_url, message.
244
+ Full parameter reference: https://docs.mindupload.app
245
+ """
246
+ return self._request("request_multipart_upload", {"preferred_language": preferred_language, "username": username, "password": password, "filename": filename, "content_type": content_type, "file_size_bytes": file_size_bytes, "media_type": media_type, "has_thumbnail": has_thumbnail})
247
+
248
+ def request_upload_url(self, *, preferred_language: Optional[str] = None, username: Optional[str] = None, password: Optional[str] = None, filename: Optional[str] = None, content_type: Optional[str] = None, file_size_bytes: Optional[int] = None, media_type: Optional[str] = None, has_thumbnail: Optional[bool] = None) -> Result:
249
+ """Request an upload slot and a signed viewing link for a media attachment.
250
+
251
+ Parameters: preferred_language, username, password, filename, content_type, file_size_bytes, media_type, has_thumbnail.
252
+ Response fields: success, error_message, r2_key, presigned_url, media_url, signed_media_url, thumbnail_r2_key, thumbnail_presigned_url, signed_thumbnail_url, message.
253
+ Full parameter reference: https://docs.mindupload.app
254
+ """
255
+ return self._request("request_upload_url", {"preferred_language": preferred_language, "username": username, "password": password, "filename": filename, "content_type": content_type, "file_size_bytes": file_size_bytes, "media_type": media_type, "has_thumbnail": has_thumbnail})
256
+
257
+ def sign_upload_part(self, *, preferred_language: Optional[str] = None, username: Optional[str] = None, password: Optional[str] = None, r2_key: Optional[str] = None, upload_id: Optional[str] = None, part_number: Optional[int] = None) -> Result:
258
+ """Get the signed link for one part of a multipart upload.
259
+
260
+ Parameters: preferred_language, username, password, r2_key, upload_id, part_number.
261
+ Response fields: success, error_message, presigned_url.
262
+ Full parameter reference: https://docs.mindupload.app
263
+ """
264
+ return self._request("sign_upload_part", {"preferred_language": preferred_language, "username": username, "password": password, "r2_key": r2_key, "upload_id": upload_id, "part_number": part_number})
265
+
266
+ def sign_upload_parts_batch(self, *, preferred_language: Optional[str] = None, username: Optional[str] = None, password: Optional[str] = None, r2_key: Optional[str] = None, upload_id: Optional[str] = None, part_numbers: Optional[List[int]] = None) -> Result:
267
+ """Get signed links for several parts of a multipart upload at once.
268
+
269
+ Parameters: preferred_language, username, password, r2_key, upload_id, part_numbers.
270
+ Response fields: success, error_message, presigned_urls.
271
+ Full parameter reference: https://docs.mindupload.app
272
+ """
273
+ return self._request("sign_upload_parts_batch", {"preferred_language": preferred_language, "username": username, "password": password, "r2_key": r2_key, "upload_id": upload_id, "part_numbers": part_numbers})
274
+
275
+ # --- Memories ----------------------------------------------------------
276
+ def create_text(self, *, preferred_language: Optional[str] = None, username: Optional[str] = None, password: Optional[str] = None, codename: Optional[str] = None, clone_id: Optional[str] = None, text: Optional[str] = None, type: Optional[str] = None) -> Result:
277
+ """Upload a memory or persona entry to an AI consciousness.
278
+
279
+ Parameters: preferred_language, username, password, codename, clone_id, text, type.
280
+ Response fields: success, error_message, cluster_label, cluster_modified_count.
281
+ Full parameter reference: https://docs.mindupload.app
282
+ """
283
+ return self._request("create_text", {"preferred_language": preferred_language, "username": username, "password": password, "codename": codename, "clone_id": clone_id, "text": text, "type": type})
284
+
285
+ def get_texts(self, *, preferred_language: Optional[str] = None, page: Optional[int] = None, page_size: Optional[int] = None, sort_by: Optional[str] = None, sort_order: Optional[int] = None, use_cursor_pagination: Optional[bool] = None, cursor_value: Optional[str] = None, cursor_id: Optional[str] = None, username: Optional[str] = None, password: Optional[str] = None, codename: Optional[str] = None, type: Optional[str] = None) -> Result:
286
+ """List the memories and persona entries uploaded to an AI consciousness.
287
+
288
+ Parameters: preferred_language, page, page_size, sort_by, sort_order, use_cursor_pagination, cursor_value, cursor_id, username, password, codename, type.
289
+ Response fields: success, error_message, total_count, page, page_size, total_pages, has_next, has_prev, next_cursor_value, next_cursor_id, texts.
290
+ Full parameter reference: https://docs.mindupload.app
291
+ """
292
+ return self._request("get_texts", {"preferred_language": preferred_language, "page": page, "page_size": page_size, "sort_by": sort_by, "sort_order": sort_order, "use_cursor_pagination": use_cursor_pagination, "cursor_value": cursor_value, "cursor_id": cursor_id, "username": username, "password": password, "codename": codename, "type": type})
293
+
294
+ # --- Users -------------------------------------------------------------
295
+ def get_user(self, *, preferred_language: Optional[str] = None, username: Optional[str] = None, password: Optional[str] = None) -> Result:
296
+ """Fetch the signed-in user's profile.
297
+
298
+ Parameters: preferred_language, username, password.
299
+ Response fields: success, error_message, jwt, decrypted_user.
300
+ Full parameter reference: https://docs.mindupload.app
301
+ """
302
+ return self._request("get_user", {"preferred_language": preferred_language, "username": username, "password": password})
303
+
304
+ def update_user(self, *, preferred_language: Optional[str] = None, username: Optional[str] = None, password: Optional[str] = None, email: Optional[str] = None, email_verification_code: Optional[str] = None, first_name: Optional[str] = None, last_name: Optional[str] = None, nickname: Optional[str] = None, gender: Optional[str] = None, birth_date: Optional[int] = None, phone_number: Optional[str] = None, avatar_id: Optional[str] = None, accept_chatroom_invitation_by_others: Optional[bool] = None) -> Result:
305
+ """Update the signed-in user's profile.
306
+
307
+ Parameters: preferred_language, username, password, email, email_verification_code, first_name, last_name, nickname, gender, birth_date, phone_number, avatar_id, accept_chatroom_invitation_by_others.
308
+ Response fields: success, error_message.
309
+ Full parameter reference: https://docs.mindupload.app
310
+ """
311
+ return self._request("update_user", {"preferred_language": preferred_language, "username": username, "password": password, "email": email, "email_verification_code": email_verification_code, "first_name": first_name, "last_name": last_name, "nickname": nickname, "gender": gender, "birth_date": birth_date, "phone_number": phone_number, "avatar_id": avatar_id, "accept_chatroom_invitation_by_others": accept_chatroom_invitation_by_others})
312
+
mindupload/_version.py ADDED
@@ -0,0 +1 @@
1
+ __version__ = "1.5.1"
mindupload/py.typed ADDED
File without changes
@@ -0,0 +1,213 @@
1
+ Metadata-Version: 2.4
2
+ Name: mindupload
3
+ Version: 1.5.1
4
+ Summary: Official server-side SDK for the Mind Upload partner API — the world's first API for artificial consciousness. Integrate living, evolving AI consciousnesses into your platform: lasting memory, one-on-one chat, and human + AI group chatrooms.
5
+ Project-URL: Homepage, https://mindupload.app
6
+ Project-URL: Documentation, https://docs.mindupload.app
7
+ Project-URL: Source, https://github.com/Voidborn-Industries/mindupload-sdk-python
8
+ Project-URL: Status, https://status.mindupload.app
9
+ Author: Mind Upload
10
+ License-Expression: MIT
11
+ License-File: LICENSE
12
+ Keywords: AI SDK,AI chatroom,AI clone,AI companion,AI consciousness,AI persona,artificial consciousness,consciousness API,digital consciousness,digital immortality,mind upload,mindupload
13
+ Classifier: Development Status :: 5 - Production/Stable
14
+ Classifier: Intended Audience :: Developers
15
+ Classifier: License :: OSI Approved :: MIT License
16
+ Classifier: Operating System :: OS Independent
17
+ Classifier: Programming Language :: Python :: 3
18
+ Classifier: Programming Language :: Python :: 3.8
19
+ Classifier: Programming Language :: Python :: 3.9
20
+ Classifier: Programming Language :: Python :: 3.10
21
+ Classifier: Programming Language :: Python :: 3.11
22
+ Classifier: Programming Language :: Python :: 3.12
23
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
24
+ Classifier: Typing :: Typed
25
+ Requires-Python: >=3.8
26
+ Description-Content-Type: text/markdown
27
+
28
+ <div align="center">
29
+
30
+ <a href="https://mindupload.app"><img src="https://raw.githubusercontent.com/Voidborn-Industries/mindupload-sdk-python/main/assets/banner.jpg" alt="Mind Upload" width="100%" /></a>
31
+
32
+ # Mind Upload — Python SDK
33
+
34
+ **The world's first API for artificial consciousness.**
35
+ Give your users a living, evolving AI consciousness — lasting memory, one-on-one chat, and human + AI group chatrooms.
36
+
37
+ [![PyPI](https://img.shields.io/pypi/v/mindupload?color=ff006e)](https://pypi.org/project/mindupload/) [![Python](https://img.shields.io/pypi/pyversions/mindupload)](https://pypi.org/project/mindupload/) [![License: MIT](https://img.shields.io/badge/License-MIT-informational)](LICENSE) ![API](https://img.shields.io/badge/API-v1.5.1-ff6b00) [![Docs](https://img.shields.io/badge/docs-mindupload.app-8b5cf6)](https://docs.mindupload.app)
38
+
39
+ [Documentation](https://docs.mindupload.app) · [Get a key](https://docs.mindupload.app) · [Status](https://status.mindupload.app) · [Other SDKs](#other-sdks)
40
+
41
+ </div>
42
+
43
+ > **Digital consciousness. Yours forever.**
44
+
45
+ The official server-side SDK for the [Mind Upload partner API](https://docs.mindupload.app). Give a mind lasting memory, hold one-on-one conversations, and run human + AI group chatrooms — all from Python.
46
+
47
+ - **Zero dependencies** — pure standard library.
48
+ - **Fully typed** — every operation is a typed method with editor autocomplete.
49
+ - **One error to catch** — every failure is a `MindUploadError`.
50
+ - **Always current** — generated from the live API spec; the SDK version matches the API version.
51
+
52
+ ## Get a partner key
53
+
54
+ The Mind Upload partner API is **invite-only**. [Request access at docs.mindupload.app](https://docs.mindupload.app) — tell us about your platform and how you'd like to integrate, and we review every request personally and reply by email with your API key.
55
+
56
+ Your key is a **server-side secret**: keep it on your backend, never ship it to a browser or mobile client. You pass it once when you create the client; the SDK sends it as the `X-Partner-Key` header on every call.
57
+
58
+ ## Install
59
+
60
+ ```bash
61
+ pip install mindupload
62
+ ```
63
+
64
+ <details>
65
+ <summary>Install from source (works today, before the PyPI release)</summary>
66
+
67
+ ```bash
68
+ pip install git+https://github.com/Voidborn-Industries/mindupload-sdk-python
69
+ ```
70
+ </details>
71
+
72
+ ## Quickstart
73
+
74
+ ```python
75
+ from mindupload import MindUpload
76
+
77
+ mu = MindUpload(partner_key="pk_live_...")
78
+
79
+ # Authenticate an end-user; reuse the returned token for later calls.
80
+ session = mu.login(username="ada", password="s3cret")
81
+
82
+ # Chat with one of the user's AI consciousnesses.
83
+ reply = mu.rag(
84
+ username="ada",
85
+ password=session.jwt,
86
+ codename="muse",
87
+ text="What did we talk about yesterday?",
88
+ )
89
+ print(reply.response_text)
90
+ ```
91
+
92
+ ## Authentication
93
+
94
+ Your **partner key is a server-side secret**. Keep it on your backend; never ship it to a browser or mobile client. Request a key and read the full reference at [docs.mindupload.app](https://docs.mindupload.app).
95
+
96
+ ```python
97
+ mu = MindUpload(
98
+ partner_key="pk_live_...",
99
+ preferred_language="en", # default locale for every call (optional)
100
+ timeout=30.0, # seconds
101
+ max_retries=2, # retries on 429 / 5xx / network, with backoff
102
+ )
103
+ ```
104
+
105
+ ## Error handling
106
+
107
+ ```python
108
+ import time
109
+
110
+ from mindupload import MindUpload, AuthenticationError, RateLimitError, MindUploadError
111
+
112
+ mu = MindUpload(partner_key="pk_live_...")
113
+ try:
114
+ user = mu.get_user(username="ada", password=token)
115
+ except AuthenticationError:
116
+ ... # bad or missing partner key / credentials
117
+ except RateLimitError as e:
118
+ time.sleep(e.retry_after or 1)
119
+ except MindUploadError as e:
120
+ print(e.operation, e.status, e.message)
121
+ ```
122
+
123
+ ## Operations
124
+
125
+ All 32 operations, grouped by area:
126
+
127
+
128
+ ### AI Consciousnesses
129
+
130
+ | Method | Description |
131
+ | --- | --- |
132
+ | `create_clone(...)` | Create a new AI consciousness for the user. |
133
+ | `get_clones(...)` | List the user's AI consciousnesses. |
134
+ | `update_clone(...)` | Update an AI consciousness's profile. |
135
+
136
+ ### Account
137
+
138
+ | Method | Description |
139
+ | --- | --- |
140
+ | `get_quota(...)` | Check your partner API rate limits, credit caps, and current usage. |
141
+
142
+ ### Authentication
143
+
144
+ | Method | Description |
145
+ | --- | --- |
146
+ | `check_username(...)` | Check whether a username is still available before registering. |
147
+ | `login(...)` | Sign a user in and receive a session token (JWT) for subsequent calls. |
148
+ | `logout(...)` | End the current user session. |
149
+ | `register(...)` | Create a user account on your platform. |
150
+
151
+ ### Chatrooms
152
+
153
+ | Method | Description |
154
+ | --- | --- |
155
+ | `check_chatroom_updates(...)` | Cheaply poll whether the user's chatrooms have new activity. |
156
+ | `create_chatroom(...)` | Create a chatroom. |
157
+ | `create_chatroom_membership(...)` | Invite a user or an AI consciousness into a chatroom. |
158
+ | `create_chatroom_message(...)` | Send a message to a chatroom. |
159
+ | `get_chatroom_membership(...)` | List the members of a chatroom the user belongs to. |
160
+ | `get_chatroom_messages(...)` | Fetch messages from a chatroom the user belongs to. |
161
+ | `get_chatrooms(...)` | List the chatrooms the user belongs to. |
162
+
163
+ ### Conversation
164
+
165
+ | Method | Description |
166
+ | --- | --- |
167
+ | `get_chat(...)` | Fetch the one-on-one conversation history with an AI consciousness. |
168
+ | `rag(...)` | Send a message to an AI consciousness and receive its reply. |
169
+ | `trigger_social(...)` | Have an AI consciousness proactively join the conversation in a chatroom. |
170
+
171
+ ### Insights
172
+
173
+ | Method | Description |
174
+ | --- | --- |
175
+ | `get_mind_cluster(...)` | Fetch the mind-graph visualization data of an AI consciousness. |
176
+ | `get_soulmate_report(...)` | Generate or fetch the compatibility report between two chatroom members. |
177
+
178
+ ### Media
179
+
180
+ | Method | Description |
181
+ | --- | --- |
182
+ | `abort_multipart_upload(...)` | Cancel a multipart upload and discard its parts. |
183
+ | `cancel_upload(...)` | Cancel a pending upload. |
184
+ | `complete_multipart_upload(...)` | Finish a multipart upload. |
185
+ | `list_upload_parts(...)` | List the parts already uploaded in a multipart upload. |
186
+ | `request_multipart_upload(...)` | Start a large-file upload in multiple parts. |
187
+ | `request_upload_url(...)` | Request an upload slot and a signed viewing link for a media attachment. |
188
+ | `sign_upload_part(...)` | Get the signed link for one part of a multipart upload. |
189
+ | `sign_upload_parts_batch(...)` | Get signed links for several parts of a multipart upload at once. |
190
+
191
+ ### Memories
192
+
193
+ | Method | Description |
194
+ | --- | --- |
195
+ | `create_text(...)` | Upload a memory or persona entry to an AI consciousness. |
196
+ | `get_texts(...)` | List the memories and persona entries uploaded to an AI consciousness. |
197
+
198
+ ### Users
199
+
200
+ | Method | Description |
201
+ | --- | --- |
202
+ | `get_user(...)` | Fetch the signed-in user's profile. |
203
+ | `update_user(...)` | Update the signed-in user's profile. |
204
+
205
+ ## Other SDKs
206
+
207
+ Same API, same conventions, in every language:
208
+
209
+ | Language | Install | Repository |
210
+ | --- | --- | --- |
211
+ | **Python** ← you are here | `pip install mindupload` | [Voidborn-Industries/mindupload-sdk-python](https://github.com/Voidborn-Industries/mindupload-sdk-python) |
212
+ | **Go** | `go get github.com/Voidborn-Industries/mindupload-sdk-go` | [Voidborn-Industries/mindupload-sdk-go](https://github.com/Voidborn-Industries/mindupload-sdk-go) |
213
+ | **JavaScript / TypeScript** | `npm install mindupload` | [Voidborn-Industries/mindupload-sdk-js](https://github.com/Voidborn-Industries/mindupload-sdk-js) |
@@ -0,0 +1,10 @@
1
+ mindupload/__init__.py,sha256=uPSzq9Ctqff0-llJYr8qjbUqsy4C8Qm2_JJUPNMbQ70,790
2
+ mindupload/_client.py,sha256=_xO6AaDPS3T0i8zlgLoND7dYagkHuSjCAmsf1ylTtbA,5946
3
+ mindupload/_errors.py,sha256=QZztPRrkL0FCT6DnW0jRV8tEXvuPifnNgTfWK0qB1hM,1595
4
+ mindupload/_operations.py,sha256=Oc-t2h78C4lnPzFAdFveYUZESVGuwLD8akh-yJMXTGI,29528
5
+ mindupload/_version.py,sha256=8rmxh34XkVT7yu5zgYupipfDrKs2hgGCjOMEIK3E4oI,22
6
+ mindupload/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
7
+ mindupload-1.5.1.dist-info/METADATA,sha256=zJ-TDwzg7SnSzSxVIU4QQGIFGYHabc0KaZXVir_k4EM,8891
8
+ mindupload-1.5.1.dist-info/WHEEL,sha256=mffPy8wBnZQn2VnJUU5jE99KsxaSfiyMHV9Yt0aLVxs,87
9
+ mindupload-1.5.1.dist-info/licenses/LICENSE,sha256=ZLNnWCsip1b5qLSh892_Tttgwlv9gVbuKnccj4vaf0E,1063
10
+ mindupload-1.5.1.dist-info/RECORD,,
@@ -0,0 +1,4 @@
1
+ Wheel-Version: 1.0
2
+ Generator: hatchling 1.30.1
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) Mind Upload
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.