superartifacts 0.1.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.
@@ -0,0 +1,6 @@
1
+ .venv/
2
+ dist/
3
+ uv.lock
4
+ __pycache__/
5
+ .pytest_cache/
6
+ .ruff_cache/
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Super Artifacts
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.
@@ -0,0 +1,241 @@
1
+ Metadata-Version: 2.5
2
+ Name: superartifacts
3
+ Version: 0.1.0
4
+ Summary: Python SDK for the Super Artifacts REST API: publish artifacts and read their data from your own code.
5
+ Project-URL: Homepage, https://superartifacts.app/docs/sdks/python
6
+ Project-URL: Repository, https://github.com/Superbelt/sdk
7
+ Project-URL: Issues, https://github.com/Superbelt/sdk/issues
8
+ Author: Super Artifacts
9
+ License-Expression: MIT
10
+ License-File: LICENSE
11
+ Keywords: api,artifacts,publishing,sdk,super-artifacts
12
+ Classifier: Development Status :: 4 - Beta
13
+ Classifier: Intended Audience :: Developers
14
+ Classifier: License :: OSI Approved :: MIT License
15
+ Classifier: Programming Language :: Python :: 3
16
+ Classifier: Programming Language :: Python :: 3.10
17
+ Classifier: Programming Language :: Python :: 3.11
18
+ Classifier: Programming Language :: Python :: 3.12
19
+ Classifier: Programming Language :: Python :: 3.13
20
+ Classifier: Typing :: Typed
21
+ Requires-Python: >=3.10
22
+ Requires-Dist: httpx<1,>=0.27
23
+ Requires-Dist: pydantic<3,>=2
24
+ Description-Content-Type: text/markdown
25
+
26
+ # Super Artifacts for Python
27
+
28
+ Needs a Solo plan or higher. MCP and skills are unaffected.
29
+
30
+ The official Python SDK for the [Super Artifacts](https://superartifacts.app) REST API: publish
31
+ artifacts, read what visitors submitted, manage projects and templates, from your own code.
32
+
33
+ Full docs: <https://superartifacts.app/docs/sdks/python>
34
+
35
+ ## Install
36
+
37
+ ```bash
38
+ pip install superartifacts
39
+ ```
40
+
41
+ Python 3.10 or newer. Create a key in
42
+ [Settings → API keys](https://superartifacts.app/settings/api-keys).
43
+
44
+ ## Quickstart
45
+
46
+ ```python
47
+ import os
48
+
49
+ from superartifacts import SuperArtifacts
50
+
51
+ sa = SuperArtifacts(api_key=os.environ["SUPERARTIFACTS_API_KEY"])
52
+
53
+ print(sa.account.whoami()["handle"])
54
+
55
+ sa.artifacts.publish(
56
+ slug="hello",
57
+ html="<!doctype html><h1>Hello</h1>",
58
+ )
59
+
60
+ for artifact in sa.artifacts.list()["artifacts"]:
61
+ print(artifact["slug"])
62
+
63
+ for row in sa.data.iter_artifact_rows(artifact="hello"):
64
+ print(row)
65
+ ```
66
+
67
+ `api_key` falls back to `SUPERARTIFACTS_API_KEY`, so `SuperArtifacts()` is enough.
68
+
69
+ Keyword arguments hold path, query and body fields together, in snake_case: the SDK splits them
70
+ as the API expects, so `values_file=` goes out as `valuesFile`. Values nested inside a dict keep
71
+ the API's spelling. Answers are plain dicts, as the
72
+ [API reference](https://superartifacts.app/docs/reference/api-endpoints) describes them.
73
+
74
+ **Server-side only.** The API refuses any request carrying an `Origin` header, which every browser
75
+ adds. Keep the key on a server.
76
+
77
+ ## Async
78
+
79
+ `AsyncSuperArtifacts` is the same surface, awaited:
80
+
81
+ ```python
82
+ import asyncio
83
+
84
+ from superartifacts import AsyncSuperArtifacts
85
+
86
+
87
+ async def main():
88
+ async with AsyncSuperArtifacts() as sa:
89
+ print(await sa.account.whoami())
90
+ async for row in sa.data.iter_artifact_rows(artifact="hello"):
91
+ print(row)
92
+
93
+
94
+ asyncio.run(main())
95
+ ```
96
+
97
+ ## Configuration
98
+
99
+ ```python
100
+ SuperArtifacts(
101
+ api_key=None, # or SUPERARTIFACTS_API_KEY
102
+ base_url="https://superartifacts.app",
103
+ timeout=60.0, # seconds
104
+ max_retries=2,
105
+ http_client=None, # your own httpx.Client; yours to close
106
+ )
107
+ ```
108
+
109
+ Every method also takes `timeout=` for that call, and every `POST` takes `idempotency_key=`.
110
+ Use the client as a context manager, or call `close()`.
111
+
112
+ ## Errors
113
+
114
+ Everything raised is a `SuperArtifactsError`, with `.status`, `.code`, `.message`, `.retry_after`
115
+ and the full error object in `.details`.
116
+ A client-side failure has `.status` 0 and its own `.code`: a missing key is an `AuthenticationError`
117
+ with `missing_api_key`.
118
+
119
+ | Status | Exception |
120
+ | --- | --- |
121
+ | 400 | `InvalidArgumentsError` |
122
+ | 401 | `AuthenticationError` (also raised when there is no key) |
123
+ | 402 | `PlanLimitError` |
124
+ | 403 | `PermissionError`, or `ScopeRequiredError` when a read key tried a write |
125
+ | 404 | `NotFoundError` |
126
+ | 409 | `ConflictError` |
127
+ | 413 | `PayloadTooLargeError` |
128
+ | 429 | `RateLimitError` |
129
+ | 5xx | `InternalError` |
130
+ | none | `APIConnectionError`, `APITimeoutError` |
131
+
132
+ ```python
133
+ from superartifacts import NotFoundError
134
+
135
+ try:
136
+ sa.artifacts.get(artifact="missing")
137
+ except NotFoundError as error:
138
+ print(error.code, error.message)
139
+ ```
140
+
141
+ `superartifacts.PermissionError` is not the builtin of that name: import it by name when you
142
+ catch it.
143
+
144
+ ## Retries
145
+
146
+ Up to `max_retries` more tries, two by default:
147
+
148
+ - A `429` is retried after its `Retry-After`. Past 60 seconds it is not waited out: the
149
+ `RateLimitError` comes straight back with `retry_after` set.
150
+ - `502`, `503`, `504` and network errors are retried with exponential backoff and jitter, for
151
+ `GET`, `DELETE` and `POST`.
152
+ - `PATCH` and `PUT` are retried on `429` only. `500` and every other `4xx` are never retried.
153
+
154
+ Every `POST` sends an `Idempotency-Key`, one per call and the same across its retries, so a retried
155
+ publish happens once. Pass `idempotency_key=` to choose your own, which also makes a retry of your
156
+ own code safe.
157
+
158
+ ## Pagination
159
+
160
+ `iter_*` methods walk every page and yield one item at a time, taking the same arguments as the
161
+ list they wrap:
162
+
163
+ | Method | Wraps |
164
+ | --- | --- |
165
+ | `sa.data.iter_rows(...)` | `sa.data.list_rows(...)`, with `dataset=` or `collection=` |
166
+ | `sa.data.iter_artifact_rows(...)` | `sa.data.list_artifact_rows(...)` |
167
+ | `sa.entries.iter(...)` | `sa.entries.list(...)` |
168
+ | `sa.mentions.iter(...)` | `sa.mentions.list(...)` |
169
+
170
+ ## Publish a page you made
171
+
172
+ Super Artifacts runs no model: you bring the inference. Make the page with your own model, write it
173
+ to a file, publish the file.
174
+
175
+ ```python
176
+ from pathlib import Path
177
+
178
+ from anthropic import Anthropic
179
+ from superartifacts import SuperArtifacts
180
+
181
+ # Your model, your key. We run no inference.
182
+ msg = Anthropic().messages.create(
183
+ model="claude-haiku-5-5",
184
+ max_tokens=4000,
185
+ messages=[{
186
+ "role": "user",
187
+ "content": "A launch checklist. HTML only.",
188
+ }],
189
+ )
190
+ Path("checklist.html").write_text(
191
+ msg.content[0].text
192
+ )
193
+
194
+ # We host the file. Reads SUPERARTIFACTS_API_KEY
195
+ sa = SuperArtifacts()
196
+ page = sa.artifacts.publish_file(
197
+ "checklist.html"
198
+ )
199
+ print(page["url"])
200
+ ```
201
+
202
+ `publish_file` takes an HTML file, or a folder with an `index.html` (its other files are uploaded
203
+ beside it). The slug defaults to the file's or folder's name. `prompt` is optional: send it only if
204
+ you want the request shown on the artifact's card; nothing is generated from it.
205
+
206
+ ## Uploads
207
+
208
+ A JSON body stops at 1 MiB. Bigger files go through `uploads.upload`, which stages them and sends
209
+ the bytes straight to their upload URLs. It takes a mapping of path in the artifact to bytes or a
210
+ local file, within the limits of 5 MiB a file and 10 MiB a set:
211
+
212
+ ```python
213
+ staged = sa.uploads.upload({"images/hero.png": "./hero.png", "notes.txt": b"hello"})
214
+
215
+ sa.artifacts.publish(
216
+ slug="hello",
217
+ html='<!doctype html><img src="images/hero.png">',
218
+ uploads=staged["uploadId"],
219
+ )
220
+ ```
221
+
222
+ ## License
223
+
224
+ MIT
225
+
226
+ ## Webhook endpoints for an agent
227
+
228
+ Create an endpoint filtered to the agent that owns the calling key. Store the returned
229
+ signing secret in the receiver’s secret store; it is returned only by create and rotate.
230
+ The listener must be publicly reachable. Mutations require a key with write scope.
231
+
232
+ ```python
233
+ endpoint = sa.webhooks.create(
234
+ url="https://agent.example/webhook",
235
+ agent="me",
236
+ event_types=["trigger.fired"],
237
+ )
238
+ # Save endpoint["secret"] securely; never log it.
239
+ sa.webhooks.test(id=endpoint["id"])
240
+ messages = sa.webhooks.messages(id=endpoint["id"], since_minutes=60)
241
+ ```
@@ -0,0 +1,216 @@
1
+ # Super Artifacts for Python
2
+
3
+ Needs a Solo plan or higher. MCP and skills are unaffected.
4
+
5
+ The official Python SDK for the [Super Artifacts](https://superartifacts.app) REST API: publish
6
+ artifacts, read what visitors submitted, manage projects and templates, from your own code.
7
+
8
+ Full docs: <https://superartifacts.app/docs/sdks/python>
9
+
10
+ ## Install
11
+
12
+ ```bash
13
+ pip install superartifacts
14
+ ```
15
+
16
+ Python 3.10 or newer. Create a key in
17
+ [Settings → API keys](https://superartifacts.app/settings/api-keys).
18
+
19
+ ## Quickstart
20
+
21
+ ```python
22
+ import os
23
+
24
+ from superartifacts import SuperArtifacts
25
+
26
+ sa = SuperArtifacts(api_key=os.environ["SUPERARTIFACTS_API_KEY"])
27
+
28
+ print(sa.account.whoami()["handle"])
29
+
30
+ sa.artifacts.publish(
31
+ slug="hello",
32
+ html="<!doctype html><h1>Hello</h1>",
33
+ )
34
+
35
+ for artifact in sa.artifacts.list()["artifacts"]:
36
+ print(artifact["slug"])
37
+
38
+ for row in sa.data.iter_artifact_rows(artifact="hello"):
39
+ print(row)
40
+ ```
41
+
42
+ `api_key` falls back to `SUPERARTIFACTS_API_KEY`, so `SuperArtifacts()` is enough.
43
+
44
+ Keyword arguments hold path, query and body fields together, in snake_case: the SDK splits them
45
+ as the API expects, so `values_file=` goes out as `valuesFile`. Values nested inside a dict keep
46
+ the API's spelling. Answers are plain dicts, as the
47
+ [API reference](https://superartifacts.app/docs/reference/api-endpoints) describes them.
48
+
49
+ **Server-side only.** The API refuses any request carrying an `Origin` header, which every browser
50
+ adds. Keep the key on a server.
51
+
52
+ ## Async
53
+
54
+ `AsyncSuperArtifacts` is the same surface, awaited:
55
+
56
+ ```python
57
+ import asyncio
58
+
59
+ from superartifacts import AsyncSuperArtifacts
60
+
61
+
62
+ async def main():
63
+ async with AsyncSuperArtifacts() as sa:
64
+ print(await sa.account.whoami())
65
+ async for row in sa.data.iter_artifact_rows(artifact="hello"):
66
+ print(row)
67
+
68
+
69
+ asyncio.run(main())
70
+ ```
71
+
72
+ ## Configuration
73
+
74
+ ```python
75
+ SuperArtifacts(
76
+ api_key=None, # or SUPERARTIFACTS_API_KEY
77
+ base_url="https://superartifacts.app",
78
+ timeout=60.0, # seconds
79
+ max_retries=2,
80
+ http_client=None, # your own httpx.Client; yours to close
81
+ )
82
+ ```
83
+
84
+ Every method also takes `timeout=` for that call, and every `POST` takes `idempotency_key=`.
85
+ Use the client as a context manager, or call `close()`.
86
+
87
+ ## Errors
88
+
89
+ Everything raised is a `SuperArtifactsError`, with `.status`, `.code`, `.message`, `.retry_after`
90
+ and the full error object in `.details`.
91
+ A client-side failure has `.status` 0 and its own `.code`: a missing key is an `AuthenticationError`
92
+ with `missing_api_key`.
93
+
94
+ | Status | Exception |
95
+ | --- | --- |
96
+ | 400 | `InvalidArgumentsError` |
97
+ | 401 | `AuthenticationError` (also raised when there is no key) |
98
+ | 402 | `PlanLimitError` |
99
+ | 403 | `PermissionError`, or `ScopeRequiredError` when a read key tried a write |
100
+ | 404 | `NotFoundError` |
101
+ | 409 | `ConflictError` |
102
+ | 413 | `PayloadTooLargeError` |
103
+ | 429 | `RateLimitError` |
104
+ | 5xx | `InternalError` |
105
+ | none | `APIConnectionError`, `APITimeoutError` |
106
+
107
+ ```python
108
+ from superartifacts import NotFoundError
109
+
110
+ try:
111
+ sa.artifacts.get(artifact="missing")
112
+ except NotFoundError as error:
113
+ print(error.code, error.message)
114
+ ```
115
+
116
+ `superartifacts.PermissionError` is not the builtin of that name: import it by name when you
117
+ catch it.
118
+
119
+ ## Retries
120
+
121
+ Up to `max_retries` more tries, two by default:
122
+
123
+ - A `429` is retried after its `Retry-After`. Past 60 seconds it is not waited out: the
124
+ `RateLimitError` comes straight back with `retry_after` set.
125
+ - `502`, `503`, `504` and network errors are retried with exponential backoff and jitter, for
126
+ `GET`, `DELETE` and `POST`.
127
+ - `PATCH` and `PUT` are retried on `429` only. `500` and every other `4xx` are never retried.
128
+
129
+ Every `POST` sends an `Idempotency-Key`, one per call and the same across its retries, so a retried
130
+ publish happens once. Pass `idempotency_key=` to choose your own, which also makes a retry of your
131
+ own code safe.
132
+
133
+ ## Pagination
134
+
135
+ `iter_*` methods walk every page and yield one item at a time, taking the same arguments as the
136
+ list they wrap:
137
+
138
+ | Method | Wraps |
139
+ | --- | --- |
140
+ | `sa.data.iter_rows(...)` | `sa.data.list_rows(...)`, with `dataset=` or `collection=` |
141
+ | `sa.data.iter_artifact_rows(...)` | `sa.data.list_artifact_rows(...)` |
142
+ | `sa.entries.iter(...)` | `sa.entries.list(...)` |
143
+ | `sa.mentions.iter(...)` | `sa.mentions.list(...)` |
144
+
145
+ ## Publish a page you made
146
+
147
+ Super Artifacts runs no model: you bring the inference. Make the page with your own model, write it
148
+ to a file, publish the file.
149
+
150
+ ```python
151
+ from pathlib import Path
152
+
153
+ from anthropic import Anthropic
154
+ from superartifacts import SuperArtifacts
155
+
156
+ # Your model, your key. We run no inference.
157
+ msg = Anthropic().messages.create(
158
+ model="claude-haiku-5-5",
159
+ max_tokens=4000,
160
+ messages=[{
161
+ "role": "user",
162
+ "content": "A launch checklist. HTML only.",
163
+ }],
164
+ )
165
+ Path("checklist.html").write_text(
166
+ msg.content[0].text
167
+ )
168
+
169
+ # We host the file. Reads SUPERARTIFACTS_API_KEY
170
+ sa = SuperArtifacts()
171
+ page = sa.artifacts.publish_file(
172
+ "checklist.html"
173
+ )
174
+ print(page["url"])
175
+ ```
176
+
177
+ `publish_file` takes an HTML file, or a folder with an `index.html` (its other files are uploaded
178
+ beside it). The slug defaults to the file's or folder's name. `prompt` is optional: send it only if
179
+ you want the request shown on the artifact's card; nothing is generated from it.
180
+
181
+ ## Uploads
182
+
183
+ A JSON body stops at 1 MiB. Bigger files go through `uploads.upload`, which stages them and sends
184
+ the bytes straight to their upload URLs. It takes a mapping of path in the artifact to bytes or a
185
+ local file, within the limits of 5 MiB a file and 10 MiB a set:
186
+
187
+ ```python
188
+ staged = sa.uploads.upload({"images/hero.png": "./hero.png", "notes.txt": b"hello"})
189
+
190
+ sa.artifacts.publish(
191
+ slug="hello",
192
+ html='<!doctype html><img src="images/hero.png">',
193
+ uploads=staged["uploadId"],
194
+ )
195
+ ```
196
+
197
+ ## License
198
+
199
+ MIT
200
+
201
+ ## Webhook endpoints for an agent
202
+
203
+ Create an endpoint filtered to the agent that owns the calling key. Store the returned
204
+ signing secret in the receiver’s secret store; it is returned only by create and rotate.
205
+ The listener must be publicly reachable. Mutations require a key with write scope.
206
+
207
+ ```python
208
+ endpoint = sa.webhooks.create(
209
+ url="https://agent.example/webhook",
210
+ agent="me",
211
+ event_types=["trigger.fired"],
212
+ )
213
+ # Save endpoint["secret"] securely; never log it.
214
+ sa.webhooks.test(id=endpoint["id"])
215
+ messages = sa.webhooks.messages(id=endpoint["id"], since_minutes=60)
216
+ ```
@@ -0,0 +1,48 @@
1
+ [build-system]
2
+ requires = ["hatchling"]
3
+ build-backend = "hatchling.build"
4
+
5
+ [project]
6
+ name = "superartifacts"
7
+ version = "0.1.0"
8
+ description = "Python SDK for the Super Artifacts REST API: publish artifacts and read their data from your own code."
9
+ readme = "README.md"
10
+ requires-python = ">=3.10"
11
+ license = "MIT"
12
+ license-files = ["LICENSE"]
13
+ authors = [{ name = "Super Artifacts" }]
14
+ keywords = ["super-artifacts", "artifacts", "publishing", "api", "sdk"]
15
+ classifiers = [
16
+ "Development Status :: 4 - Beta",
17
+ "Intended Audience :: Developers",
18
+ "License :: OSI Approved :: MIT License",
19
+ "Programming Language :: Python :: 3",
20
+ "Programming Language :: Python :: 3.10",
21
+ "Programming Language :: Python :: 3.11",
22
+ "Programming Language :: Python :: 3.12",
23
+ "Programming Language :: Python :: 3.13",
24
+ "Typing :: Typed",
25
+ ]
26
+ dependencies = ["httpx>=0.27,<1", "pydantic>=2,<3"]
27
+
28
+ [project.urls]
29
+ Homepage = "https://superartifacts.app/docs/sdks/python"
30
+ Repository = "https://github.com/Superbelt/sdk"
31
+ Issues = "https://github.com/Superbelt/sdk/issues"
32
+
33
+ [dependency-groups]
34
+ dev = ["pytest>=8"]
35
+
36
+ [tool.hatch.build.targets.wheel]
37
+ packages = ["src/superartifacts"]
38
+
39
+ [tool.pytest.ini_options]
40
+ testpaths = ["tests"]
41
+
42
+ [tool.ruff]
43
+ line-length = 100
44
+ target-version = "py310"
45
+ extend-exclude = ["src/superartifacts/_generated"]
46
+
47
+ [tool.ruff.lint]
48
+ select = ["E", "F", "I", "UP", "B"]
@@ -0,0 +1,39 @@
1
+ """Python SDK for the Super Artifacts REST API. https://superartifacts.app/docs/sdks/python"""
2
+
3
+ from ._client import AsyncSuperArtifacts, SuperArtifacts, __version__
4
+ from ._errors import (
5
+ APIConnectionError,
6
+ APITimeoutError,
7
+ AuthenticationError,
8
+ ConflictError,
9
+ InternalError,
10
+ InvalidArgumentsError,
11
+ NotFoundError,
12
+ PayloadTooLargeError,
13
+ PermissionError,
14
+ PlanLimitError,
15
+ RateLimitError,
16
+ ScopeRequiredError,
17
+ SuperArtifactsError,
18
+ )
19
+ from ._generated import models
20
+
21
+ __all__ = [
22
+ "APIConnectionError",
23
+ "APITimeoutError",
24
+ "AsyncSuperArtifacts",
25
+ "AuthenticationError",
26
+ "ConflictError",
27
+ "InternalError",
28
+ "InvalidArgumentsError",
29
+ "NotFoundError",
30
+ "PayloadTooLargeError",
31
+ "PermissionError",
32
+ "PlanLimitError",
33
+ "RateLimitError",
34
+ "ScopeRequiredError",
35
+ "SuperArtifacts",
36
+ "SuperArtifactsError",
37
+ "__version__",
38
+ "models",
39
+ ]