unitysvc-py 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.
- unitysvc_py-0.1.0/LICENSE +21 -0
- unitysvc_py-0.1.0/PKG-INFO +200 -0
- unitysvc_py-0.1.0/README.md +160 -0
- unitysvc_py-0.1.0/pyproject.toml +90 -0
- unitysvc_py-0.1.0/setup.cfg +4 -0
- unitysvc_py-0.1.0/src/unitysvc_py/__init__.py +68 -0
- unitysvc_py-0.1.0/src/unitysvc_py/_generated/__init__.py +8 -0
- unitysvc_py-0.1.0/src/unitysvc_py/_generated/api/__init__.py +1 -0
- unitysvc_py-0.1.0/src/unitysvc_py/_generated/api/customer_aliases/__init__.py +1 -0
- unitysvc_py-0.1.0/src/unitysvc_py/_generated/api/customer_aliases/customer_aliases_create_alias.py +241 -0
- unitysvc_py-0.1.0/src/unitysvc_py/_generated/api/customer_aliases/customer_aliases_delete_alias.py +221 -0
- unitysvc_py-0.1.0/src/unitysvc_py/_generated/api/customer_aliases/customer_aliases_get_alias.py +217 -0
- unitysvc_py-0.1.0/src/unitysvc_py/_generated/api/customer_aliases/customer_aliases_list_aliases.py +273 -0
- unitysvc_py-0.1.0/src/unitysvc_py/_generated/api/customer_aliases/customer_aliases_set_alias_routing.py +237 -0
- unitysvc_py-0.1.0/src/unitysvc_py/_generated/api/customer_aliases/customer_aliases_update_alias.py +251 -0
- unitysvc_py-0.1.0/src/unitysvc_py/_generated/api/customer_recurrent_requests/__init__.py +1 -0
- unitysvc_py-0.1.0/src/unitysvc_py/_generated/api/customer_recurrent_requests/customer_recurrent_requests_create_recurrent_request.py +241 -0
- unitysvc_py-0.1.0/src/unitysvc_py/_generated/api/customer_recurrent_requests/customer_recurrent_requests_get_recurrent_request_detail.py +225 -0
- unitysvc_py-0.1.0/src/unitysvc_py/_generated/api/customer_recurrent_requests/customer_recurrent_requests_list_recurrent_requests.py +315 -0
- unitysvc_py-0.1.0/src/unitysvc_py/_generated/api/customer_recurrent_requests/customer_recurrent_requests_remove_recurrent_request.py +221 -0
- unitysvc_py-0.1.0/src/unitysvc_py/_generated/api/customer_recurrent_requests/customer_recurrent_requests_trigger_recurrent_request.py +237 -0
- unitysvc_py-0.1.0/src/unitysvc_py/_generated/api/customer_recurrent_requests/customer_recurrent_requests_update_recurrent_request.py +255 -0
- unitysvc_py-0.1.0/src/unitysvc_py/_generated/api/customer_secrets/__init__.py +1 -0
- unitysvc_py-0.1.0/src/unitysvc_py/_generated/api/customer_secrets/customer_secrets_check_secret_exists.py +236 -0
- unitysvc_py-0.1.0/src/unitysvc_py/_generated/api/customer_secrets/customer_secrets_create_secret.py +261 -0
- unitysvc_py-0.1.0/src/unitysvc_py/_generated/api/customer_secrets/customer_secrets_delete_secret.py +241 -0
- unitysvc_py-0.1.0/src/unitysvc_py/_generated/api/customer_secrets/customer_secrets_get_secret.py +233 -0
- unitysvc_py-0.1.0/src/unitysvc_py/_generated/api/customer_secrets/customer_secrets_list_secrets.py +254 -0
- unitysvc_py-0.1.0/src/unitysvc_py/_generated/api/customer_secrets/customer_secrets_update_secret.py +259 -0
- unitysvc_py-0.1.0/src/unitysvc_py/_generated/client.py +263 -0
- unitysvc_py-0.1.0/src/unitysvc_py/_generated/errors.py +14 -0
- unitysvc_py-0.1.0/src/unitysvc_py/_generated/models/__init__.py +59 -0
- unitysvc_py-0.1.0/src/unitysvc_py/_generated/models/http_validation_error.py +94 -0
- unitysvc_py-0.1.0/src/unitysvc_py/_generated/models/message.py +71 -0
- unitysvc_py-0.1.0/src/unitysvc_py/_generated/models/recurrent_request_create.py +242 -0
- unitysvc_py-0.1.0/src/unitysvc_py/_generated/models/recurrent_request_create_body_template_type_0.py +62 -0
- unitysvc_py-0.1.0/src/unitysvc_py/_generated/models/recurrent_request_create_request_headers_type_0.py +62 -0
- unitysvc_py-0.1.0/src/unitysvc_py/_generated/models/recurrent_request_public.py +337 -0
- unitysvc_py-0.1.0/src/unitysvc_py/_generated/models/recurrent_request_public_body_template_type_0.py +62 -0
- unitysvc_py-0.1.0/src/unitysvc_py/_generated/models/recurrent_request_public_schedule_type_0.py +62 -0
- unitysvc_py-0.1.0/src/unitysvc_py/_generated/models/recurrent_request_public_state_type_0.py +62 -0
- unitysvc_py-0.1.0/src/unitysvc_py/_generated/models/recurrent_request_status_enum.py +10 -0
- unitysvc_py-0.1.0/src/unitysvc_py/_generated/models/recurrent_request_update.py +250 -0
- unitysvc_py-0.1.0/src/unitysvc_py/_generated/models/recurrent_request_update_body_template_type_0.py +62 -0
- unitysvc_py-0.1.0/src/unitysvc_py/_generated/models/recurrent_request_update_request_headers_type_0.py +62 -0
- unitysvc_py-0.1.0/src/unitysvc_py/_generated/models/recurrent_request_update_schedule_type_0.py +62 -0
- unitysvc_py-0.1.0/src/unitysvc_py/_generated/models/recurrent_requests_public.py +98 -0
- unitysvc_py-0.1.0/src/unitysvc_py/_generated/models/secret_create.py +82 -0
- unitysvc_py-0.1.0/src/unitysvc_py/_generated/models/secret_exists_response.py +80 -0
- unitysvc_py-0.1.0/src/unitysvc_py/_generated/models/secret_owner_type_enum.py +10 -0
- unitysvc_py-0.1.0/src/unitysvc_py/_generated/models/secret_public.py +176 -0
- unitysvc_py-0.1.0/src/unitysvc_py/_generated/models/secret_update.py +74 -0
- unitysvc_py-0.1.0/src/unitysvc_py/_generated/models/secrets_public.py +98 -0
- unitysvc_py-0.1.0/src/unitysvc_py/_generated/models/service_alias_create.py +173 -0
- unitysvc_py-0.1.0/src/unitysvc_py/_generated/models/service_alias_create_routing_key_override_type_0.py +62 -0
- unitysvc_py-0.1.0/src/unitysvc_py/_generated/models/service_alias_update.py +207 -0
- unitysvc_py-0.1.0/src/unitysvc_py/_generated/models/service_alias_update_request_routing_key_type_0.py +62 -0
- unitysvc_py-0.1.0/src/unitysvc_py/_generated/models/service_alias_update_routing_key_override_type_0.py +62 -0
- unitysvc_py-0.1.0/src/unitysvc_py/_generated/models/validation_error.py +102 -0
- unitysvc_py-0.1.0/src/unitysvc_py/_generated/types.py +53 -0
- unitysvc_py-0.1.0/src/unitysvc_py/_http.py +57 -0
- unitysvc_py-0.1.0/src/unitysvc_py/aclient.py +152 -0
- unitysvc_py-0.1.0/src/unitysvc_py/cli.py +87 -0
- unitysvc_py-0.1.0/src/unitysvc_py/client.py +186 -0
- unitysvc_py-0.1.0/src/unitysvc_py/commands/__init__.py +5 -0
- unitysvc_py-0.1.0/src/unitysvc_py/commands/_helpers.py +104 -0
- unitysvc_py-0.1.0/src/unitysvc_py/commands/aliases.py +109 -0
- unitysvc_py-0.1.0/src/unitysvc_py/commands/recurrent_requests.py +127 -0
- unitysvc_py-0.1.0/src/unitysvc_py/commands/secrets.py +159 -0
- unitysvc_py-0.1.0/src/unitysvc_py/exceptions.py +120 -0
- unitysvc_py-0.1.0/src/unitysvc_py/py.typed +0 -0
- unitysvc_py-0.1.0/src/unitysvc_py/resources/__init__.py +8 -0
- unitysvc_py-0.1.0/src/unitysvc_py/resources/aaliases.py +104 -0
- unitysvc_py-0.1.0/src/unitysvc_py/resources/aliases.py +128 -0
- unitysvc_py-0.1.0/src/unitysvc_py/resources/arecurrent_requests.py +131 -0
- unitysvc_py-0.1.0/src/unitysvc_py/resources/asecrets.py +103 -0
- unitysvc_py-0.1.0/src/unitysvc_py/resources/recurrent_requests.py +150 -0
- unitysvc_py-0.1.0/src/unitysvc_py/resources/secrets.py +122 -0
- unitysvc_py-0.1.0/src/unitysvc_py.egg-info/PKG-INFO +200 -0
- unitysvc_py-0.1.0/src/unitysvc_py.egg-info/SOURCES.txt +85 -0
- unitysvc_py-0.1.0/src/unitysvc_py.egg-info/dependency_links.txt +1 -0
- unitysvc_py-0.1.0/src/unitysvc_py.egg-info/entry_points.txt +2 -0
- unitysvc_py-0.1.0/src/unitysvc_py.egg-info/requires.txt +25 -0
- unitysvc_py-0.1.0/src/unitysvc_py.egg-info/top_level.txt +1 -0
- unitysvc_py-0.1.0/tests/test_cli.py +52 -0
- unitysvc_py-0.1.0/tests/test_client.py +86 -0
- unitysvc_py-0.1.0/tests/test_http.py +57 -0
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026, Bo Peng
|
|
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,200 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: unitysvc-py
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Customer-facing Python SDK and `usvc` CLI for UnitySVC
|
|
5
|
+
Author-email: Bo Peng <bo.peng@unitysvc.com>
|
|
6
|
+
Maintainer-email: Bo Peng <bo.peng@unitysvc.com>
|
|
7
|
+
License-Expression: MIT
|
|
8
|
+
Project-URL: bugs, https://github.com/unitysvc/unitysvc-py/issues
|
|
9
|
+
Project-URL: homepage, https://github.com/unitysvc/unitysvc-py
|
|
10
|
+
Classifier: Programming Language :: Python :: 3
|
|
11
|
+
Classifier: Programming Language :: Python :: 3 :: Only
|
|
12
|
+
Classifier: Operating System :: OS Independent
|
|
13
|
+
Classifier: Typing :: Typed
|
|
14
|
+
Requires-Python: >=3.11
|
|
15
|
+
Description-Content-Type: text/markdown
|
|
16
|
+
License-File: LICENSE
|
|
17
|
+
Requires-Dist: typer
|
|
18
|
+
Requires-Dist: rich
|
|
19
|
+
Requires-Dist: httpx>=0.24
|
|
20
|
+
Requires-Dist: attrs>=23.0
|
|
21
|
+
Requires-Dist: python-dateutil>=2.8
|
|
22
|
+
Provides-Extra: test
|
|
23
|
+
Requires-Dist: coverage; extra == "test"
|
|
24
|
+
Requires-Dist: pytest; extra == "test"
|
|
25
|
+
Requires-Dist: pytest-asyncio; extra == "test"
|
|
26
|
+
Requires-Dist: ruff; extra == "test"
|
|
27
|
+
Requires-Dist: mypy; extra == "test"
|
|
28
|
+
Requires-Dist: respx; extra == "test"
|
|
29
|
+
Provides-Extra: dev
|
|
30
|
+
Requires-Dist: coverage; extra == "dev"
|
|
31
|
+
Requires-Dist: pytest; extra == "dev"
|
|
32
|
+
Requires-Dist: ruff; extra == "dev"
|
|
33
|
+
Requires-Dist: ipdb; extra == "dev"
|
|
34
|
+
Provides-Extra: docs
|
|
35
|
+
Requires-Dist: mkdocs; extra == "docs"
|
|
36
|
+
Requires-Dist: mkdocs-material; extra == "docs"
|
|
37
|
+
Requires-Dist: mkdocs-autorefs; extra == "docs"
|
|
38
|
+
Requires-Dist: pymdown-extensions; extra == "docs"
|
|
39
|
+
Dynamic: license-file
|
|
40
|
+
|
|
41
|
+
# unitysvc-py
|
|
42
|
+
|
|
43
|
+
Customer-facing Python SDK and `usvc` CLI for [UnitySVC](https://unitysvc.com/).
|
|
44
|
+
|
|
45
|
+
This package is a thin, typed facade over the UnitySVC customer API.
|
|
46
|
+
It exposes:
|
|
47
|
+
|
|
48
|
+
- A sync [`Client`](#programmatic-usage) and async `AsyncClient` for
|
|
49
|
+
the customer-tagged backend endpoints.
|
|
50
|
+
- The `usvc` CLI for managing the same resources from the terminal.
|
|
51
|
+
|
|
52
|
+
> **Status:** early scaffolding. The customer API currently exposes
|
|
53
|
+
> only a handful of endpoints (aliases, recurrent requests, secrets);
|
|
54
|
+
> the SDK tracks those today and will grow as the backend adds more.
|
|
55
|
+
|
|
56
|
+
## Install
|
|
57
|
+
|
|
58
|
+
```bash
|
|
59
|
+
pip install unitysvc-py
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
## Programmatic usage
|
|
63
|
+
|
|
64
|
+
```python
|
|
65
|
+
from unitysvc_py import Client
|
|
66
|
+
|
|
67
|
+
client = Client(api_key="svcpass_...") # or Client.from_env()
|
|
68
|
+
|
|
69
|
+
# List and set secrets
|
|
70
|
+
secrets = client.secrets.list()
|
|
71
|
+
for s in secrets.data:
|
|
72
|
+
print(s.name, s.id)
|
|
73
|
+
|
|
74
|
+
client.secrets.create({"name": "openai-key", "value": "sk-..."})
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
### Configuration
|
|
78
|
+
|
|
79
|
+
The SDK is configured via a small set of environment variables. Only
|
|
80
|
+
`UNITYSVC_API_KEY` is required; everything else has a sensible
|
|
81
|
+
default.
|
|
82
|
+
|
|
83
|
+
| Variable | Purpose | Default |
|
|
84
|
+
|--------------------------|---------------------------------------------|----------------------------------------------|
|
|
85
|
+
| `UNITYSVC_API_KEY` | Customer API key (`svcpass_...`) | (required) |
|
|
86
|
+
| `UNITYSVC_API_URL` | Control-plane API base URL | `https://api.staging.unitysvc.com/v1` |
|
|
87
|
+
| `UNITYSVC_API_BASE_URL` | HTTP API gateway base URL (inference) | (unset) |
|
|
88
|
+
| `UNITYSVC_S3_BASE_URL` | S3-compatible gateway base URL | (unset) |
|
|
89
|
+
| `UNITYSVC_SMTP_BASE_URL` | SMTP gateway base URL | (unset) |
|
|
90
|
+
|
|
91
|
+
The customer context is encoded entirely in the API key, so no
|
|
92
|
+
separate `customer_id` argument is required.
|
|
93
|
+
|
|
94
|
+
`UNITYSVC_API_URL` is what the SDK itself uses for control-plane
|
|
95
|
+
calls. The `*_BASE_URL` variables are exposed on `Client` as
|
|
96
|
+
`client.api_base_url`, `client.s3_base_url`, `client.smtp_base_url`
|
|
97
|
+
for downstream inference/storage/email SDKs to pick up — the SDK
|
|
98
|
+
itself doesn't talk to them directly.
|
|
99
|
+
|
|
100
|
+
### Async client
|
|
101
|
+
|
|
102
|
+
```python
|
|
103
|
+
import asyncio
|
|
104
|
+
from unitysvc_py import AsyncClient
|
|
105
|
+
|
|
106
|
+
async def main():
|
|
107
|
+
async with AsyncClient(api_key="svcpass_...") as client:
|
|
108
|
+
secrets = await client.secrets.list()
|
|
109
|
+
for s in secrets.data:
|
|
110
|
+
print(s.name)
|
|
111
|
+
|
|
112
|
+
asyncio.run(main())
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
### Errors
|
|
116
|
+
|
|
117
|
+
All errors are subclasses of `unitysvc_py.UnitysvcSDKError`:
|
|
118
|
+
|
|
119
|
+
```python
|
|
120
|
+
from unitysvc_py import (
|
|
121
|
+
UnitysvcSDKError,
|
|
122
|
+
AuthenticationError, # 401
|
|
123
|
+
PermissionError, # 403
|
|
124
|
+
NotFoundError, # 404
|
|
125
|
+
ValidationError, # 400, 422
|
|
126
|
+
ConflictError, # 409
|
|
127
|
+
RateLimitError, # 429
|
|
128
|
+
ServerError, # 5xx
|
|
129
|
+
APIError, # base for everything above
|
|
130
|
+
)
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
## CLI: `usvc`
|
|
134
|
+
|
|
135
|
+
The CLI follows the SDK's configuration — set `UNITYSVC_API_KEY` (and
|
|
136
|
+
optionally `UNITYSVC_API_URL`) and you can run:
|
|
137
|
+
|
|
138
|
+
```
|
|
139
|
+
usvc env # show which env vars the SDK will pick up
|
|
140
|
+
|
|
141
|
+
# Secrets
|
|
142
|
+
usvc secrets list # list secrets
|
|
143
|
+
usvc secrets set NAME --value V # create or update a secret by name
|
|
144
|
+
usvc secrets delete NAME # delete a secret by name
|
|
145
|
+
|
|
146
|
+
# Service aliases
|
|
147
|
+
usvc aliases list
|
|
148
|
+
usvc aliases show ALIAS_ID
|
|
149
|
+
usvc aliases delete ALIAS_ID
|
|
150
|
+
|
|
151
|
+
# Recurrent requests
|
|
152
|
+
usvc recurrent-requests list
|
|
153
|
+
usvc recurrent-requests show REQUEST_ID
|
|
154
|
+
usvc recurrent-requests trigger REQUEST_ID
|
|
155
|
+
usvc recurrent-requests delete REQUEST_ID
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
Every command accepts `--api-key` and `--base-url` overrides.
|
|
159
|
+
|
|
160
|
+
## Layout
|
|
161
|
+
|
|
162
|
+
```
|
|
163
|
+
src/unitysvc_py/
|
|
164
|
+
├── client.py # Client (sync) facade
|
|
165
|
+
├── aclient.py # AsyncClient (async) facade
|
|
166
|
+
├── exceptions.py # UnitysvcSDKError + status-code subclasses
|
|
167
|
+
├── _http.py # internal: unwrap generated Response → typed model or APIError
|
|
168
|
+
├── resources/
|
|
169
|
+
│ ├── secrets.py # client.secrets.*
|
|
170
|
+
│ ├── aliases.py # client.aliases.*
|
|
171
|
+
│ ├── recurrent_requests.py # client.recurrent_requests.*
|
|
172
|
+
│ ├── asecrets.py # async mirror
|
|
173
|
+
│ ├── aaliases.py # async mirror
|
|
174
|
+
│ └── arecurrent_requests.py # async mirror
|
|
175
|
+
├── _generated/ # openapi-python-client output (do not edit by hand)
|
|
176
|
+
├── commands/ # Typer command groups for the CLI
|
|
177
|
+
│ ├── _helpers.py # run_async, async_client, model_list, ...
|
|
178
|
+
│ └── secrets.py # `usvc secrets {list,set,delete}`
|
|
179
|
+
└── cli.py # `usvc` Typer entry point
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
## Regenerating the API client
|
|
183
|
+
|
|
184
|
+
The low-level client under `src/unitysvc_py/_generated/` is produced
|
|
185
|
+
by [openapi-python-client] from a copy of the backend OpenAPI spec at
|
|
186
|
+
`openapi.json`. To regenerate after a backend change:
|
|
187
|
+
|
|
188
|
+
```bash
|
|
189
|
+
# Requires a sibling checkout of unitysvc/unitysvc
|
|
190
|
+
./scripts/generate_client.sh
|
|
191
|
+
```
|
|
192
|
+
|
|
193
|
+
The script reads `../unitysvc/backend/generated/customer_api.json` by
|
|
194
|
+
default; pass an explicit spec path as the first argument to override.
|
|
195
|
+
|
|
196
|
+
[openapi-python-client]: https://github.com/openapi-generators/openapi-python-client
|
|
197
|
+
|
|
198
|
+
## License
|
|
199
|
+
|
|
200
|
+
MIT
|
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
# unitysvc-py
|
|
2
|
+
|
|
3
|
+
Customer-facing Python SDK and `usvc` CLI for [UnitySVC](https://unitysvc.com/).
|
|
4
|
+
|
|
5
|
+
This package is a thin, typed facade over the UnitySVC customer API.
|
|
6
|
+
It exposes:
|
|
7
|
+
|
|
8
|
+
- A sync [`Client`](#programmatic-usage) and async `AsyncClient` for
|
|
9
|
+
the customer-tagged backend endpoints.
|
|
10
|
+
- The `usvc` CLI for managing the same resources from the terminal.
|
|
11
|
+
|
|
12
|
+
> **Status:** early scaffolding. The customer API currently exposes
|
|
13
|
+
> only a handful of endpoints (aliases, recurrent requests, secrets);
|
|
14
|
+
> the SDK tracks those today and will grow as the backend adds more.
|
|
15
|
+
|
|
16
|
+
## Install
|
|
17
|
+
|
|
18
|
+
```bash
|
|
19
|
+
pip install unitysvc-py
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
## Programmatic usage
|
|
23
|
+
|
|
24
|
+
```python
|
|
25
|
+
from unitysvc_py import Client
|
|
26
|
+
|
|
27
|
+
client = Client(api_key="svcpass_...") # or Client.from_env()
|
|
28
|
+
|
|
29
|
+
# List and set secrets
|
|
30
|
+
secrets = client.secrets.list()
|
|
31
|
+
for s in secrets.data:
|
|
32
|
+
print(s.name, s.id)
|
|
33
|
+
|
|
34
|
+
client.secrets.create({"name": "openai-key", "value": "sk-..."})
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
### Configuration
|
|
38
|
+
|
|
39
|
+
The SDK is configured via a small set of environment variables. Only
|
|
40
|
+
`UNITYSVC_API_KEY` is required; everything else has a sensible
|
|
41
|
+
default.
|
|
42
|
+
|
|
43
|
+
| Variable | Purpose | Default |
|
|
44
|
+
|--------------------------|---------------------------------------------|----------------------------------------------|
|
|
45
|
+
| `UNITYSVC_API_KEY` | Customer API key (`svcpass_...`) | (required) |
|
|
46
|
+
| `UNITYSVC_API_URL` | Control-plane API base URL | `https://api.staging.unitysvc.com/v1` |
|
|
47
|
+
| `UNITYSVC_API_BASE_URL` | HTTP API gateway base URL (inference) | (unset) |
|
|
48
|
+
| `UNITYSVC_S3_BASE_URL` | S3-compatible gateway base URL | (unset) |
|
|
49
|
+
| `UNITYSVC_SMTP_BASE_URL` | SMTP gateway base URL | (unset) |
|
|
50
|
+
|
|
51
|
+
The customer context is encoded entirely in the API key, so no
|
|
52
|
+
separate `customer_id` argument is required.
|
|
53
|
+
|
|
54
|
+
`UNITYSVC_API_URL` is what the SDK itself uses for control-plane
|
|
55
|
+
calls. The `*_BASE_URL` variables are exposed on `Client` as
|
|
56
|
+
`client.api_base_url`, `client.s3_base_url`, `client.smtp_base_url`
|
|
57
|
+
for downstream inference/storage/email SDKs to pick up — the SDK
|
|
58
|
+
itself doesn't talk to them directly.
|
|
59
|
+
|
|
60
|
+
### Async client
|
|
61
|
+
|
|
62
|
+
```python
|
|
63
|
+
import asyncio
|
|
64
|
+
from unitysvc_py import AsyncClient
|
|
65
|
+
|
|
66
|
+
async def main():
|
|
67
|
+
async with AsyncClient(api_key="svcpass_...") as client:
|
|
68
|
+
secrets = await client.secrets.list()
|
|
69
|
+
for s in secrets.data:
|
|
70
|
+
print(s.name)
|
|
71
|
+
|
|
72
|
+
asyncio.run(main())
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
### Errors
|
|
76
|
+
|
|
77
|
+
All errors are subclasses of `unitysvc_py.UnitysvcSDKError`:
|
|
78
|
+
|
|
79
|
+
```python
|
|
80
|
+
from unitysvc_py import (
|
|
81
|
+
UnitysvcSDKError,
|
|
82
|
+
AuthenticationError, # 401
|
|
83
|
+
PermissionError, # 403
|
|
84
|
+
NotFoundError, # 404
|
|
85
|
+
ValidationError, # 400, 422
|
|
86
|
+
ConflictError, # 409
|
|
87
|
+
RateLimitError, # 429
|
|
88
|
+
ServerError, # 5xx
|
|
89
|
+
APIError, # base for everything above
|
|
90
|
+
)
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
## CLI: `usvc`
|
|
94
|
+
|
|
95
|
+
The CLI follows the SDK's configuration — set `UNITYSVC_API_KEY` (and
|
|
96
|
+
optionally `UNITYSVC_API_URL`) and you can run:
|
|
97
|
+
|
|
98
|
+
```
|
|
99
|
+
usvc env # show which env vars the SDK will pick up
|
|
100
|
+
|
|
101
|
+
# Secrets
|
|
102
|
+
usvc secrets list # list secrets
|
|
103
|
+
usvc secrets set NAME --value V # create or update a secret by name
|
|
104
|
+
usvc secrets delete NAME # delete a secret by name
|
|
105
|
+
|
|
106
|
+
# Service aliases
|
|
107
|
+
usvc aliases list
|
|
108
|
+
usvc aliases show ALIAS_ID
|
|
109
|
+
usvc aliases delete ALIAS_ID
|
|
110
|
+
|
|
111
|
+
# Recurrent requests
|
|
112
|
+
usvc recurrent-requests list
|
|
113
|
+
usvc recurrent-requests show REQUEST_ID
|
|
114
|
+
usvc recurrent-requests trigger REQUEST_ID
|
|
115
|
+
usvc recurrent-requests delete REQUEST_ID
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
Every command accepts `--api-key` and `--base-url` overrides.
|
|
119
|
+
|
|
120
|
+
## Layout
|
|
121
|
+
|
|
122
|
+
```
|
|
123
|
+
src/unitysvc_py/
|
|
124
|
+
├── client.py # Client (sync) facade
|
|
125
|
+
├── aclient.py # AsyncClient (async) facade
|
|
126
|
+
├── exceptions.py # UnitysvcSDKError + status-code subclasses
|
|
127
|
+
├── _http.py # internal: unwrap generated Response → typed model or APIError
|
|
128
|
+
├── resources/
|
|
129
|
+
│ ├── secrets.py # client.secrets.*
|
|
130
|
+
│ ├── aliases.py # client.aliases.*
|
|
131
|
+
│ ├── recurrent_requests.py # client.recurrent_requests.*
|
|
132
|
+
│ ├── asecrets.py # async mirror
|
|
133
|
+
│ ├── aaliases.py # async mirror
|
|
134
|
+
│ └── arecurrent_requests.py # async mirror
|
|
135
|
+
├── _generated/ # openapi-python-client output (do not edit by hand)
|
|
136
|
+
├── commands/ # Typer command groups for the CLI
|
|
137
|
+
│ ├── _helpers.py # run_async, async_client, model_list, ...
|
|
138
|
+
│ └── secrets.py # `usvc secrets {list,set,delete}`
|
|
139
|
+
└── cli.py # `usvc` Typer entry point
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
## Regenerating the API client
|
|
143
|
+
|
|
144
|
+
The low-level client under `src/unitysvc_py/_generated/` is produced
|
|
145
|
+
by [openapi-python-client] from a copy of the backend OpenAPI spec at
|
|
146
|
+
`openapi.json`. To regenerate after a backend change:
|
|
147
|
+
|
|
148
|
+
```bash
|
|
149
|
+
# Requires a sibling checkout of unitysvc/unitysvc
|
|
150
|
+
./scripts/generate_client.sh
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
The script reads `../unitysvc/backend/generated/customer_api.json` by
|
|
154
|
+
default; pass an explicit spec path as the first argument to override.
|
|
155
|
+
|
|
156
|
+
[openapi-python-client]: https://github.com/openapi-generators/openapi-python-client
|
|
157
|
+
|
|
158
|
+
## License
|
|
159
|
+
|
|
160
|
+
MIT
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=61.0", "wheel"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "unitysvc-py"
|
|
7
|
+
version = "0.1.0"
|
|
8
|
+
description = "Customer-facing Python SDK and `usvc` CLI for UnitySVC"
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
authors = [{ name = "Bo Peng", email = "bo.peng@unitysvc.com" }]
|
|
11
|
+
maintainers = [{ name = "Bo Peng", email = "bo.peng@unitysvc.com" }]
|
|
12
|
+
license = "MIT"
|
|
13
|
+
classifiers = [
|
|
14
|
+
"Programming Language :: Python :: 3",
|
|
15
|
+
"Programming Language :: Python :: 3 :: Only",
|
|
16
|
+
"Operating System :: OS Independent",
|
|
17
|
+
"Typing :: Typed",
|
|
18
|
+
]
|
|
19
|
+
dependencies = [
|
|
20
|
+
"typer",
|
|
21
|
+
"rich",
|
|
22
|
+
"httpx>=0.24",
|
|
23
|
+
"attrs>=23.0",
|
|
24
|
+
"python-dateutil>=2.8",
|
|
25
|
+
]
|
|
26
|
+
requires-python = ">= 3.11"
|
|
27
|
+
|
|
28
|
+
[project.optional-dependencies]
|
|
29
|
+
test = [
|
|
30
|
+
"coverage",
|
|
31
|
+
"pytest",
|
|
32
|
+
"pytest-asyncio",
|
|
33
|
+
"ruff",
|
|
34
|
+
"mypy",
|
|
35
|
+
"respx",
|
|
36
|
+
]
|
|
37
|
+
dev = [
|
|
38
|
+
"coverage",
|
|
39
|
+
"pytest",
|
|
40
|
+
"ruff",
|
|
41
|
+
"ipdb",
|
|
42
|
+
]
|
|
43
|
+
docs = [
|
|
44
|
+
"mkdocs",
|
|
45
|
+
"mkdocs-material",
|
|
46
|
+
"mkdocs-autorefs",
|
|
47
|
+
"pymdown-extensions",
|
|
48
|
+
]
|
|
49
|
+
|
|
50
|
+
[project.urls]
|
|
51
|
+
bugs = "https://github.com/unitysvc/unitysvc-py/issues"
|
|
52
|
+
homepage = "https://github.com/unitysvc/unitysvc-py"
|
|
53
|
+
|
|
54
|
+
[project.scripts]
|
|
55
|
+
usvc = "unitysvc_py.cli:app"
|
|
56
|
+
|
|
57
|
+
[tool.ruff]
|
|
58
|
+
line-length = 120
|
|
59
|
+
# openapi-python-client produces its own idiosyncratic formatting.
|
|
60
|
+
# Excluding keeps regenerations byte-stable — no post-processing
|
|
61
|
+
# step, no drift between the spec and the committed client.
|
|
62
|
+
extend-exclude = ["src/unitysvc_py/_generated"]
|
|
63
|
+
|
|
64
|
+
[tool.ruff.lint]
|
|
65
|
+
select = [
|
|
66
|
+
"E", # pycodestyle errors
|
|
67
|
+
"W", # pycodestyle warnings
|
|
68
|
+
"F", # Pyflakes
|
|
69
|
+
"I", # isort
|
|
70
|
+
"B", # flake8-bugbear
|
|
71
|
+
"UP", # pyupgrade
|
|
72
|
+
]
|
|
73
|
+
ignore = [
|
|
74
|
+
"B008", # Typer pattern uses function calls in argument defaults
|
|
75
|
+
"B904",
|
|
76
|
+
]
|
|
77
|
+
|
|
78
|
+
[tool.mypy]
|
|
79
|
+
ignore_missing_imports = true
|
|
80
|
+
warn_unused_ignores = false
|
|
81
|
+
disable_error_code = ["import-untyped"]
|
|
82
|
+
|
|
83
|
+
[tool.setuptools]
|
|
84
|
+
include-package-data = true
|
|
85
|
+
|
|
86
|
+
[tool.setuptools.package-data]
|
|
87
|
+
unitysvc_py = ["py.typed"]
|
|
88
|
+
|
|
89
|
+
[tool.pytest.ini_options]
|
|
90
|
+
testpaths = ["tests"]
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
"""UnitySVC Python SDK — customer-facing tools for UnitySVC.
|
|
2
|
+
|
|
3
|
+
This package provides:
|
|
4
|
+
|
|
5
|
+
- The :class:`Client` / :class:`AsyncClient` HTTP SDK targeting the
|
|
6
|
+
customer-tagged ``/v1/customer/*`` endpoints (aliases, recurrent
|
|
7
|
+
requests, secrets).
|
|
8
|
+
- The ``usvc`` CLI for managing the same resources from the terminal.
|
|
9
|
+
|
|
10
|
+
Quick start::
|
|
11
|
+
|
|
12
|
+
from unitysvc_py import Client
|
|
13
|
+
|
|
14
|
+
client = Client(api_key="svcpass_...")
|
|
15
|
+
secrets = client.secrets.list()
|
|
16
|
+
|
|
17
|
+
The customer context is encoded entirely in the API key, so no
|
|
18
|
+
separate ``customer_id`` is required. The default base URL points at
|
|
19
|
+
the staging environment; override with the ``base_url`` constructor
|
|
20
|
+
argument or the ``UNITYSVC_API_URL`` env var.
|
|
21
|
+
"""
|
|
22
|
+
|
|
23
|
+
from .aclient import AsyncClient
|
|
24
|
+
from .client import (
|
|
25
|
+
DEFAULT_API_URL,
|
|
26
|
+
ENV_API_BASE_URL,
|
|
27
|
+
ENV_API_KEY,
|
|
28
|
+
ENV_API_URL,
|
|
29
|
+
ENV_S3_BASE_URL,
|
|
30
|
+
ENV_SMTP_BASE_URL,
|
|
31
|
+
Client,
|
|
32
|
+
)
|
|
33
|
+
from .exceptions import (
|
|
34
|
+
APIError,
|
|
35
|
+
AuthenticationError,
|
|
36
|
+
ConflictError,
|
|
37
|
+
NotFoundError,
|
|
38
|
+
PermissionError,
|
|
39
|
+
RateLimitError,
|
|
40
|
+
ServerError,
|
|
41
|
+
UnitysvcSDKError,
|
|
42
|
+
ValidationError,
|
|
43
|
+
)
|
|
44
|
+
|
|
45
|
+
__author__ = """Bo Peng"""
|
|
46
|
+
__email__ = "bo.peng@unitysvc.com"
|
|
47
|
+
|
|
48
|
+
__all__ = [
|
|
49
|
+
# Client
|
|
50
|
+
"Client",
|
|
51
|
+
"AsyncClient",
|
|
52
|
+
"DEFAULT_API_URL",
|
|
53
|
+
"ENV_API_KEY",
|
|
54
|
+
"ENV_API_URL",
|
|
55
|
+
"ENV_API_BASE_URL",
|
|
56
|
+
"ENV_S3_BASE_URL",
|
|
57
|
+
"ENV_SMTP_BASE_URL",
|
|
58
|
+
# Exceptions
|
|
59
|
+
"UnitysvcSDKError",
|
|
60
|
+
"APIError",
|
|
61
|
+
"AuthenticationError",
|
|
62
|
+
"PermissionError",
|
|
63
|
+
"NotFoundError",
|
|
64
|
+
"ValidationError",
|
|
65
|
+
"ConflictError",
|
|
66
|
+
"RateLimitError",
|
|
67
|
+
"ServerError",
|
|
68
|
+
]
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
""" Contains methods for accessing the API """
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
""" Contains endpoint functions for accessing the API """
|