3lc-compute-plugin-sdk 0.2.2__tar.gz → 0.3.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.
Files changed (50) hide show
  1. {3lc_compute_plugin_sdk-0.2.2 → 3lc_compute_plugin_sdk-0.3.0}/.github/workflows/release.yml +27 -1
  2. 3lc_compute_plugin_sdk-0.3.0/CHANGELOG.md +149 -0
  3. {3lc_compute_plugin_sdk-0.2.2 → 3lc_compute_plugin_sdk-0.3.0}/CLAUDE.md +10 -7
  4. {3lc_compute_plugin_sdk-0.2.2 → 3lc_compute_plugin_sdk-0.3.0}/PKG-INFO +23 -17
  5. {3lc_compute_plugin_sdk-0.2.2 → 3lc_compute_plugin_sdk-0.3.0}/README.md +21 -15
  6. {3lc_compute_plugin_sdk-0.2.2 → 3lc_compute_plugin_sdk-0.3.0}/docs/api.md +7 -13
  7. {3lc_compute_plugin_sdk-0.2.2 → 3lc_compute_plugin_sdk-0.3.0}/docs/index.md +1 -1
  8. {3lc_compute_plugin_sdk-0.2.2 → 3lc_compute_plugin_sdk-0.3.0}/docs/plugin-guide.md +139 -107
  9. {3lc_compute_plugin_sdk-0.2.2 → 3lc_compute_plugin_sdk-0.3.0}/pyproject.toml +2 -2
  10. 3lc_compute_plugin_sdk-0.3.0/src/tlc_plugin_sdk/__init__.py +42 -0
  11. {3lc_compute_plugin_sdk-0.2.2 → 3lc_compute_plugin_sdk-0.3.0}/src/tlc_plugin_sdk/asgi_app.py +27 -27
  12. {3lc_compute_plugin_sdk-0.2.2 → 3lc_compute_plugin_sdk-0.3.0}/src/tlc_plugin_sdk/contract/plugin-api.d.ts +105 -27
  13. {3lc_compute_plugin_sdk-0.2.2 → 3lc_compute_plugin_sdk-0.3.0}/src/tlc_plugin_sdk/contract.py +31 -27
  14. {3lc_compute_plugin_sdk-0.2.2 → 3lc_compute_plugin_sdk-0.3.0}/src/tlc_plugin_sdk/job_context.py +54 -21
  15. {3lc_compute_plugin_sdk-0.2.2 → 3lc_compute_plugin_sdk-0.3.0}/src/tlc_plugin_sdk/shared/job_tracker.py +22 -2
  16. {3lc_compute_plugin_sdk-0.2.2 → 3lc_compute_plugin_sdk-0.3.0}/src/tlc_plugin_sdk/shared/labels.py +11 -9
  17. {3lc_compute_plugin_sdk-0.2.2 → 3lc_compute_plugin_sdk-0.3.0}/src/tlc_plugin_sdk/shared/modality.py +7 -27
  18. {3lc_compute_plugin_sdk-0.2.2 → 3lc_compute_plugin_sdk-0.3.0}/src/tlc_plugin_sdk/worker.py +10 -7
  19. 3lc_compute_plugin_sdk-0.3.0/tests/test_job_context.py +139 -0
  20. 3lc_compute_plugin_sdk-0.3.0/tests/test_js_contract_surface.py +50 -0
  21. {3lc_compute_plugin_sdk-0.2.2 → 3lc_compute_plugin_sdk-0.3.0}/uv.lock +118 -118
  22. 3lc_compute_plugin_sdk-0.2.2/CHANGELOG.md +0 -78
  23. 3lc_compute_plugin_sdk-0.2.2/src/tlc_plugin_sdk/__init__.py +0 -58
  24. {3lc_compute_plugin_sdk-0.2.2 → 3lc_compute_plugin_sdk-0.3.0}/.github/workflows/ci.yml +0 -0
  25. {3lc_compute_plugin_sdk-0.2.2 → 3lc_compute_plugin_sdk-0.3.0}/.github/workflows/docs.yml +0 -0
  26. {3lc_compute_plugin_sdk-0.2.2 → 3lc_compute_plugin_sdk-0.3.0}/.gitignore +0 -0
  27. {3lc_compute_plugin_sdk-0.2.2 → 3lc_compute_plugin_sdk-0.3.0}/LICENSE +0 -0
  28. {3lc_compute_plugin_sdk-0.2.2 → 3lc_compute_plugin_sdk-0.3.0}/docs/conf.py +0 -0
  29. {3lc_compute_plugin_sdk-0.2.2 → 3lc_compute_plugin_sdk-0.3.0}/docs/package-lock.json +0 -0
  30. {3lc_compute_plugin_sdk-0.2.2 → 3lc_compute_plugin_sdk-0.3.0}/docs/package.json +0 -0
  31. {3lc_compute_plugin_sdk-0.2.2 → 3lc_compute_plugin_sdk-0.3.0}/docs/requirements.txt +0 -0
  32. {3lc_compute_plugin_sdk-0.2.2 → 3lc_compute_plugin_sdk-0.3.0}/src/tlc_plugin_sdk/py.typed +0 -0
  33. {3lc_compute_plugin_sdk-0.2.2 → 3lc_compute_plugin_sdk-0.3.0}/src/tlc_plugin_sdk/shared/__init__.py +0 -0
  34. {3lc_compute_plugin_sdk-0.2.2 → 3lc_compute_plugin_sdk-0.3.0}/src/tlc_plugin_sdk/shared/alias_override_ui.py +0 -0
  35. {3lc_compute_plugin_sdk-0.2.2 → 3lc_compute_plugin_sdk-0.3.0}/src/tlc_plugin_sdk/shared/alias_ui.py +0 -0
  36. {3lc_compute_plugin_sdk-0.2.2 → 3lc_compute_plugin_sdk-0.3.0}/src/tlc_plugin_sdk/shared/aliases.py +0 -0
  37. {3lc_compute_plugin_sdk-0.2.2 → 3lc_compute_plugin_sdk-0.3.0}/src/tlc_plugin_sdk/shared/config_store.py +0 -0
  38. {3lc_compute_plugin_sdk-0.2.2 → 3lc_compute_plugin_sdk-0.3.0}/src/tlc_plugin_sdk/shared/config_ui.py +0 -0
  39. {3lc_compute_plugin_sdk-0.2.2 → 3lc_compute_plugin_sdk-0.3.0}/src/tlc_plugin_sdk/shared/data_source_routes.py +0 -0
  40. {3lc_compute_plugin_sdk-0.2.2 → 3lc_compute_plugin_sdk-0.3.0}/src/tlc_plugin_sdk/shared/data_source_ui.py +0 -0
  41. {3lc_compute_plugin_sdk-0.2.2 → 3lc_compute_plugin_sdk-0.3.0}/src/tlc_plugin_sdk/shared/generic_job.py +0 -0
  42. {3lc_compute_plugin_sdk-0.2.2 → 3lc_compute_plugin_sdk-0.3.0}/src/tlc_plugin_sdk/shared/images.py +0 -0
  43. {3lc_compute_plugin_sdk-0.2.2 → 3lc_compute_plugin_sdk-0.3.0}/src/tlc_plugin_sdk/shared/model_storage.py +0 -0
  44. {3lc_compute_plugin_sdk-0.2.2 → 3lc_compute_plugin_sdk-0.3.0}/src/tlc_plugin_sdk/shared/naming.py +0 -0
  45. {3lc_compute_plugin_sdk-0.2.2 → 3lc_compute_plugin_sdk-0.3.0}/src/tlc_plugin_sdk/shared/ui_inject.py +0 -0
  46. {3lc_compute_plugin_sdk-0.2.2 → 3lc_compute_plugin_sdk-0.3.0}/src/tlc_plugin_sdk/shared/url_utils.py +0 -0
  47. {3lc_compute_plugin_sdk-0.2.2 → 3lc_compute_plugin_sdk-0.3.0}/tests/test_data_source_routes.py +0 -0
  48. {3lc_compute_plugin_sdk-0.2.2 → 3lc_compute_plugin_sdk-0.3.0}/tests/test_import_light.py +0 -0
  49. {3lc_compute_plugin_sdk-0.2.2 → 3lc_compute_plugin_sdk-0.3.0}/tests/test_url_utils.py +0 -0
  50. {3lc_compute_plugin_sdk-0.2.2 → 3lc_compute_plugin_sdk-0.3.0}/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: read
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,149 @@
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
+ Nothing yet.
11
+
12
+ ## [0.3.0] - 2026-08-25
13
+
14
+ A coordinated contract wave: breaking **for plugin authors** but **not on the wire** — the
15
+ worker→host NDJSON events are unchanged, so a host keeps running a plugin venv still on 0.2.x
16
+ (skew badge only). The whole first-party fleet re-pins `>=0.3.0,<0.4.0` in the same release.
17
+
18
+ ### Added
19
+ - `JobContext.fail(message)` raises `JobFailed` (exported from `tlc_plugin_sdk`); the worker
20
+ reports it as the terminal `error` event with the **bare** message, while any other exception
21
+ keeps its `TypeName: …` prefix.
22
+ - `PluginJobs.list(pluginId?)` — `GET {compute}/api/plugins/jobs`, filtered client-side by
23
+ `plugin_id`. Use it to **seed a fragment on mount** from the durable host job list (the
24
+ fragment is torn down on navigation and `job_update` is live-only). Declared in the `.d.ts`.
25
+ - `PluginJobs.connect(namespace)` — warm the namespace socket on mount. `track()`/`on()`
26
+ connect lazily and SocketIO does not replay events to a not-yet-connected client, so a
27
+ fragment listening for custom events from the first second (or re-attaching to a job it did
28
+ not start) calls this first; `run()` already connects for you.
29
+ - The host's `/ui` handler now **auto-injects** the `PluginJobs` client into every fragment; the
30
+ client is idempotent, so a plugin that still injects it by hand keeps working.
31
+ - `ComputePlugin.compute()` ships a default (returns an error dict), so only `get_ui_fragment()`
32
+ stays abstract.
33
+ - `.d.ts` truth pass: `PluginJobUpdate.error`, `PluginFetchOptions.allowErrorStatus`,
34
+ `PluginJobsApi.list`, `TlcLocationApi.rowsSpanLocations`/`shortLabel`, and a new
35
+ **Legacy globals** ambient block (`openTablePicker`/`closeTablePicker`/`CancelJob`/`cssVar`
36
+ and the injected `_tlc*` helpers).
37
+
38
+ ### Changed
39
+ - **One contract axis.** `PY_CONTRACT` and `JS_CONTRACT` are removed; `SDK_CONTRACT_VERSION`
40
+ (the package version) is the whole contract, compared on `MAJOR.MINOR`. `/health` reports
41
+ `sdk_version` only (no `py_contract`/`js_contract`).
42
+ - `JobContext.result` takes a **positional** `url` (`ctx.result(url)`); it opens the Open
43
+ button — a run *or* a table URL. The `run_url=` keyword is gone; the emitted `result` event
44
+ still carries `run_url` (wire unchanged).
45
+ - `JobContext.progress` documents `percent=-1` as indeterminate.
46
+
47
+ ### Removed
48
+ - `shared.modality.detect_modality_from_url` (zero users). `detect_modality_from_schema`,
49
+ `classify_metrics_columns`, `labels.candidate_label_paths`, and `labels.find_label_path`
50
+ are now internal (underscore-prefixed). Public alternatives: `detect_modality_from_table`
51
+ for modality; `get_label_map` / `get_label_names` / `find_label_column` for labels.
52
+ `classify_metrics_columns` was a plugin-specific column-name heuristic with no public
53
+ replacement — inline it (its one known user did).
54
+ - The orphan `_ICON_SVG` constant; the dead `params.setdefault("url", "")` in the `/compute`
55
+ handler.
56
+
57
+ ### Migration
58
+ - `ctx.result(run_url=x)` → `ctx.result(x)`.
59
+ - Validation failures: raise, or `ctx.fail("message")` for a clean user-facing card.
60
+ - Reading `tlc_plugin_sdk.PY_CONTRACT` / `JS_CONTRACT`, or a worker's `/health`
61
+ `py_contract`/`js_contract`: use `SDK_CONTRACT_VERSION` / `sdk_version` and compare on
62
+ `MAJOR.MINOR`.
63
+ - `shared.modality.detect_modality_from_url(url)` → load the table yourself and call
64
+ `detect_modality_from_table(table)`. `classify_metrics_columns(columns)` → copy the
65
+ heuristic into your plugin.
66
+ - A fragment that kept its own always-connected socket for custom events: switch to
67
+ `PluginJobs.on(namespace, event, handler)` and call `PluginJobs.connect(namespace)` on mount
68
+ so nothing emitted before the first `track()` is missed.
69
+ - Fragments may delete their manual `inject_scripts(raw, job_tracker_script())` — the host
70
+ injects `PluginJobs` now (harmless to keep).
71
+ - The manifest never declared the SocketIO namespace or `sdk_version`; both were dead and are
72
+ documented as such. The namespace is host-derived as `/<plugin-id>`.
73
+
74
+ ## [0.2.3] - 2026-08-21
75
+
76
+ ### Changed
77
+ - Docs: the plugin guide and API docstrings describe the single runtime — a plugin runs in
78
+ its own venv behind a worker; the manifest's `isolation` key accepts only `"venv"` (the
79
+ default when absent). The guide's isolation table, the in-tree host exception callout, and
80
+ the `unload` endpoint reference are gone; the dev-workflow section now documents the
81
+ folder-Source + editable-venv + `worker/stop` loop. No code or contract change.
82
+
83
+ ## [0.2.2] - 2026-08-18
84
+
85
+ ### Added
86
+ - Shared data-source picker (`shared.data_source_ui`) and its browse/upload route handlers
87
+ (`shared.data_source_routes`): plugins mount one consistent widget for choosing a local
88
+ path or uploading a file, instead of each fragment rolling its own (#11).
89
+
90
+ ### Changed
91
+ - **Distribution moved to PyPI**: `3lc-compute-plugin-sdk` is now published to public
92
+ [PyPI](https://pypi.org/project/3lc-compute-plugin-sdk/) via Trusted Publishing; the private
93
+ CloudRepo index (pypi.3lc.ai) is no longer needed to install the SDK. Manual prerelease
94
+ builds keep publishing to CloudRepo for a grace period (#13).
95
+ - The `[shared]` extra's `3lc` dependency resolves from public PyPI (its home since the 3.2
96
+ rust release), so the SDK no longer pins a custom package index (#12).
97
+
98
+ ### Security
99
+ - The data-source `/browse` route is confined to operator-configured roots
100
+ (`TLC_DATA_SOURCE_ROOTS`, `os.pathsep`-separated; default: the service user's home).
101
+ Every requested path is realpath-resolved before the containment check, so `..`
102
+ segments and symlinks pointing outside a root are denied rather than followed, and
103
+ the widget's breadcrumb stops at the root instead of offering the whole filesystem (#11).
104
+ - The data-source `/upload-temp` route strips directory components from the
105
+ client-supplied filename (closing a path traversal where a `../…` name chose the
106
+ write location), lands each upload in its own private temp directory so same-named
107
+ uploads never clobber each other, and rejects bodies larger than
108
+ `TLC_DATA_SOURCE_MAX_UPLOAD_MB` (default 512) (#11).
109
+
110
+ ## [0.2.1] - 2026-08-13
111
+
112
+ ### Added
113
+ - Tilde expansion and local-path normalization helpers in `shared.url_utils`
114
+ (`normalize_local_path`, `normalize_url`): user-supplied paths expand `~` and resolve
115
+ to absolute form at every ingress, so exported files and stored URLs never carry a
116
+ user-relative path (#10).
117
+
118
+ ## [0.2.0] - 2026-08-05
119
+
120
+ ### Added
121
+ - JS_CONTRACT 0.2: project locations (`TlcLocation`) are available to plugin UI fragments
122
+ through `PLUGIN_API`, so plugins can resolve and present where a project's data lives (#8).
123
+ - The plugin worker reports the SDK contract version from its `/health` endpoint, letting the
124
+ host enforce compatibility floors precisely instead of guessing (#6).
125
+
126
+ ### Changed
127
+ - The plugin worker releases cached GPU memory after every job, so a finished GPU job no longer
128
+ pins CUDA memory that other plugins need (#7).
129
+
130
+ ## [0.1.1] - 2026-07-03
131
+
132
+ ### Changed
133
+ - **Distribution renamed**: `3lc-plugin-sdk` is now published as `3lc-compute-plugin-sdk`
134
+ (the import name `tlc_plugin_sdk` is unchanged). Update your dependency declarations.
135
+
136
+ ### Fixed
137
+ - Regenerated `uv.lock` for the renamed distribution; release builds append the run number as a
138
+ build counter.
139
+
140
+ ## [0.1.0] - 2026-06-30
141
+
142
+ First public release of the plugin SDK for the 3LC compute service.
143
+
144
+ ### Added
145
+ - The plugin contract: `ComputePlugin`, `JobContext`, and the `PLUGIN_API` browser bridge
146
+ (JS_CONTRACT) that plugin UI fragments use to talk to the host.
147
+ - Slim base package with a `[shared]` extra for common plugin dependencies.
148
+ - Apache-2.0 license, public CI, and a documentation site including the plugin author guide and
149
+ a rendered browser-contract reference.
@@ -83,10 +83,11 @@ section is the condensed working order.
83
83
  - **Config + GPU job + SocketIO**: sam3, yolo, or timm
84
84
  - **Inline data display (no standalone page)**: `tlc_plugin_table_statistics`
85
85
  - **Action on selected resources**: `tlc_plugin_merger`
86
- 3. Read `src/tlc_plugin_sdk/contract.py` — `ComputePlugin` is an `abc.ABC` with two abstract
87
- methods (`get_ui_fragment()`, `compute()`) and an `id` attribute the host stamps from the
88
- manifest. The optional hooks (`run_job`, `initialise_runtime`, `shutdown_runtime`,
89
- `get_route_handlers`) ship as no-op defaults, so you override only what you need. There is
86
+ 3. Read `src/tlc_plugin_sdk/contract.py` — `ComputePlugin` is an `abc.ABC` with one abstract
87
+ method (`get_ui_fragment()`) and an `id` attribute the host stamps from the manifest.
88
+ `compute()` and the other hooks (`run_job`, `initialise_runtime`, `shutdown_runtime`,
89
+ `get_route_handlers`) ship as defaults (`compute()` reports that the plugin doesn't
90
+ implement it), so you override only what you need. There is
90
91
  no `register()` and **no** `get_active_jobs`/`cancel_job` — the host owns job listing and
91
92
  cancellation.
92
93
  4. Read `tlc_plugin_exporter`'s `plugin.toml` + `__init__.py` — the canonical manifest +
@@ -154,9 +155,11 @@ section is the condensed working order.
154
155
  form fills call `_tlcSyncAliasFromForm(prefix, projectId, folderId)`.
155
156
 
156
157
  **SocketIO** (if real-time updates are needed):
157
- - `socketio_namespace` is optional — it defaults to `/<plugin-id>`, which the host
158
- auto-registers at startup; declare it in `[runtime]` only to override.
159
- - Prefer the `window.PluginJobs` client over a hand-rolled socket.
158
+ - The namespace is host-derived as `/<plugin-id>` and auto-registered at startup; the manifest
159
+ does not (and never did) declare it.
160
+ - Prefer the `window.PluginJobs` client over a hand-rolled socket. It connects a namespace
161
+ lazily; call `PluginJobs.connect('/<plugin-id>')` on mount if the fragment needs custom
162
+ events from the first second (`run()` connects for you).
160
163
  - Use `ctx.emit(name, payload)` only for telemetry the generic schema can't express
161
164
  (e.g. a loss curve); never re-emit the generic lifecycle by hand.
162
165
 
@@ -1,7 +1,7 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: 3lc-compute-plugin-sdk
3
- Version: 0.2.2
4
- Summary: Public Python SDK for building 3LC compute-service plugins (the import-light plugin contract).
3
+ Version: 0.3.0
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
7
7
  Author-email: 3LC <support@3lc.ai>
@@ -44,15 +44,16 @@ 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 `compute` / `get_ui_fragment`;
48
- job and lifecycle hooks ship as no-op defaults. There is no `register()` to call — a plugin's
49
- metadata lives in its `[tool.tlc-compute]` manifest, and the host discovers it from there.
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`.
53
54
  - **The worker** (`python -m tlc_plugin_sdk.worker`) — serves your plugin's Litestar route
54
- handlers + the generic reserved routes as an ASGI app, identically whether the host runs it
55
- in-process or out-of-process in its own venv.
55
+ handlers + the generic reserved routes as an ASGI app, out-of-process in the plugin's
56
+ own venv.
56
57
  - **`tlc_plugin_sdk.shared.*`** — batteries the heavy plugins share: URL-alias registration,
57
58
  config storage/UI, the generic-job helpers, image/label/modality utilities, script injection.
58
59
 
@@ -63,20 +64,25 @@ 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
- A plugin pins the SDK (`3lc-compute-plugin-sdk>=X,<Y`) and declares the contract it targets via its
68
- manifest. The host implements a contract range and the SDK version is the lingua franca that
69
- both sides agree on. Versions are SemVer; see **Status** below for the 0.x stability stance.
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
71
+ stance.
70
72
 
71
- ## Boundary (the one rule)
73
+ ## How it fits
72
74
 
73
- The SDK is the **root** of the plugin dependency graph: it depends only on `3lc` + `uvicorn` +
74
- `litestar`, and **never** on the host (`3lc-compute`) or on any plugin. If your plugin only
75
- needs `tlc_plugin_sdk`, it is portable across host versions and can run in its own isolated venv.
75
+ This SDK sits at the root of the plugin dependency graph. Its only required dependencies are
76
+ `uvicorn` and `litestar`; the `3lc` data plane is an optional extra
77
+ (`3lc-compute-plugin-sdk[shared]`), pulled in only by the `shared.*` helpers. It **never** depends
78
+ on the compute service itself or on any plugin. A plugin built against `tlc_plugin_sdk` alone is
79
+ therefore portable across service versions and runs in its own isolated environment.
76
80
 
77
81
  ## Status
78
82
 
79
- **0.2 is the released contract line.** Within 0.x the contract evolves **additively only**:
80
- symbols and schemas that exist keep working, and anything breaking waits for a major bump. In
83
+ **0.3 is the current contract line.** Within 0.x the contract still evolves — mostly additively,
84
+ but a coordinated breaking change may land on a MINOR bump while the fleet re-pins in lockstep (0.3
85
+ did: `JobContext.result` took a positional `url`, and the `PY_CONTRACT`/`JS_CONTRACT` axes
86
+ collapsed into `SDK_CONTRACT_VERSION`). Anything reshaping the core still waits for a major bump. In
81
87
  the browser bridge, `PLUGIN_API.libs.io` is a stable part of the contract; the other bundled
82
88
  libs are best-effort (see the guide).
@@ -18,15 +18,16 @@ 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 `compute` / `get_ui_fragment`;
22
- job and lifecycle hooks ship as no-op defaults. There is no `register()` to call — a plugin's
23
- metadata lives in its `[tool.tlc-compute]` manifest, and the host discovers it from there.
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`.
27
28
  - **The worker** (`python -m tlc_plugin_sdk.worker`) — serves your plugin's Litestar route
28
- handlers + the generic reserved routes as an ASGI app, identically whether the host runs it
29
- in-process or out-of-process in its own venv.
29
+ handlers + the generic reserved routes as an ASGI app, out-of-process in the plugin's
30
+ own venv.
30
31
  - **`tlc_plugin_sdk.shared.*`** — batteries the heavy plugins share: URL-alias registration,
31
32
  config storage/UI, the generic-job helpers, image/label/modality utilities, script injection.
32
33
 
@@ -37,20 +38,25 @@ 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
- A plugin pins the SDK (`3lc-compute-plugin-sdk>=X,<Y`) and declares the contract it targets via its
42
- manifest. The host implements a contract range and the SDK version is the lingua franca that
43
- both sides agree on. Versions are SemVer; see **Status** below for the 0.x stability stance.
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
45
+ stance.
44
46
 
45
- ## Boundary (the one rule)
47
+ ## How it fits
46
48
 
47
- The SDK is the **root** of the plugin dependency graph: it depends only on `3lc` + `uvicorn` +
48
- `litestar`, and **never** on the host (`3lc-compute`) or on any plugin. If your plugin only
49
- needs `tlc_plugin_sdk`, it is portable across host versions and can run in its own isolated venv.
49
+ This SDK sits at the root of the plugin dependency graph. Its only required dependencies are
50
+ `uvicorn` and `litestar`; the `3lc` data plane is an optional extra
51
+ (`3lc-compute-plugin-sdk[shared]`), pulled in only by the `shared.*` helpers. It **never** depends
52
+ on the compute service itself or on any plugin. A plugin built against `tlc_plugin_sdk` alone is
53
+ therefore portable across service versions and runs in its own isolated environment.
50
54
 
51
55
  ## Status
52
56
 
53
- **0.2 is the released contract line.** Within 0.x the contract evolves **additively only**:
54
- symbols and schemas that exist keep working, and anything breaking waits for a major bump. In
57
+ **0.3 is the current contract line.** Within 0.x the contract still evolves — mostly additively,
58
+ but a coordinated breaking change may land on a MINOR bump while the fleet re-pins in lockstep (0.3
59
+ did: `JobContext.result` took a positional `url`, and the `PY_CONTRACT`/`JS_CONTRACT` axes
60
+ collapsed into `SDK_CONTRACT_VERSION`). Anything reshaping the core still waits for a major bump. In
55
61
  the browser bridge, `PLUGIN_API.libs.io` is a stable part of the contract; the other bundled
56
62
  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 wheel/SemVer — the dependency *pin* a plugin resolves
23
- against (`3lc-compute-plugin-sdk>=X,<Y`). `PY_CONTRACT` and `JS_CONTRACT` are finer-grained
24
- **capability markers** for runtime feature-detection: `PY_CONTRACT` covers the Python
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`). Both increment independently and are always
28
- `<=` the package version. See `docs/plugin-guide.md` → "Two version axes".
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 (`JS_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`, the source of truth for
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
@@ -17,7 +17,7 @@ pip install 3lc-compute-plugin-sdk # import name: tlc_plugin_sdk
17
17
  against: `progress` / `metric` / `log` / `result`, `emit` for rich UI, and
18
18
  cooperative `cancelled`.
19
19
  - **The worker** (`python -m tlc_plugin_sdk.worker`) — serves your plugin's Litestar
20
- route handlers as an ASGI app, in-process or out-of-process in its own venv.
20
+ route handlers as an ASGI app, out-of-process in the plugin's own venv.
21
21
 
22
22
  ```{toctree}
23
23
  :maxdepth: 2