3lc-compute-plugin-sdk 0.2.3__tar.gz → 0.3.1__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.
- {3lc_compute_plugin_sdk-0.2.3 → 3lc_compute_plugin_sdk-0.3.1}/.github/workflows/release.yml +27 -1
- 3lc_compute_plugin_sdk-0.3.1/CHANGELOG.md +189 -0
- {3lc_compute_plugin_sdk-0.2.3 → 3lc_compute_plugin_sdk-0.3.1}/CLAUDE.md +22 -20
- {3lc_compute_plugin_sdk-0.2.3 → 3lc_compute_plugin_sdk-0.3.1}/PKG-INFO +20 -16
- {3lc_compute_plugin_sdk-0.2.3 → 3lc_compute_plugin_sdk-0.3.1}/README.md +18 -14
- {3lc_compute_plugin_sdk-0.2.3 → 3lc_compute_plugin_sdk-0.3.1}/docs/api.md +7 -13
- {3lc_compute_plugin_sdk-0.2.3 → 3lc_compute_plugin_sdk-0.3.1}/docs/plugin-guide.md +99 -45
- {3lc_compute_plugin_sdk-0.2.3 → 3lc_compute_plugin_sdk-0.3.1}/pyproject.toml +19 -24
- 3lc_compute_plugin_sdk-0.3.1/src/tlc_plugin_sdk/__init__.py +42 -0
- {3lc_compute_plugin_sdk-0.2.3 → 3lc_compute_plugin_sdk-0.3.1}/src/tlc_plugin_sdk/asgi_app.py +17 -9
- {3lc_compute_plugin_sdk-0.2.3 → 3lc_compute_plugin_sdk-0.3.1}/src/tlc_plugin_sdk/contract/plugin-api.d.ts +105 -27
- {3lc_compute_plugin_sdk-0.2.3 → 3lc_compute_plugin_sdk-0.3.1}/src/tlc_plugin_sdk/contract.py +29 -24
- {3lc_compute_plugin_sdk-0.2.3 → 3lc_compute_plugin_sdk-0.3.1}/src/tlc_plugin_sdk/job_context.py +48 -10
- {3lc_compute_plugin_sdk-0.2.3 → 3lc_compute_plugin_sdk-0.3.1}/src/tlc_plugin_sdk/shared/data_source_routes.py +79 -6
- {3lc_compute_plugin_sdk-0.2.3 → 3lc_compute_plugin_sdk-0.3.1}/src/tlc_plugin_sdk/shared/data_source_ui.py +35 -4
- {3lc_compute_plugin_sdk-0.2.3 → 3lc_compute_plugin_sdk-0.3.1}/src/tlc_plugin_sdk/shared/job_tracker.py +22 -2
- {3lc_compute_plugin_sdk-0.2.3 → 3lc_compute_plugin_sdk-0.3.1}/src/tlc_plugin_sdk/shared/labels.py +11 -9
- {3lc_compute_plugin_sdk-0.2.3 → 3lc_compute_plugin_sdk-0.3.1}/src/tlc_plugin_sdk/shared/modality.py +7 -27
- {3lc_compute_plugin_sdk-0.2.3 → 3lc_compute_plugin_sdk-0.3.1}/src/tlc_plugin_sdk/worker.py +5 -2
- {3lc_compute_plugin_sdk-0.2.3 → 3lc_compute_plugin_sdk-0.3.1}/tests/test_data_source_routes.py +73 -0
- {3lc_compute_plugin_sdk-0.2.3 → 3lc_compute_plugin_sdk-0.3.1}/tests/test_import_light.py +2 -2
- 3lc_compute_plugin_sdk-0.3.1/tests/test_job_context.py +139 -0
- 3lc_compute_plugin_sdk-0.3.1/tests/test_js_contract_surface.py +50 -0
- {3lc_compute_plugin_sdk-0.2.3 → 3lc_compute_plugin_sdk-0.3.1}/uv.lock +3 -7
- 3lc_compute_plugin_sdk-0.2.3/CHANGELOG.md +0 -87
- 3lc_compute_plugin_sdk-0.2.3/src/tlc_plugin_sdk/__init__.py +0 -58
- {3lc_compute_plugin_sdk-0.2.3 → 3lc_compute_plugin_sdk-0.3.1}/.github/workflows/ci.yml +0 -0
- {3lc_compute_plugin_sdk-0.2.3 → 3lc_compute_plugin_sdk-0.3.1}/.github/workflows/docs.yml +0 -0
- {3lc_compute_plugin_sdk-0.2.3 → 3lc_compute_plugin_sdk-0.3.1}/.gitignore +0 -0
- {3lc_compute_plugin_sdk-0.2.3 → 3lc_compute_plugin_sdk-0.3.1}/LICENSE +0 -0
- {3lc_compute_plugin_sdk-0.2.3 → 3lc_compute_plugin_sdk-0.3.1}/docs/conf.py +0 -0
- {3lc_compute_plugin_sdk-0.2.3 → 3lc_compute_plugin_sdk-0.3.1}/docs/index.md +0 -0
- {3lc_compute_plugin_sdk-0.2.3 → 3lc_compute_plugin_sdk-0.3.1}/docs/package-lock.json +0 -0
- {3lc_compute_plugin_sdk-0.2.3 → 3lc_compute_plugin_sdk-0.3.1}/docs/package.json +0 -0
- {3lc_compute_plugin_sdk-0.2.3 → 3lc_compute_plugin_sdk-0.3.1}/docs/requirements.txt +0 -0
- {3lc_compute_plugin_sdk-0.2.3 → 3lc_compute_plugin_sdk-0.3.1}/src/tlc_plugin_sdk/py.typed +0 -0
- {3lc_compute_plugin_sdk-0.2.3 → 3lc_compute_plugin_sdk-0.3.1}/src/tlc_plugin_sdk/shared/__init__.py +0 -0
- {3lc_compute_plugin_sdk-0.2.3 → 3lc_compute_plugin_sdk-0.3.1}/src/tlc_plugin_sdk/shared/alias_override_ui.py +0 -0
- {3lc_compute_plugin_sdk-0.2.3 → 3lc_compute_plugin_sdk-0.3.1}/src/tlc_plugin_sdk/shared/alias_ui.py +0 -0
- {3lc_compute_plugin_sdk-0.2.3 → 3lc_compute_plugin_sdk-0.3.1}/src/tlc_plugin_sdk/shared/aliases.py +0 -0
- {3lc_compute_plugin_sdk-0.2.3 → 3lc_compute_plugin_sdk-0.3.1}/src/tlc_plugin_sdk/shared/config_store.py +0 -0
- {3lc_compute_plugin_sdk-0.2.3 → 3lc_compute_plugin_sdk-0.3.1}/src/tlc_plugin_sdk/shared/config_ui.py +0 -0
- {3lc_compute_plugin_sdk-0.2.3 → 3lc_compute_plugin_sdk-0.3.1}/src/tlc_plugin_sdk/shared/generic_job.py +0 -0
- {3lc_compute_plugin_sdk-0.2.3 → 3lc_compute_plugin_sdk-0.3.1}/src/tlc_plugin_sdk/shared/images.py +0 -0
- {3lc_compute_plugin_sdk-0.2.3 → 3lc_compute_plugin_sdk-0.3.1}/src/tlc_plugin_sdk/shared/model_storage.py +0 -0
- {3lc_compute_plugin_sdk-0.2.3 → 3lc_compute_plugin_sdk-0.3.1}/src/tlc_plugin_sdk/shared/naming.py +0 -0
- {3lc_compute_plugin_sdk-0.2.3 → 3lc_compute_plugin_sdk-0.3.1}/src/tlc_plugin_sdk/shared/ui_inject.py +0 -0
- {3lc_compute_plugin_sdk-0.2.3 → 3lc_compute_plugin_sdk-0.3.1}/src/tlc_plugin_sdk/shared/url_utils.py +0 -0
- {3lc_compute_plugin_sdk-0.2.3 → 3lc_compute_plugin_sdk-0.3.1}/tests/test_url_utils.py +0 -0
- {3lc_compute_plugin_sdk-0.2.3 → 3lc_compute_plugin_sdk-0.3.1}/tests/test_worker_gpu_reclaim.py +0 -0
|
@@ -28,7 +28,7 @@ jobs:
|
|
|
28
28
|
runs-on: ubuntu-latest
|
|
29
29
|
environment: release
|
|
30
30
|
permissions:
|
|
31
|
-
contents:
|
|
31
|
+
contents: write # create the GitHub Release (step below)
|
|
32
32
|
id-token: write # OIDC token for PyPI Trusted Publishing
|
|
33
33
|
steps:
|
|
34
34
|
- uses: actions/checkout@v4
|
|
@@ -53,6 +53,32 @@ jobs:
|
|
|
53
53
|
- name: Publish to PyPI
|
|
54
54
|
run: uv publish --trusted-publishing always
|
|
55
55
|
|
|
56
|
+
# Publish the tagged version's changelog section as a GitHub Release (curated notes,
|
|
57
|
+
# not GitHub's commit list). The Release stays editable after publish for touch-up.
|
|
58
|
+
- name: Extract changelog section
|
|
59
|
+
id: changelog
|
|
60
|
+
run: |
|
|
61
|
+
VER="${GITHUB_REF_NAME#v}"
|
|
62
|
+
awk -v v="$VER" '$0 ~ "^## \\[" v "\\]" {f=1; next} /^## \[/ && f {exit} f' \
|
|
63
|
+
CHANGELOG.md > notes.md
|
|
64
|
+
echo "ver=$VER" >> "$GITHUB_OUTPUT"
|
|
65
|
+
{ echo "notes<<EOF"; cat notes.md; echo EOF; } >> "$GITHUB_OUTPUT"
|
|
66
|
+
|
|
67
|
+
- name: Create GitHub Release
|
|
68
|
+
uses: softprops/action-gh-release@v2
|
|
69
|
+
with:
|
|
70
|
+
tag_name: ${{ github.ref_name }}
|
|
71
|
+
name: ${{ github.ref_name }}
|
|
72
|
+
body: |
|
|
73
|
+
${{ steps.changelog.outputs.notes }}
|
|
74
|
+
|
|
75
|
+
## Installation
|
|
76
|
+
```bash
|
|
77
|
+
pip install 3lc-compute-plugin-sdk==${{ steps.changelog.outputs.ver }}
|
|
78
|
+
```
|
|
79
|
+
draft: false
|
|
80
|
+
generate_release_notes: false
|
|
81
|
+
|
|
56
82
|
# Manual (workflow_dispatch) runs → build-numbered prereleases to the legacy CloudRepo index.
|
|
57
83
|
publish-cloudrepo-prerelease:
|
|
58
84
|
if: github.event_name == 'workflow_dispatch'
|
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to `3lc-compute-plugin-sdk` are documented in this file.
|
|
4
|
+
|
|
5
|
+
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
|
6
|
+
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
|
+
|
|
8
|
+
## [Unreleased]
|
|
9
|
+
|
|
10
|
+
## [0.3.1] - 2026-08-27
|
|
11
|
+
|
|
12
|
+
A patch on the 0.3 line, additive and pin-compatible (`>=0.3.0,<0.4.0` resolves it). The SDK now
|
|
13
|
+
brings the `3lc` data plane as a base dependency, and the shared data-source browse widget gains an
|
|
14
|
+
input/output picker split.
|
|
15
|
+
|
|
16
|
+
### Added
|
|
17
|
+
- The shared data-source browse widget shows a small caption ("Showing files matching:
|
|
18
|
+
`<glob>`") when a field declares an `accept` glob, so users understand why some files in a
|
|
19
|
+
listed folder don't appear as selectable — folders themselves are never glob-filtered.
|
|
20
|
+
|
|
21
|
+
### Changed
|
|
22
|
+
- **`3lc[pandas]` is now a base dependency.** Installing `3lc-compute-plugin-sdk` brings the 3LC
|
|
23
|
+
data plane, so a plugin pins the SDK alone and gets `tlc` and every `shared.*` helper with it.
|
|
24
|
+
The former `[shared]` extra is kept as an empty, deprecated alias — existing
|
|
25
|
+
`3lc-compute-plugin-sdk[shared]` pins keep resolving — and will be removed at 1.0. Drop the
|
|
26
|
+
suffix when you raise your floor to a release carrying this change. Nothing changes at import
|
|
27
|
+
time: `tlc` is still loaded lazily, only by the helpers that use it.
|
|
28
|
+
- **Docs:** clarified `runtime.provision_extra` in the plugin guide — it names the
|
|
29
|
+
optional-dependency group the host installs into your plugin's venv (keeping the base light for
|
|
30
|
+
discovery and letting one distribution carry several plugins), not an "umbrella-only, required"
|
|
31
|
+
key. Restated that isolation is venv-only: every plugin runs from a host-built managed venv,
|
|
32
|
+
with no in-process/host mode and no `.venv` beside the source. No contract or code change.
|
|
33
|
+
- **Data-source browse widget:** the widget now takes a `purpose` config axis (`'input'`, the
|
|
34
|
+
default, or `'output'`), mirrored by a `purpose` query param on `/browse`. Input pickers now
|
|
35
|
+
**hide** folders the compute process cannot open (rather than showing them disabled), so the
|
|
36
|
+
listing only offers places you can actually descend into. Output pickers show **all** folders
|
|
37
|
+
and instead flag the ones that are not writable — dimmed with a lock glyph and a tooltip, but
|
|
38
|
+
still navigable so you can descend to a writable subdirectory — and disable "Select This
|
|
39
|
+
Folder" when the current directory is not writable. In output mode `/browse` returns a
|
|
40
|
+
`writable` flag on each directory entry and for the listed directory itself (computed only in
|
|
41
|
+
output mode; an older host that omits the flag is treated as writable).
|
|
42
|
+
|
|
43
|
+
### Fixed
|
|
44
|
+
- **Data-source browse widget:** folders the compute process cannot actually open (e.g. macOS
|
|
45
|
+
TCC-protected packages like `Photos Library.photoslibrary`) are now marked inaccessible and
|
|
46
|
+
rendered disabled — dimmed, with a lock glyph and a tooltip — instead of listed as navigable
|
|
47
|
+
and then failing with a red "Permission denied" panel when clicked. The `/browse` route probes
|
|
48
|
+
each directory's openability with `os.scandir` (not `os.access`, which TCC packages spuriously
|
|
49
|
+
pass) and tags directory entries with an `accessible` flag; an older host that omits the flag
|
|
50
|
+
keeps working (entries are treated as accessible).
|
|
51
|
+
|
|
52
|
+
## [0.3.0] - 2026-08-25
|
|
53
|
+
|
|
54
|
+
A coordinated contract wave: breaking **for plugin authors** but **not on the wire** — the
|
|
55
|
+
worker→host NDJSON events are unchanged, so a host keeps running a plugin venv still on 0.2.x
|
|
56
|
+
(skew badge only). The whole first-party fleet re-pins `>=0.3.0,<0.4.0` in the same release.
|
|
57
|
+
|
|
58
|
+
### Added
|
|
59
|
+
- `JobContext.fail(message)` raises `JobFailed` (exported from `tlc_plugin_sdk`); the worker
|
|
60
|
+
reports it as the terminal `error` event with the **bare** message, while any other exception
|
|
61
|
+
keeps its `TypeName: …` prefix.
|
|
62
|
+
- `PluginJobs.list(pluginId?)` — `GET {compute}/api/plugins/jobs`, filtered client-side by
|
|
63
|
+
`plugin_id`. Use it to **seed a fragment on mount** from the durable host job list (the
|
|
64
|
+
fragment is torn down on navigation and `job_update` is live-only). Declared in the `.d.ts`.
|
|
65
|
+
- `PluginJobs.connect(namespace)` — warm the namespace socket on mount. `track()`/`on()`
|
|
66
|
+
connect lazily and SocketIO does not replay events to a not-yet-connected client, so a
|
|
67
|
+
fragment listening for custom events from the first second (or re-attaching to a job it did
|
|
68
|
+
not start) calls this first; `run()` already connects for you.
|
|
69
|
+
- The host's `/ui` handler now **auto-injects** the `PluginJobs` client into every fragment; the
|
|
70
|
+
client is idempotent, so a plugin that still injects it by hand keeps working.
|
|
71
|
+
- `ComputePlugin.compute()` ships a default (returns an error dict), so only `get_ui_fragment()`
|
|
72
|
+
stays abstract.
|
|
73
|
+
- `.d.ts` truth pass: `PluginJobUpdate.error`, `PluginFetchOptions.allowErrorStatus`,
|
|
74
|
+
`PluginJobsApi.list`, `TlcLocationApi.rowsSpanLocations`/`shortLabel`, and a new
|
|
75
|
+
**Legacy globals** ambient block (`openTablePicker`/`closeTablePicker`/`CancelJob`/`cssVar`
|
|
76
|
+
and the injected `_tlc*` helpers).
|
|
77
|
+
|
|
78
|
+
### Changed
|
|
79
|
+
- **One contract axis.** `PY_CONTRACT` and `JS_CONTRACT` are removed; `SDK_CONTRACT_VERSION`
|
|
80
|
+
(the package version) is the whole contract, compared on `MAJOR.MINOR`. `/health` reports
|
|
81
|
+
`sdk_version` only (no `py_contract`/`js_contract`).
|
|
82
|
+
- `JobContext.result` takes a **positional** `url` (`ctx.result(url)`); it opens the Open
|
|
83
|
+
button — a run *or* a table URL. The `run_url=` keyword is gone; the emitted `result` event
|
|
84
|
+
still carries `run_url` (wire unchanged).
|
|
85
|
+
- `JobContext.progress` documents `percent=-1` as indeterminate.
|
|
86
|
+
|
|
87
|
+
### Removed
|
|
88
|
+
- `shared.modality.detect_modality_from_url` (zero users). `detect_modality_from_schema`,
|
|
89
|
+
`classify_metrics_columns`, `labels.candidate_label_paths`, and `labels.find_label_path`
|
|
90
|
+
are now internal (underscore-prefixed). Public alternatives: `detect_modality_from_table`
|
|
91
|
+
for modality; `get_label_map` / `get_label_names` / `find_label_column` for labels.
|
|
92
|
+
`classify_metrics_columns` was a plugin-specific column-name heuristic with no public
|
|
93
|
+
replacement — inline it (its one known user did).
|
|
94
|
+
- The orphan `_ICON_SVG` constant; the dead `params.setdefault("url", "")` in the `/compute`
|
|
95
|
+
handler.
|
|
96
|
+
|
|
97
|
+
### Migration
|
|
98
|
+
- `ctx.result(run_url=x)` → `ctx.result(x)`.
|
|
99
|
+
- Validation failures: raise, or `ctx.fail("message")` for a clean user-facing card.
|
|
100
|
+
- Reading `tlc_plugin_sdk.PY_CONTRACT` / `JS_CONTRACT`, or a worker's `/health`
|
|
101
|
+
`py_contract`/`js_contract`: use `SDK_CONTRACT_VERSION` / `sdk_version` and compare on
|
|
102
|
+
`MAJOR.MINOR`.
|
|
103
|
+
- `shared.modality.detect_modality_from_url(url)` → load the table yourself and call
|
|
104
|
+
`detect_modality_from_table(table)`. `classify_metrics_columns(columns)` → copy the
|
|
105
|
+
heuristic into your plugin.
|
|
106
|
+
- A fragment that kept its own always-connected socket for custom events: switch to
|
|
107
|
+
`PluginJobs.on(namespace, event, handler)` and call `PluginJobs.connect(namespace)` on mount
|
|
108
|
+
so nothing emitted before the first `track()` is missed.
|
|
109
|
+
- Fragments may delete their manual `inject_scripts(raw, job_tracker_script())` — the host
|
|
110
|
+
injects `PluginJobs` now (harmless to keep).
|
|
111
|
+
- The manifest never declared the SocketIO namespace or `sdk_version`; both were dead and are
|
|
112
|
+
documented as such. The namespace is host-derived as `/<plugin-id>`.
|
|
113
|
+
|
|
114
|
+
## [0.2.3] - 2026-08-21
|
|
115
|
+
|
|
116
|
+
### Changed
|
|
117
|
+
- Docs: the plugin guide and API docstrings describe the single runtime — a plugin runs in
|
|
118
|
+
its own venv behind a worker; the manifest's `isolation` key accepts only `"venv"` (the
|
|
119
|
+
default when absent). The guide's isolation table, the in-tree host exception callout, and
|
|
120
|
+
the `unload` endpoint reference are gone; the dev-workflow section now documents the
|
|
121
|
+
folder-Source + editable-venv + `worker/stop` loop. No code or contract change.
|
|
122
|
+
|
|
123
|
+
## [0.2.2] - 2026-08-18
|
|
124
|
+
|
|
125
|
+
### Added
|
|
126
|
+
- Shared data-source picker (`shared.data_source_ui`) and its browse/upload route handlers
|
|
127
|
+
(`shared.data_source_routes`): plugins mount one consistent widget for choosing a local
|
|
128
|
+
path or uploading a file, instead of each fragment rolling its own (#11).
|
|
129
|
+
|
|
130
|
+
### Changed
|
|
131
|
+
- **Distribution moved to PyPI**: `3lc-compute-plugin-sdk` is now published to public
|
|
132
|
+
[PyPI](https://pypi.org/project/3lc-compute-plugin-sdk/) via Trusted Publishing; the private
|
|
133
|
+
CloudRepo index (pypi.3lc.ai) is no longer needed to install the SDK. Manual prerelease
|
|
134
|
+
builds keep publishing to CloudRepo for a grace period (#13).
|
|
135
|
+
- The `[shared]` extra's `3lc` dependency resolves from public PyPI (its home since the 3.2
|
|
136
|
+
rust release), so the SDK no longer pins a custom package index (#12).
|
|
137
|
+
|
|
138
|
+
### Security
|
|
139
|
+
- The data-source `/browse` route is confined to operator-configured roots
|
|
140
|
+
(`TLC_DATA_SOURCE_ROOTS`, `os.pathsep`-separated; default: the service user's home).
|
|
141
|
+
Every requested path is realpath-resolved before the containment check, so `..`
|
|
142
|
+
segments and symlinks pointing outside a root are denied rather than followed, and
|
|
143
|
+
the widget's breadcrumb stops at the root instead of offering the whole filesystem (#11).
|
|
144
|
+
- The data-source `/upload-temp` route strips directory components from the
|
|
145
|
+
client-supplied filename (closing a path traversal where a `../…` name chose the
|
|
146
|
+
write location), lands each upload in its own private temp directory so same-named
|
|
147
|
+
uploads never clobber each other, and rejects bodies larger than
|
|
148
|
+
`TLC_DATA_SOURCE_MAX_UPLOAD_MB` (default 512) (#11).
|
|
149
|
+
|
|
150
|
+
## [0.2.1] - 2026-08-13
|
|
151
|
+
|
|
152
|
+
### Added
|
|
153
|
+
- Tilde expansion and local-path normalization helpers in `shared.url_utils`
|
|
154
|
+
(`normalize_local_path`, `normalize_url`): user-supplied paths expand `~` and resolve
|
|
155
|
+
to absolute form at every ingress, so exported files and stored URLs never carry a
|
|
156
|
+
user-relative path (#10).
|
|
157
|
+
|
|
158
|
+
## [0.2.0] - 2026-08-05
|
|
159
|
+
|
|
160
|
+
### Added
|
|
161
|
+
- JS_CONTRACT 0.2: project locations (`TlcLocation`) are available to plugin UI fragments
|
|
162
|
+
through `PLUGIN_API`, so plugins can resolve and present where a project's data lives (#8).
|
|
163
|
+
- The plugin worker reports the SDK contract version from its `/health` endpoint, letting the
|
|
164
|
+
host enforce compatibility floors precisely instead of guessing (#6).
|
|
165
|
+
|
|
166
|
+
### Changed
|
|
167
|
+
- The plugin worker releases cached GPU memory after every job, so a finished GPU job no longer
|
|
168
|
+
pins CUDA memory that other plugins need (#7).
|
|
169
|
+
|
|
170
|
+
## [0.1.1] - 2026-07-03
|
|
171
|
+
|
|
172
|
+
### Changed
|
|
173
|
+
- **Distribution renamed**: `3lc-plugin-sdk` is now published as `3lc-compute-plugin-sdk`
|
|
174
|
+
(the import name `tlc_plugin_sdk` is unchanged). Update your dependency declarations.
|
|
175
|
+
|
|
176
|
+
### Fixed
|
|
177
|
+
- Regenerated `uv.lock` for the renamed distribution; release builds append the run number as a
|
|
178
|
+
build counter.
|
|
179
|
+
|
|
180
|
+
## [0.1.0] - 2026-06-30
|
|
181
|
+
|
|
182
|
+
First public release of the plugin SDK for the 3LC compute service.
|
|
183
|
+
|
|
184
|
+
### Added
|
|
185
|
+
- The plugin contract: `ComputePlugin`, `JobContext`, and the `PLUGIN_API` browser bridge
|
|
186
|
+
(JS_CONTRACT) that plugin UI fragments use to talk to the host.
|
|
187
|
+
- Slim base package with a `[shared]` extra for common plugin dependencies.
|
|
188
|
+
- Apache-2.0 license, public CI, and a documentation site including the plugin author guide and
|
|
189
|
+
a rendered browser-contract reference.
|
|
@@ -18,16 +18,16 @@ in a separate repository and is **not** a dependency of this SDK.
|
|
|
18
18
|
host-side). The SDK is the root of the dependency graph; the host depends on the SDK, never the
|
|
19
19
|
reverse. The whole point — a plugin built against just this wheel runs in its own isolated venv.
|
|
20
20
|
2. **Import-light.** Importing `tlc_plugin_sdk` must not eagerly pull the server stack *or the data
|
|
21
|
-
plane*. `litestar` and `
|
|
22
|
-
|
|
23
|
-
host-only SocketIO server must not be importable from here at all.
|
|
24
|
-
enforces this (guards `litestar`/`socketio`/`uvicorn`/`tlc`) —
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
21
|
+
plane*. `litestar`, `uvicorn`, and `tlc` are all base deps but are imported **lazily** — the
|
|
22
|
+
server stack only when a worker actually serves, `tlc` only inside the `shared.*` helpers that
|
|
23
|
+
use it; and the host-only SocketIO server must not be importable from here at all.
|
|
24
|
+
`tests/test_import_light.py` enforces this (guards `litestar`/`socketio`/`uvicorn`/`tlc`) —
|
|
25
|
+
keep it green. Import-light is about worker startup time, not about what is installed.
|
|
26
|
+
3. **Dependencies stay minimal.** Base = `uvicorn` + `litestar` + `3lc[pandas]`, nothing else.
|
|
27
|
+
`3lc` is deliberately a base dep, not an extra: every real plugin uses the data plane, and making
|
|
28
|
+
it optional only created a way to build a venv that imports fine and fails on first use. (The
|
|
29
|
+
`[shared]` extra it used to live behind is an empty, deprecated alias — gone at 1.0.) Adding a
|
|
30
|
+
base dep widens what every plugin venv must install — justify it.
|
|
31
31
|
4. **The contract is published — every public symbol is forever-ish.** The 0.x line is
|
|
32
32
|
additive-only (see README → Status): additions are the safe move; reshaping
|
|
33
33
|
`JobContext`/`ComputePlugin` waits for a major bump. The version is the contract version
|
|
@@ -50,9 +50,8 @@ and how to port an existing plugin.
|
|
|
50
50
|
|
|
51
51
|
## Dev setup
|
|
52
52
|
|
|
53
|
-
`3lc` resolves from
|
|
54
|
-
|
|
55
|
-
path source.
|
|
53
|
+
`3lc` resolves from public PyPI. To develop against an unreleased `3lc`, override locally
|
|
54
|
+
(uncommitted) with an editable path source in `[tool.uv.sources]`.
|
|
56
55
|
|
|
57
56
|
```bash
|
|
58
57
|
uv sync # installs the SDK + dev tools (ruff, mypy, pytest)
|
|
@@ -83,10 +82,11 @@ section is the condensed working order.
|
|
|
83
82
|
- **Config + GPU job + SocketIO**: sam3, yolo, or timm
|
|
84
83
|
- **Inline data display (no standalone page)**: `tlc_plugin_table_statistics`
|
|
85
84
|
- **Action on selected resources**: `tlc_plugin_merger`
|
|
86
|
-
3. Read `src/tlc_plugin_sdk/contract.py` — `ComputePlugin` is an `abc.ABC` with
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
`get_route_handlers`) ship as
|
|
85
|
+
3. Read `src/tlc_plugin_sdk/contract.py` — `ComputePlugin` is an `abc.ABC` with one abstract
|
|
86
|
+
method (`get_ui_fragment()`) and an `id` attribute the host stamps from the manifest.
|
|
87
|
+
`compute()` and the other hooks (`run_job`, `initialise_runtime`, `shutdown_runtime`,
|
|
88
|
+
`get_route_handlers`) ship as defaults (`compute()` reports that the plugin doesn't
|
|
89
|
+
implement it), so you override only what you need. There is
|
|
90
90
|
no `register()` and **no** `get_active_jobs`/`cancel_job` — the host owns job listing and
|
|
91
91
|
cancellation.
|
|
92
92
|
4. Read `tlc_plugin_exporter`'s `plugin.toml` + `__init__.py` — the canonical manifest +
|
|
@@ -154,9 +154,11 @@ section is the condensed working order.
|
|
|
154
154
|
form fills call `_tlcSyncAliasFromForm(prefix, projectId, folderId)`.
|
|
155
155
|
|
|
156
156
|
**SocketIO** (if real-time updates are needed):
|
|
157
|
-
-
|
|
158
|
-
|
|
159
|
-
- Prefer the `window.PluginJobs` client over a hand-rolled socket.
|
|
157
|
+
- The namespace is host-derived as `/<plugin-id>` and auto-registered at startup; the manifest
|
|
158
|
+
does not (and never did) declare it.
|
|
159
|
+
- Prefer the `window.PluginJobs` client over a hand-rolled socket. It connects a namespace
|
|
160
|
+
lazily; call `PluginJobs.connect('/<plugin-id>')` on mount if the fragment needs custom
|
|
161
|
+
events from the first second (`run()` connects for you).
|
|
160
162
|
- Use `ctx.emit(name, payload)` only for telemetry the generic schema can't express
|
|
161
163
|
(e.g. a loss curve); never re-emit the generic lifecycle by hand.
|
|
162
164
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: 3lc-compute-plugin-sdk
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.3.1
|
|
4
4
|
Summary: Public Python SDK for building 3LC compute-service plugins.
|
|
5
5
|
Project-URL: Homepage, https://github.com/3lc-ai/3lc-compute-plugin-sdk
|
|
6
6
|
Project-URL: Repository, https://github.com/3lc-ai/3lc-compute-plugin-sdk
|
|
@@ -18,10 +18,10 @@ Classifier: Programming Language :: Python :: 3.11
|
|
|
18
18
|
Classifier: Programming Language :: Python :: 3.12
|
|
19
19
|
Classifier: Typing :: Typed
|
|
20
20
|
Requires-Python: >=3.10
|
|
21
|
+
Requires-Dist: 3lc[pandas]<4.0.0,>=3.0.0
|
|
21
22
|
Requires-Dist: litestar<3.0.0,>=2.22.0
|
|
22
23
|
Requires-Dist: uvicorn>=0.34.0
|
|
23
24
|
Provides-Extra: shared
|
|
24
|
-
Requires-Dist: 3lc[pandas]<4.0.0,>=3.0.0; extra == 'shared'
|
|
25
25
|
Description-Content-Type: text/markdown
|
|
26
26
|
|
|
27
27
|
# 3lc-compute-plugin-sdk
|
|
@@ -44,9 +44,10 @@ pip install 3lc-compute-plugin-sdk # import name: tlc_plugin_sdk
|
|
|
44
44
|
|
|
45
45
|
## What it gives you
|
|
46
46
|
|
|
47
|
-
- **`ComputePlugin`** — the base class you subclass. Implement `
|
|
48
|
-
job and lifecycle hooks ship as
|
|
49
|
-
metadata lives in its `
|
|
47
|
+
- **`ComputePlugin`** — the base class you subclass. Implement `get_ui_fragment` (and `compute`
|
|
48
|
+
if you expose a synchronous endpoint); job and lifecycle hooks ship as safe defaults. There is
|
|
49
|
+
no `register()` to call — a plugin's metadata lives in its `plugin.toml` manifest (or a
|
|
50
|
+
`[tool.tlc-compute]` table in `pyproject.toml`), and the Hub discovers it from there.
|
|
50
51
|
- **`JobContext`** — the surface a long-running job programs against: `progress` / `metric` /
|
|
51
52
|
`log` / `result` for the generic job panel, `emit` for your plugin's own rich UI, and
|
|
52
53
|
cooperative `cancelled`.
|
|
@@ -63,23 +64,26 @@ format, custom routes, the job model, UI fragment, checklist).
|
|
|
63
64
|
|
|
64
65
|
## The contract version
|
|
65
66
|
|
|
66
|
-
`tlc_plugin_sdk.SDK_CONTRACT_VERSION` is this package's own version — one source of truth
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
67
|
+
`tlc_plugin_sdk.SDK_CONTRACT_VERSION` is this package's own version — one source of truth, one
|
|
68
|
+
contract axis (both the Python and the browser surface). A plugin pins the SDK
|
|
69
|
+
(`3lc-compute-plugin-sdk>=X,<Y`); the compute service and frontend implement a range and compare
|
|
70
|
+
compatibility on `MAJOR.MINOR`. Versions are SemVer; see **Status** below for the 0.x stability
|
|
70
71
|
stance.
|
|
71
72
|
|
|
72
73
|
## How it fits
|
|
73
74
|
|
|
74
|
-
This SDK sits at the root of the plugin dependency graph. Its
|
|
75
|
-
`
|
|
76
|
-
|
|
77
|
-
on the compute service itself or on any plugin.
|
|
78
|
-
therefore portable across service versions and
|
|
75
|
+
This SDK sits at the root of the plugin dependency graph. Its dependencies are `uvicorn`,
|
|
76
|
+
`litestar`, and the `3lc` data plane (`3lc[pandas]`) — so installing the SDK is all a plugin needs
|
|
77
|
+
to use `tlc` and the `shared.*` helpers built on it. All three are imported lazily; `import
|
|
78
|
+
tlc_plugin_sdk` stays cheap. It **never** depends on the compute service itself or on any plugin.
|
|
79
|
+
A plugin built against `tlc_plugin_sdk` alone is therefore portable across service versions and
|
|
80
|
+
runs in its own isolated environment.
|
|
79
81
|
|
|
80
82
|
## Status
|
|
81
83
|
|
|
82
|
-
**0.
|
|
83
|
-
|
|
84
|
+
**0.3 is the current contract line.** Within 0.x the contract still evolves — mostly additively,
|
|
85
|
+
but a coordinated breaking change may land on a MINOR bump while the fleet re-pins in lockstep (0.3
|
|
86
|
+
did: `JobContext.result` took a positional `url`, and the `PY_CONTRACT`/`JS_CONTRACT` axes
|
|
87
|
+
collapsed into `SDK_CONTRACT_VERSION`). Anything reshaping the core still waits for a major bump. In
|
|
84
88
|
the browser bridge, `PLUGIN_API.libs.io` is a stable part of the contract; the other bundled
|
|
85
89
|
libs are best-effort (see the guide).
|
|
@@ -18,9 +18,10 @@ pip install 3lc-compute-plugin-sdk # import name: tlc_plugin_sdk
|
|
|
18
18
|
|
|
19
19
|
## What it gives you
|
|
20
20
|
|
|
21
|
-
- **`ComputePlugin`** — the base class you subclass. Implement `
|
|
22
|
-
job and lifecycle hooks ship as
|
|
23
|
-
metadata lives in its `
|
|
21
|
+
- **`ComputePlugin`** — the base class you subclass. Implement `get_ui_fragment` (and `compute`
|
|
22
|
+
if you expose a synchronous endpoint); job and lifecycle hooks ship as safe defaults. There is
|
|
23
|
+
no `register()` to call — a plugin's metadata lives in its `plugin.toml` manifest (or a
|
|
24
|
+
`[tool.tlc-compute]` table in `pyproject.toml`), and the Hub discovers it from there.
|
|
24
25
|
- **`JobContext`** — the surface a long-running job programs against: `progress` / `metric` /
|
|
25
26
|
`log` / `result` for the generic job panel, `emit` for your plugin's own rich UI, and
|
|
26
27
|
cooperative `cancelled`.
|
|
@@ -37,23 +38,26 @@ format, custom routes, the job model, UI fragment, checklist).
|
|
|
37
38
|
|
|
38
39
|
## The contract version
|
|
39
40
|
|
|
40
|
-
`tlc_plugin_sdk.SDK_CONTRACT_VERSION` is this package's own version — one source of truth
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
41
|
+
`tlc_plugin_sdk.SDK_CONTRACT_VERSION` is this package's own version — one source of truth, one
|
|
42
|
+
contract axis (both the Python and the browser surface). A plugin pins the SDK
|
|
43
|
+
(`3lc-compute-plugin-sdk>=X,<Y`); the compute service and frontend implement a range and compare
|
|
44
|
+
compatibility on `MAJOR.MINOR`. Versions are SemVer; see **Status** below for the 0.x stability
|
|
44
45
|
stance.
|
|
45
46
|
|
|
46
47
|
## How it fits
|
|
47
48
|
|
|
48
|
-
This SDK sits at the root of the plugin dependency graph. Its
|
|
49
|
-
`
|
|
50
|
-
|
|
51
|
-
on the compute service itself or on any plugin.
|
|
52
|
-
therefore portable across service versions and
|
|
49
|
+
This SDK sits at the root of the plugin dependency graph. Its dependencies are `uvicorn`,
|
|
50
|
+
`litestar`, and the `3lc` data plane (`3lc[pandas]`) — so installing the SDK is all a plugin needs
|
|
51
|
+
to use `tlc` and the `shared.*` helpers built on it. All three are imported lazily; `import
|
|
52
|
+
tlc_plugin_sdk` stays cheap. It **never** depends on the compute service itself or on any plugin.
|
|
53
|
+
A plugin built against `tlc_plugin_sdk` alone is therefore portable across service versions and
|
|
54
|
+
runs in its own isolated environment.
|
|
53
55
|
|
|
54
56
|
## Status
|
|
55
57
|
|
|
56
|
-
**0.
|
|
57
|
-
|
|
58
|
+
**0.3 is the current contract line.** Within 0.x the contract still evolves — mostly additively,
|
|
59
|
+
but a coordinated breaking change may land on a MINOR bump while the fleet re-pins in lockstep (0.3
|
|
60
|
+
did: `JobContext.result` took a positional `url`, and the `PY_CONTRACT`/`JS_CONTRACT` axes
|
|
61
|
+
collapsed into `SDK_CONTRACT_VERSION`). Anything reshaping the core still waits for a major bump. In
|
|
58
62
|
the browser bridge, `PLUGIN_API.libs.io` is a stable part of the contract; the other bundled
|
|
59
63
|
libs are best-effort (see the guide).
|
|
@@ -13,29 +13,23 @@ package's own docstrings.
|
|
|
13
13
|
:members:
|
|
14
14
|
|
|
15
15
|
.. autodata:: tlc_plugin_sdk.SDK_CONTRACT_VERSION
|
|
16
|
-
|
|
17
|
-
.. autodata:: tlc_plugin_sdk.PY_CONTRACT
|
|
18
|
-
|
|
19
|
-
.. autodata:: tlc_plugin_sdk.JS_CONTRACT
|
|
20
16
|
```
|
|
21
17
|
|
|
22
|
-
`SDK_CONTRACT_VERSION` is the
|
|
23
|
-
against (`3lc-compute-plugin-sdk>=X,<Y`)
|
|
24
|
-
|
|
25
|
-
surface (`ComputePlugin` / `JobContext`), `JS_CONTRACT` the browser surface
|
|
18
|
+
`SDK_CONTRACT_VERSION` is the one contract axis: this package's own version — the
|
|
19
|
+
dependency *pin* a plugin resolves against (`3lc-compute-plugin-sdk>=X,<Y`) and the version
|
|
20
|
+
of both the Python surface (`ComputePlugin` / `JobContext`) and the browser surface
|
|
26
21
|
(`PLUGIN_API` / `PluginJobs` / `TlcData`, declared in
|
|
27
|
-
`tlc_plugin_sdk/contract/plugin-api.d.ts`).
|
|
28
|
-
|
|
22
|
+
`tlc_plugin_sdk/contract/plugin-api.d.ts`). The host and frontend implement a range and
|
|
23
|
+
compare compatibility on `MAJOR.MINOR`. See `docs/plugin-guide.md` → "Version & Compatibility".
|
|
29
24
|
|
|
30
25
|
The `.d.ts` ships in the wheel at
|
|
31
26
|
`<site-packages>/tlc_plugin_sdk/contract/plugin-api.d.ts`; a plain-JS `ui.html` references
|
|
32
27
|
it with `/// <reference types="3lc-compute-plugin-sdk/contract/plugin-api" />`.
|
|
33
28
|
|
|
34
|
-
## The browser contract
|
|
29
|
+
## The browser contract
|
|
35
30
|
|
|
36
31
|
The browser surface a plugin's `ui.html` programs against — `PLUGIN_API`, `PluginJobs`,
|
|
37
|
-
and the `TlcData` helper — is declared in `plugin-api.d.ts
|
|
38
|
-
`JS_CONTRACT`.
|
|
32
|
+
and the `TlcData` helper — is declared in `plugin-api.d.ts`.
|
|
39
33
|
|
|
40
34
|
```{eval-rst}
|
|
41
35
|
.. automodule documents every exported symbol in the file, so new contract
|