3lc-compute-plugin-sdk 0.2.2__tar.gz → 0.2.3__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 (46) hide show
  1. {3lc_compute_plugin_sdk-0.2.2 → 3lc_compute_plugin_sdk-0.2.3}/CHANGELOG.md +9 -0
  2. {3lc_compute_plugin_sdk-0.2.2 → 3lc_compute_plugin_sdk-0.2.3}/PKG-INFO +14 -11
  3. {3lc_compute_plugin_sdk-0.2.2 → 3lc_compute_plugin_sdk-0.2.3}/README.md +12 -9
  4. {3lc_compute_plugin_sdk-0.2.2 → 3lc_compute_plugin_sdk-0.2.3}/docs/index.md +1 -1
  5. {3lc_compute_plugin_sdk-0.2.2 → 3lc_compute_plugin_sdk-0.2.3}/docs/plugin-guide.md +54 -69
  6. {3lc_compute_plugin_sdk-0.2.2 → 3lc_compute_plugin_sdk-0.2.3}/pyproject.toml +2 -2
  7. {3lc_compute_plugin_sdk-0.2.2 → 3lc_compute_plugin_sdk-0.2.3}/src/tlc_plugin_sdk/asgi_app.py +10 -18
  8. {3lc_compute_plugin_sdk-0.2.2 → 3lc_compute_plugin_sdk-0.2.3}/src/tlc_plugin_sdk/contract.py +2 -3
  9. {3lc_compute_plugin_sdk-0.2.2 → 3lc_compute_plugin_sdk-0.2.3}/src/tlc_plugin_sdk/job_context.py +6 -11
  10. {3lc_compute_plugin_sdk-0.2.2 → 3lc_compute_plugin_sdk-0.2.3}/src/tlc_plugin_sdk/worker.py +5 -5
  11. {3lc_compute_plugin_sdk-0.2.2 → 3lc_compute_plugin_sdk-0.2.3}/uv.lock +118 -118
  12. {3lc_compute_plugin_sdk-0.2.2 → 3lc_compute_plugin_sdk-0.2.3}/.github/workflows/ci.yml +0 -0
  13. {3lc_compute_plugin_sdk-0.2.2 → 3lc_compute_plugin_sdk-0.2.3}/.github/workflows/docs.yml +0 -0
  14. {3lc_compute_plugin_sdk-0.2.2 → 3lc_compute_plugin_sdk-0.2.3}/.github/workflows/release.yml +0 -0
  15. {3lc_compute_plugin_sdk-0.2.2 → 3lc_compute_plugin_sdk-0.2.3}/.gitignore +0 -0
  16. {3lc_compute_plugin_sdk-0.2.2 → 3lc_compute_plugin_sdk-0.2.3}/CLAUDE.md +0 -0
  17. {3lc_compute_plugin_sdk-0.2.2 → 3lc_compute_plugin_sdk-0.2.3}/LICENSE +0 -0
  18. {3lc_compute_plugin_sdk-0.2.2 → 3lc_compute_plugin_sdk-0.2.3}/docs/api.md +0 -0
  19. {3lc_compute_plugin_sdk-0.2.2 → 3lc_compute_plugin_sdk-0.2.3}/docs/conf.py +0 -0
  20. {3lc_compute_plugin_sdk-0.2.2 → 3lc_compute_plugin_sdk-0.2.3}/docs/package-lock.json +0 -0
  21. {3lc_compute_plugin_sdk-0.2.2 → 3lc_compute_plugin_sdk-0.2.3}/docs/package.json +0 -0
  22. {3lc_compute_plugin_sdk-0.2.2 → 3lc_compute_plugin_sdk-0.2.3}/docs/requirements.txt +0 -0
  23. {3lc_compute_plugin_sdk-0.2.2 → 3lc_compute_plugin_sdk-0.2.3}/src/tlc_plugin_sdk/__init__.py +0 -0
  24. {3lc_compute_plugin_sdk-0.2.2 → 3lc_compute_plugin_sdk-0.2.3}/src/tlc_plugin_sdk/contract/plugin-api.d.ts +0 -0
  25. {3lc_compute_plugin_sdk-0.2.2 → 3lc_compute_plugin_sdk-0.2.3}/src/tlc_plugin_sdk/py.typed +0 -0
  26. {3lc_compute_plugin_sdk-0.2.2 → 3lc_compute_plugin_sdk-0.2.3}/src/tlc_plugin_sdk/shared/__init__.py +0 -0
  27. {3lc_compute_plugin_sdk-0.2.2 → 3lc_compute_plugin_sdk-0.2.3}/src/tlc_plugin_sdk/shared/alias_override_ui.py +0 -0
  28. {3lc_compute_plugin_sdk-0.2.2 → 3lc_compute_plugin_sdk-0.2.3}/src/tlc_plugin_sdk/shared/alias_ui.py +0 -0
  29. {3lc_compute_plugin_sdk-0.2.2 → 3lc_compute_plugin_sdk-0.2.3}/src/tlc_plugin_sdk/shared/aliases.py +0 -0
  30. {3lc_compute_plugin_sdk-0.2.2 → 3lc_compute_plugin_sdk-0.2.3}/src/tlc_plugin_sdk/shared/config_store.py +0 -0
  31. {3lc_compute_plugin_sdk-0.2.2 → 3lc_compute_plugin_sdk-0.2.3}/src/tlc_plugin_sdk/shared/config_ui.py +0 -0
  32. {3lc_compute_plugin_sdk-0.2.2 → 3lc_compute_plugin_sdk-0.2.3}/src/tlc_plugin_sdk/shared/data_source_routes.py +0 -0
  33. {3lc_compute_plugin_sdk-0.2.2 → 3lc_compute_plugin_sdk-0.2.3}/src/tlc_plugin_sdk/shared/data_source_ui.py +0 -0
  34. {3lc_compute_plugin_sdk-0.2.2 → 3lc_compute_plugin_sdk-0.2.3}/src/tlc_plugin_sdk/shared/generic_job.py +0 -0
  35. {3lc_compute_plugin_sdk-0.2.2 → 3lc_compute_plugin_sdk-0.2.3}/src/tlc_plugin_sdk/shared/images.py +0 -0
  36. {3lc_compute_plugin_sdk-0.2.2 → 3lc_compute_plugin_sdk-0.2.3}/src/tlc_plugin_sdk/shared/job_tracker.py +0 -0
  37. {3lc_compute_plugin_sdk-0.2.2 → 3lc_compute_plugin_sdk-0.2.3}/src/tlc_plugin_sdk/shared/labels.py +0 -0
  38. {3lc_compute_plugin_sdk-0.2.2 → 3lc_compute_plugin_sdk-0.2.3}/src/tlc_plugin_sdk/shared/modality.py +0 -0
  39. {3lc_compute_plugin_sdk-0.2.2 → 3lc_compute_plugin_sdk-0.2.3}/src/tlc_plugin_sdk/shared/model_storage.py +0 -0
  40. {3lc_compute_plugin_sdk-0.2.2 → 3lc_compute_plugin_sdk-0.2.3}/src/tlc_plugin_sdk/shared/naming.py +0 -0
  41. {3lc_compute_plugin_sdk-0.2.2 → 3lc_compute_plugin_sdk-0.2.3}/src/tlc_plugin_sdk/shared/ui_inject.py +0 -0
  42. {3lc_compute_plugin_sdk-0.2.2 → 3lc_compute_plugin_sdk-0.2.3}/src/tlc_plugin_sdk/shared/url_utils.py +0 -0
  43. {3lc_compute_plugin_sdk-0.2.2 → 3lc_compute_plugin_sdk-0.2.3}/tests/test_data_source_routes.py +0 -0
  44. {3lc_compute_plugin_sdk-0.2.2 → 3lc_compute_plugin_sdk-0.2.3}/tests/test_import_light.py +0 -0
  45. {3lc_compute_plugin_sdk-0.2.2 → 3lc_compute_plugin_sdk-0.2.3}/tests/test_url_utils.py +0 -0
  46. {3lc_compute_plugin_sdk-0.2.2 → 3lc_compute_plugin_sdk-0.2.3}/tests/test_worker_gpu_reclaim.py +0 -0
@@ -9,6 +9,15 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
9
9
 
10
10
  Nothing yet.
11
11
 
12
+ ## [0.2.3] - 2026-08-21
13
+
14
+ ### Changed
15
+ - Docs: the plugin guide and API docstrings describe the single runtime — a plugin runs in
16
+ its own venv behind a worker; the manifest's `isolation` key accepts only `"venv"` (the
17
+ default when absent). The guide's isolation table, the in-tree host exception callout, and
18
+ the `unload` endpoint reference are gone; the dev-workflow section now documents the
19
+ folder-Source + editable-venv + `worker/stop` loop. No code or contract change.
20
+
12
21
  ## [0.2.2] - 2026-08-18
13
22
 
14
23
  ### Added
@@ -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.2.3
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>
@@ -46,13 +46,13 @@ pip install 3lc-compute-plugin-sdk # import name: tlc_plugin_sdk
46
46
 
47
47
  - **`ComputePlugin`** — the base class you subclass. Implement `compute` / `get_ui_fragment`;
48
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.
49
+ metadata lives in its `[tool.tlc-compute]` manifest, and the Hub discovers it from there.
50
50
  - **`JobContext`** — the surface a long-running job programs against: `progress` / `metric` /
51
51
  `log` / `result` for the generic job panel, `emit` for your plugin's own rich UI, and
52
52
  cooperative `cancelled`.
53
53
  - **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.
54
+ handlers + the generic reserved routes as an ASGI app, out-of-process in the plugin's
55
+ own venv.
56
56
  - **`tlc_plugin_sdk.shared.*`** — batteries the heavy plugins share: URL-alias registration,
57
57
  config storage/UI, the generic-job helpers, image/label/modality utilities, script injection.
58
58
 
@@ -65,14 +65,17 @@ format, custom routes, the job model, UI fragment, checklist).
65
65
 
66
66
  `tlc_plugin_sdk.SDK_CONTRACT_VERSION` is this package's own version — one source of truth.
67
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.
68
+ manifest. The compute service implements a contract range, and the SDK version is the shared
69
+ reference both sides agree on. Versions are SemVer; see **Status** below for the 0.x stability
70
+ stance.
70
71
 
71
- ## Boundary (the one rule)
72
+ ## How it fits
72
73
 
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.
74
+ This SDK sits at the root of the plugin dependency graph. Its only required dependencies are
75
+ `uvicorn` and `litestar`; the `3lc` data plane is an optional extra
76
+ (`3lc-compute-plugin-sdk[shared]`), pulled in only by the `shared.*` helpers. It **never** depends
77
+ on the compute service itself or on any plugin. A plugin built against `tlc_plugin_sdk` alone is
78
+ therefore portable across service versions and runs in its own isolated environment.
76
79
 
77
80
  ## Status
78
81
 
@@ -20,13 +20,13 @@ pip install 3lc-compute-plugin-sdk # import name: tlc_plugin_sdk
20
20
 
21
21
  - **`ComputePlugin`** — the base class you subclass. Implement `compute` / `get_ui_fragment`;
22
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.
23
+ metadata lives in its `[tool.tlc-compute]` manifest, and the Hub discovers it from there.
24
24
  - **`JobContext`** — the surface a long-running job programs against: `progress` / `metric` /
25
25
  `log` / `result` for the generic job panel, `emit` for your plugin's own rich UI, and
26
26
  cooperative `cancelled`.
27
27
  - **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.
28
+ handlers + the generic reserved routes as an ASGI app, out-of-process in the plugin's
29
+ own venv.
30
30
  - **`tlc_plugin_sdk.shared.*`** — batteries the heavy plugins share: URL-alias registration,
31
31
  config storage/UI, the generic-job helpers, image/label/modality utilities, script injection.
32
32
 
@@ -39,14 +39,17 @@ format, custom routes, the job model, UI fragment, checklist).
39
39
 
40
40
  `tlc_plugin_sdk.SDK_CONTRACT_VERSION` is this package's own version — one source of truth.
41
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.
42
+ manifest. The compute service implements a contract range, and the SDK version is the shared
43
+ reference both sides agree on. Versions are SemVer; see **Status** below for the 0.x stability
44
+ stance.
44
45
 
45
- ## Boundary (the one rule)
46
+ ## How it fits
46
47
 
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.
48
+ This SDK sits at the root of the plugin dependency graph. Its only required dependencies are
49
+ `uvicorn` and `litestar`; the `3lc` data plane is an optional extra
50
+ (`3lc-compute-plugin-sdk[shared]`), pulled in only by the `shared.*` helpers. It **never** depends
51
+ on the compute service itself or on any plugin. A plugin built against `tlc_plugin_sdk` alone is
52
+ therefore portable across service versions and runs in its own isolated environment.
50
53
 
51
54
  ## Status
52
55
 
@@ -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
@@ -83,11 +83,6 @@ tlc_plugin_my_plugin/ # the default shape: a standalone venv-isolated p
83
83
  └── ... # All plugin code lives here
84
84
  ```
85
85
 
86
- > **In-tree host exception.** A host may ship plugins inside its own package with
87
- > `isolation = "host"` and a host-internal entrypoint; those live in the host's source tree
88
- > and follow its layout. New plugins should use the `venv` + `tlc_plugin_<name>` shape shown
89
- > above, not the host form.
90
-
91
86
  ### 2. Write the manifest
92
87
 
93
88
  All metadata lives in a manifest — a standalone `plugin.toml` next to `__init__.py`. The host
@@ -96,10 +91,10 @@ source of truth for listing, routing, GPU/CPU classification, SocketIO wiring, a
96
91
  paths. (`read_manifest()` also accepts a `[tool.tlc-compute]` table in a plugin's `pyproject.toml`
97
92
  — it checks `plugin.toml` first.)
98
93
 
99
- A `venv` plugin keeps the **same `plugin.toml`** for metadata and adds a separate
100
- `pyproject.toml` alongside it that declares only its isolated venv's dependencies (no
94
+ A plugin keeps the **same `plugin.toml`** for metadata and adds a separate
95
+ `pyproject.toml` alongside it that declares only its venv's dependencies (no
101
96
  `[tool.tlc-compute]` table there) — see the `timm` / `sam3` / `yolo` plugins for the canonical
102
- layout, and the **Isolation** section below for how the two tiers run.
97
+ layout.
103
98
 
104
99
  ```toml
105
100
  # plugin.toml — the single source of truth for this plugin's metadata.
@@ -127,34 +122,25 @@ quick_action = false # Show in dashboard quick actions?
127
122
  # group_icon_svg = '<svg ...><rect x="2" y="2" width="12" height="12" rx="2"/></svg>'
128
123
 
129
124
  [runtime]
130
- isolation = "venv" # venv (isolated worker, the default) | host (in-process)
125
+ isolation = "venv" # "venv" is the only value (and the default when absent)
131
126
  entrypoint = "tlc_plugin_my_plugin:MyPlugin" # "pkg.module:ClassName"
132
127
  requires_gpu = false # drives GPU vs CPU classification
133
- provision_extra = "my-plugin" # REQUIRED for venv: host runs `uv sync --extra <this>`
128
+ provision_extra = "my-plugin" # umbrella extra the host provisions: `uv sync --extra <this>`
134
129
  # Optional SocketIO namespace for real-time updates. Defaults to "/<plugin-id>";
135
130
  # declare it only to override (the host registers it at startup so a UI can connect;
136
131
  # declaring it does NOT mean emitting custom events):
137
132
  # socketio_namespace = "/my-plugin"
138
133
  ```
139
134
 
140
- `runtime.provision_extra` is **required for every `venv` plugin**: it names the extra the
141
- host installs with `uv sync --extra <that-value>`. For first-party plugins this is a
142
- per-plugin extra in the `3lc-compute-plugins` umbrella `pyproject.toml`. (Host plugins don't
143
- declare it — their deps are a subset of the service.)
144
-
145
- **`isolation` — where the plugin runs (the *only* placement knob).** A plugin
146
- declares `isolation` and `requires_gpu`; it never picks a queue or names a lane.
135
+ `runtime.provision_extra` is **required for every plugin distributed through an umbrella**:
136
+ it names the extra the host installs with `uv sync --extra <that-value>`. For first-party
137
+ plugins this is a per-plugin extra in the `3lc-compute-plugins` umbrella `pyproject.toml`.
147
138
 
148
- | `isolation` | Loaded by | Deps | Best for |
149
- |---|---|---|---|
150
- | `host` (default) | folder scan → imported **in-process** | must be a subset of the service | lightweight, hot-reloadable plugins |
151
- | `venv` | manifest only → spawn a worker over a UDS | its own uv-managed venv | heavy / conflicting deps (torch, ultralytics, SAM) |
152
-
153
- The plugin class is identical either way — `run_job(ctx)` talks only to `ctx`, so
154
- the same code runs in-process or in a worker. `requires_gpu = true` routes the job
155
- through the shared GPU queue (one GPU job at a time, across every plugin);
156
- `requires_gpu = false` jobs run on the CPU queue. Both are host-owned; the plugin
157
- never touches a queue.
139
+ Every plugin runs in its own uv-managed venv, behind a worker the host spawns and talks to
140
+ over a Unix socket — the host registers the plugin from its manifest alone and never imports
141
+ its code. `requires_gpu` is the manifest's **only placement knob**: `true` routes the job
142
+ through the shared GPU queue (one GPU job at a time, across every plugin); `false` jobs run
143
+ on the CPU queue. Both are host-owned; the plugin never picks a queue or names a lane.
158
144
 
159
145
  ### 3. Implement the plugin object
160
146
 
@@ -289,13 +275,12 @@ The UI fragment is a self-contained `<style>` + `<div>` + `<script>` block. It h
289
275
 
290
276
  There is **nothing to register**. On startup, the host scans the plugin directories
291
277
  for manifests (no imports), builds a card from each, and gates compatibility against the
292
- service version. When a host plugin is actually needed, the host imports the module named in
293
- the manifest's `runtime.entrypoint` and instantiates the class, stamping the display
294
- identity (`id`, `name`, `icon`, `version`) from the manifest onto the instance.
278
+ service version. When a plugin is actually needed, its **worker** imports the module named
279
+ in the manifest's `runtime.entrypoint` and instantiates the class inside the plugin's own
280
+ venv — the host never imports plugin code.
295
281
 
296
- Because metadata is read before any import, a plugin whose dependency is missing still
297
- **lists** (greyed-out with a reason) instead of vanishing — its behavior code is never
298
- imported until it's used.
282
+ Because metadata is read without any import, a plugin whose environment is broken (or whose
283
+ manifest is invalid) still **lists** (greyed-out with a reason) instead of vanishing.
299
284
 
300
285
  That's it. Drop the directory in place with a `plugin.toml` and it will be discovered on
301
286
  startup.
@@ -340,9 +325,8 @@ Browser (3LC Hub frontend, vanilla JS) Compute service (:5020)
340
325
  └─ PLUGIN_API.authFetch(.../{subpath}) ───────▶ ANY /api/plugins/{id}/{subpath} → route handler
341
326
  ```
342
327
 
343
- A plugin fragment is plain HTML+JS+CSS and is the **same bytes** whether the plugin runs
344
- in-process (`host` mode) or in an isolated venv worker (reverse-proxied) — the frontend
345
- can't tell. `PLUGIN_API` is the **single** host→fragment JS contract; a fragment should
328
+ A plugin fragment is plain HTML+JS+CSS, served by the plugin's worker and reverse-proxied
329
+ by the host — the frontend can't tell where it came from. `PLUGIN_API` is the **single** host→fragment JS contract; a fragment should
346
330
  reach for nothing else (the `API` shorthand some plugins use is just
347
331
  `var API = window.PLUGIN_API`).
348
332
 
@@ -440,9 +424,8 @@ socket.on('progress', function(data) { ... });
440
424
  For plugins that need more than the generic `compute()` method (e.g., POST bodies, multiple
441
425
  endpoints, streaming), return **relative Litestar route handlers** from `get_route_handlers()` —
442
426
  bare `@get`/`@post` handlers with **relative** paths, no `Controller` and no `/api/plugins`
443
- prefix. The host serves them through the plugin's own per-plugin app, behind the generic
444
- `/api/plugins/{id}/{subpath}` catch-all — in-process for `host` plugins, reverse-proxied to the
445
- worker for `venv` plugins.
427
+ prefix. The host serves them through the plugin's own app in its worker, behind the generic
428
+ `/api/plugins/{id}/{subpath}` catch-all.
446
429
 
447
430
  ```python
448
431
  from typing import Any
@@ -479,8 +462,8 @@ real example.
479
462
  A plugin with a long-running task (training, inference, import) **declares** the job
480
463
  in its manifest and **implements** it as `run_job(ctx)`. It does **not** grab a queue,
481
464
  push a closure, or poll a shared `cancel_flag` — the host owns the queue, the GPU/CPU
482
- slot lease, progress fan-out, listing, and cancellation. The same `run_job` code runs
483
- in-process (`host`) or in a worker (`venv`); it only ever touches `ctx`.
465
+ slot lease, progress fan-out, listing, and cancellation. `run_job` runs in the plugin's
466
+ worker and only ever touches `ctx`.
484
467
 
485
468
  **Declare the job in the manifest.** `requires_gpu` is the only knob:
486
469
 
@@ -582,8 +565,8 @@ independently.
582
565
  **Custom events** — `ctx.emit(name, payload)` is reserved for telemetry the generic schema
583
566
  **can't** express (e.g. a training plugin's per-epoch loss curve). The host relays it
584
567
  verbatim on the plugin's namespace; the generic panel ignores it. The name `job_update` is
585
- reserved and rejected. A plugin should **not** open its own SocketIO connection — emitting
586
- through `ctx` keeps `run_job` host/venv portable.
568
+ reserved and rejected. A plugin should **not** open its own SocketIO connection — the host
569
+ owns the transport; a plugin only ever emits through `ctx`.
587
570
 
588
571
  ```python
589
572
  # backend — inside run_job, for a plugin-specific chart the generic panel can't show:
@@ -639,19 +622,19 @@ color: var(--accent); /* Accent color (#2a4a61) */
639
622
 
640
623
  ## Existing Plugins Reference
641
624
 
642
- | Plugin ID | display_mode | Section | isolation | Description |
625
+ | Plugin ID | display_mode | Section | GPU | Description |
643
626
  |---|---|---|---|---|
644
- | `importer` | sidebar | Data Ops | **venv** | Import data (YOLO, COCO, Folder, CSV, Unlabeled) |
645
- | `exporter` | sidebar | Data Ops | **venv** | Export tables to CSV, XLSX, YOLO, COCO |
646
- | `splitter` | sidebar | Data Ops | **venv** | Split tables into train/val/test sets |
647
- | `merger` | sidebar | Data Ops | **venv** | Merge 2 tables by column join |
648
- | `image-metrics` | sidebar | Data Ops | **venv** (GPU) | Image quality metrics (brightness, sharpness, noise, etc.) |
649
- | `yolo` | sidebar | AI Tools | **venv** (GPU) | Ultralytics YOLO training + metrics collection |
650
- | `sam3` | sidebar | AI Tools | **venv** (GPU) | Auto-labeling with SAM3/GroundingDINO |
651
- | `timm` | sidebar | AI Tools | **venv** (GPU) | Image classification with timm models |
652
- | `table-statistics` | hidden | Analysis | **venv** | Per-column stats & image thumbnails (API-only) |
653
- | `run-insights` | action | Analysis | host (ships with the service) | Run statistics, health scores, per-class metrics |
654
- | `table-insights` | action | Analysis | host (ships with the service) | GT-only data quality analysis (bbox sizes, balance, etc.) |
627
+ | `importer` | sidebar | Data Ops | — | Import data (YOLO, COCO, Folder, CSV, Unlabeled) |
628
+ | `exporter` | sidebar | Data Ops | — | Export tables to CSV, XLSX, YOLO, COCO |
629
+ | `splitter` | sidebar | Data Ops | — | Split tables into train/val/test sets |
630
+ | `merger` | sidebar | Data Ops | — | Merge 2 tables by column join |
631
+ | `image-metrics` | sidebar | Data Ops | GPU | Image quality metrics (brightness, sharpness, noise, etc.) |
632
+ | `yolo` | sidebar | AI Tools | GPU | Ultralytics YOLO training + metrics collection |
633
+ | `sam3` | sidebar | AI Tools | GPU | Auto-labeling with SAM3/GroundingDINO |
634
+ | `timm` | sidebar | AI Tools | GPU | Image classification with timm models |
635
+ | `table-statistics` | hidden | Analysis | — | Per-column stats & image thumbnails (API-only) |
636
+ | `run-insights` | action | Analysis | — | Run statistics, health scores, per-class metrics |
637
+ | `table-insights` | action | Analysis | — | GT-only data quality analysis (bbox sizes, balance, etc.) |
655
638
 
656
639
  ---
657
640
 
@@ -742,30 +725,32 @@ Every sidebar plugin UI should follow this structure:
742
725
 
743
726
  ---
744
727
 
745
- ## Hot-Reload (Development)
728
+ ## The Dev Loop (Development)
729
+
730
+ The fast edit-run cycle is a **folder Source + an editable venv + a worker restart** — no
731
+ service restart, no rebuild:
746
732
 
747
- During development, you can hot-reload a plugin without restarting the Compute Service:
733
+ 1. Register your checkout as a folder Source (point `--plugin-dir` / the Settings page's
734
+ plugin-directories UI at the directory holding your plugin folder). The host provisions
735
+ the plugin's venv with `uv sync`, which installs your project **editable** — so the venv
736
+ always runs the code on disk.
737
+ 2. Edit your plugin files.
738
+ 3. Retire the worker; the next request cold-starts on the current code (sub-second):
748
739
 
749
740
  ```bash
750
- # 1. Edit your plugin files on the server
751
- # 2. Call the admin reload endpoint:
752
- curl -X POST http://localhost:5020/api/admin/plugins/my-plugin/reload
741
+ curl -X POST http://localhost:5020/api/admin/plugins/my-plugin/worker/stop
753
742
  ```
754
743
 
755
744
  Or from the browser console:
756
745
  ```javascript
757
- TlcApi.authFetch(TlcApi.computeServiceUrl + '/api/admin/plugins/my-plugin/reload', {method:'POST'})
746
+ TlcApi.authFetch(TlcApi.computeServiceUrl + '/api/admin/plugins/my-plugin/worker/stop', {method:'POST'})
758
747
  .then(r => r.json()).then(console.log)
759
748
  ```
760
749
 
761
- This purges the plugin's Python modules from `sys.modules`, re-imports the package
762
- (picking up file changes), and re-initialises the runtime. Running jobs in other
763
- plugins are unaffected. The UI cache is automatically cleared.
764
-
765
- To unload a plugin entirely:
766
- ```bash
767
- curl -X POST http://localhost:5020/api/admin/plugins/my-plugin/unload
768
- ```
750
+ For **dependency** changes (a new package, a version bump in your `pyproject.toml`), rebuild
751
+ the venv instead — `POST /api/admin/plugins/my-plugin/reload` runs `uv sync --reinstall` in
752
+ the background and retires the worker when the rebuilt venv swaps in. Running jobs in other
753
+ plugins are unaffected either way.
769
754
 
770
755
  ---
771
756
 
@@ -7,8 +7,8 @@ name = "3lc-compute-plugin-sdk"
7
7
  # The plugin-contract (ABI) version. This IS tlc_plugin_sdk.SDK_CONTRACT_VERSION
8
8
  # (read back via importlib.metadata) — one source of truth. SemVer MAJOR.MINOR.PATCH;
9
9
  # the 0.x line is additive-only (see README → Status).
10
- version = "0.2.2"
11
- description = "Public Python SDK for building 3LC compute-service plugins (the import-light plugin contract)."
10
+ version = "0.2.3"
11
+ description = "Public Python SDK for building 3LC compute-service plugins."
12
12
  requires-python = ">=3.10"
13
13
  readme = "README.md"
14
14
  authors = [{ name = "3LC", email = "support@3lc.ai" }]
@@ -4,23 +4,16 @@
4
4
 
5
5
  This is the single route-authoring pattern: a plugin exposes its custom routes as
6
6
  relative Litestar route handlers via :meth:`ComputePlugin.get_route_handlers`, and
7
- the **same** handlers are served two ways from the **same** builder:
7
+ the worker (``tlc_plugin_sdk.worker``) serves the app with uvicorn on a Unix socket
8
+ (or TCP, for a remote worker); the host reverse-proxies to it.
8
9
 
9
- - **host (in-process):** the compute service builds the app once per plugin
10
- instance and invokes it directly through the ASGI interface (no socket);
11
- - **venv (out-of-process):** the worker (``tlc_plugin_sdk.worker``) serves the app
12
- with uvicorn on a Unix socket and the host reverse-proxies to it.
13
-
14
- Because both modes serve the identical app, a plugin's routes get a real router,
15
- request validation, multipart, and binary/streaming responses in **either** mode,
16
- with no host/venv divergence. Litestar runs ``def`` handlers in a threadpool, so a
17
- synchronous, CPU-bound custom route (e.g. preview inference) does not block the
18
- event loop.
10
+ Because the worker runs a real Litestar app, a plugin's routes get a real router,
11
+ request validation, multipart, and binary/streaming responses. Litestar runs
12
+ ``def`` handlers in a threadpool, so a synchronous, CPU-bound custom route (e.g.
13
+ preview inference) does not block the event loop.
19
14
 
20
15
  The app also mounts the host-reserved generic routes (``/health``, ``/ui``,
21
- ``/compute``) so the worker can answer them over the socket; for the in-process host
22
- app these are harmless (the host serves ``/ui``/``/compute`` from its own reserved
23
- param routes and never forwards them here).
16
+ ``/compute``) so the worker can answer them over the socket.
24
17
 
25
18
  Litestar is a base dependency of this SDK, but it is imported **here**, not in
26
19
  the import-light :mod:`tlc_plugin_sdk` package surface — so
@@ -80,13 +73,12 @@ def build_plugin_app(
80
73
  extra_handlers: list[BaseRouteHandler] | None = None,
81
74
  debug: bool = False,
82
75
  ) -> Litestar:
83
- """Build the Litestar app serving ``plugin``'s HTTP surface (host + venv).
76
+ """Build the Litestar app serving ``plugin``'s HTTP surface.
84
77
 
85
78
  Args:
86
79
  plugin: The plugin instance whose behavior the routes invoke.
87
- extra_handlers: Worker-only handlers (the ``/jobs/{id}/run`` stream,
88
- ``/jobs/{id}/cancel``, and ``/reclaim``); omitted for the host in-process
89
- app, whose job lifecycle is owned by the host ``JobManager``.
80
+ extra_handlers: The worker's job-channel handlers (the ``/jobs/{id}/run``
81
+ stream, ``/jobs/{id}/cancel``, and ``/reclaim``).
90
82
  debug: Litestar debug flag.
91
83
 
92
84
  Returns:
@@ -77,7 +77,7 @@ class ComputePlugin(ABC):
77
77
  """Run a long-running job against a host-provided context.
78
78
 
79
79
  The plugin reports progress/metrics and polls cancellation via ``ctx``;
80
- the same code runs in-process (host mode) or in a worker (venv mode).
80
+ the code runs in the plugin's worker and only ever touches ``ctx``.
81
81
 
82
82
  Raises:
83
83
  NotImplementedError: The default — a plugin that streams jobs must
@@ -98,8 +98,7 @@ class ComputePlugin(ABC):
98
98
  Each handler's path is **relative** to the plugin's mount point
99
99
  ``/api/plugins/{plugin_id}/`` (e.g. a ``@get("/models")`` handler serves
100
100
  ``GET /api/plugins/{plugin_id}/models``). The handlers are served by the
101
- plugin's own Litestar app — invoked in-process for host plugins,
102
- reverse-proxied to the worker for venv plugins (see
101
+ plugin's own Litestar app in its worker, reverse-proxied by the host (see
103
102
  ``tlc_plugin_sdk/asgi_app.py``); Litestar runs ``def`` handlers in a threadpool,
104
103
  so a synchronous, blocking custom route does not block the event loop. The
105
104
  reserved routes (``/run``, ``/health``, ``/ui``, ``/compute``, ``/jobs/*``)
@@ -3,15 +3,11 @@
3
3
  """``JobContext`` — the surface a plugin's ``run_job`` programs against.
4
4
 
5
5
  A plugin implements ``run_job(ctx)`` and only ever touches ``ctx`` — it never
6
- grabs a host queue or polls a shared ``cancel_flag``. The same object is used in
7
- both ``host`` and ``venv`` modes; only the **sink** (where emitted events go) and
8
- the **cancel signal** differ:
9
-
10
- - ``venv`` mode: the worker harness gives a sink that enqueues events for the
11
- streamed control-channel response, and a ``threading.Event`` set by the worker's
12
- ``/cancel`` endpoint.
13
- - ``host`` mode: the host gives a sink that relays straight to SocketIO and a
14
- cancel event tied to the host's job manager.
6
+ grabs a host queue or polls a shared ``cancel_flag``. The **sink** (where emitted
7
+ events go) and the **cancel signal** are injected: the worker harness gives a sink
8
+ that enqueues events for the streamed control-channel response, and a
9
+ ``threading.Event`` set by the worker's ``/cancel`` endpoint. Tests inject their
10
+ own sink and event and drive ``run_job`` directly.
15
11
 
16
12
  Import-light: stdlib only. Must not pull in the server stack.
17
13
  """
@@ -93,8 +89,7 @@ class JobContext:
93
89
  generic Queue & Progress panel ignores it. Use :meth:`progress` /
94
90
  :meth:`metric` / :meth:`log` for the generic panel, and this for
95
91
  plugin-specific UI (e.g. a training plugin's per-epoch loss curve) — so a
96
- plugin never opens its own SocketIO connection and stays host/venv
97
- portable (same ``run_job`` in either mode).
92
+ plugin never opens its own SocketIO connection; the host owns the transport.
98
93
 
99
94
  Args:
100
95
  name: Event name the plugin's UI listens for.
@@ -10,10 +10,10 @@ The bind transport is selectable: ``--socket`` (Unix domain socket, the default
10
10
  supervisor uses) or ``--host``/``--port`` (TCP, e.g. ``--host 127.0.0.1 --port 9100``)
11
11
  for a worker reachable over the network. Exactly one of the two must be given.
12
12
 
13
- The worker serves the **same** Litestar app the host runs in-process for a host
14
- plugin (``tlc_plugin_sdk.asgi_app.build_plugin_app``): the plugin's own route handlers
13
+ The worker serves the plugin's Litestar app
14
+ (``tlc_plugin_sdk.asgi_app.build_plugin_app``): the plugin's own route handlers
15
15
  plus the generic reserved routes (``/health``, ``/ui``, ``/compute``). On top of that
16
- it adds the venv job channel the host supervisor drives:
16
+ it adds the job channel the host supervisor drives:
17
17
 
18
18
  - ``POST /jobs/{job_id}/run`` → runs ``run_job(ctx)`` on a thread; the response
19
19
  **streams NDJSON events** (progress/metric/log)
@@ -28,8 +28,8 @@ process cannot know that another plugin's worker needs the card. The host decide
28
28
  when; the worker is the only side that can act, because a CUDA allocator is
29
29
  per-process and the supervisor's only in-process lever is killing the worker.
30
30
 
31
- Because the worker runs a real Litestar app, a plugin's custom routes get the same
32
- router, validation, multipart, and binary/streaming behavior they get in host mode —
31
+ Because the worker runs a real Litestar app, a plugin's custom routes get a real
32
+ router, validation, multipart, and binary/streaming behavior —
33
33
  and Litestar runs synchronous ``def`` handlers in a threadpool, so CPU-bound routes
34
34
  don't block the worker's event loop. Litestar + uvicorn are base dependencies of
35
35
  this SDK; they are imported here, not by the import-light :mod:`tlc_plugin_sdk`
@@ -26,36 +26,36 @@ name = "3lc"
26
26
  version = "3.2.1"
27
27
  source = { registry = "https://pypi.org/simple" }
28
28
  dependencies = [
29
- { name = "anyio", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" },
30
- { name = "cachetools", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" },
31
- { name = "click", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" },
32
- { name = "cryptlex-lexactivator", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" },
33
- { name = "filelock", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" },
34
- { name = "litestar", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" },
35
- { name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" }, marker = "(python_full_version < '3.11' and sys_platform == 'darwin') or (python_full_version < '3.11' and sys_platform == 'linux') or (python_full_version < '3.11' and sys_platform == 'win32')" },
36
- { name = "numpy", version = "2.4.6", source = { registry = "https://pypi.org/simple" }, marker = "(python_full_version >= '3.11' and sys_platform == 'darwin') or (python_full_version >= '3.11' and sys_platform == 'linux') or (python_full_version >= '3.11' and sys_platform == 'win32')" },
37
- { name = "opencv-python", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" },
38
- { name = "packaging", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" },
39
- { name = "pillow", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" },
40
- { name = "platformdirs", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" },
41
- { name = "pyarrow", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" },
42
- { name = "pycocotools", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" },
43
- { name = "pydantic", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" },
44
- { name = "pyperclip", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" },
45
- { name = "python-dateutil", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" },
29
+ { name = "anyio" },
30
+ { name = "cachetools" },
31
+ { name = "click" },
32
+ { name = "cryptlex-lexactivator" },
33
+ { name = "filelock" },
34
+ { name = "litestar" },
35
+ { name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" },
36
+ { name = "numpy", version = "2.4.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" },
37
+ { name = "opencv-python" },
38
+ { name = "packaging" },
39
+ { name = "pillow" },
40
+ { name = "platformdirs" },
41
+ { name = "pyarrow" },
42
+ { name = "pycocotools" },
43
+ { name = "pydantic" },
44
+ { name = "pyperclip" },
45
+ { name = "python-dateutil" },
46
46
  { name = "pywin32", marker = "sys_platform == 'win32'" },
47
- { name = "pyyaml", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" },
48
- { name = "randomname", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" },
49
- { name = "rich", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" },
50
- { name = "ruamel-yaml", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" },
51
- { name = "sentry-sdk", extra = ["litestar"], marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" },
52
- { name = "termcolor", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" },
53
- { name = "textual", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" },
54
- { name = "tomli", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" },
55
- { name = "tqdm", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" },
56
- { name = "typing-extensions", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" },
57
- { name = "uvicorn", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" },
58
- { name = "watchdog", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" },
47
+ { name = "pyyaml" },
48
+ { name = "randomname" },
49
+ { name = "rich" },
50
+ { name = "ruamel-yaml" },
51
+ { name = "sentry-sdk", extra = ["litestar"] },
52
+ { name = "termcolor" },
53
+ { name = "textual" },
54
+ { name = "tomli" },
55
+ { name = "tqdm" },
56
+ { name = "typing-extensions" },
57
+ { name = "uvicorn" },
58
+ { name = "watchdog" },
59
59
  ]
60
60
  wheels = [
61
61
  { url = "https://files.pythonhosted.org/packages/8f/1b/79914d46251a34c898d7f0c31e0f3f8e3d1b05889d43418e84cca2296254/3lc-3.2.1-cp310-cp310-macosx_13_0_arm64.whl", hash = "sha256:454d087f5ee6f2118f026bd2de8748225b71274dc43f394c0aa60aa0477d332b", size = 10291323, upload-time = "2026-08-14T18:20:27.851Z" },
@@ -78,29 +78,29 @@ wheels = [
78
78
 
79
79
  [package.optional-dependencies]
80
80
  pandas = [
81
- { name = "pandas", version = "2.3.3", source = { registry = "https://pypi.org/simple" }, marker = "(python_full_version < '3.11' and sys_platform == 'darwin') or (python_full_version < '3.11' and sys_platform == 'linux') or (python_full_version < '3.11' and sys_platform == 'win32')" },
82
- { name = "pandas", version = "3.0.3", source = { registry = "https://pypi.org/simple" }, marker = "(python_full_version >= '3.11' and sys_platform == 'darwin') or (python_full_version >= '3.11' and sys_platform == 'linux') or (python_full_version >= '3.11' and sys_platform == 'win32')" },
81
+ { name = "pandas", version = "2.3.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" },
82
+ { name = "pandas", version = "3.0.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" },
83
83
  ]
84
84
 
85
85
  [[package]]
86
86
  name = "3lc-compute-plugin-sdk"
87
- version = "0.2.2"
87
+ version = "0.2.3"
88
88
  source = { editable = "." }
89
89
  dependencies = [
90
- { name = "litestar", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" },
91
- { name = "uvicorn", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" },
90
+ { name = "litestar" },
91
+ { name = "uvicorn" },
92
92
  ]
93
93
 
94
94
  [package.optional-dependencies]
95
95
  shared = [
96
- { name = "3lc", extra = ["pandas"], marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" },
96
+ { name = "3lc", extra = ["pandas"] },
97
97
  ]
98
98
 
99
99
  [package.dev-dependencies]
100
100
  dev = [
101
- { name = "mypy", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" },
102
- { name = "pytest", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" },
103
- { name = "ruff", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" },
101
+ { name = "mypy" },
102
+ { name = "pytest" },
103
+ { name = "ruff" },
104
104
  ]
105
105
 
106
106
  [package.metadata]
@@ -132,9 +132,9 @@ name = "anyio"
132
132
  version = "4.14.0"
133
133
  source = { registry = "https://pypi.org/simple" }
134
134
  dependencies = [
135
- { name = "exceptiongroup", marker = "(python_full_version < '3.11' and sys_platform == 'darwin') or (python_full_version < '3.11' and sys_platform == 'linux') or (python_full_version < '3.11' and sys_platform == 'win32')" },
136
- { name = "idna", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" },
137
- { name = "typing-extensions", marker = "(python_full_version < '3.13' and sys_platform == 'darwin') or (python_full_version < '3.13' and sys_platform == 'linux') or (python_full_version < '3.13' and sys_platform == 'win32')" },
135
+ { name = "exceptiongroup", marker = "python_full_version < '3.11'" },
136
+ { name = "idna" },
137
+ { name = "typing-extensions", marker = "python_full_version < '3.13'" },
138
138
  ]
139
139
  sdist = { url = "https://files.pythonhosted.org/packages/1c/b5/001890774a9552aff22502b8da382593109ce0c95314abaebbb116567545/anyio-4.14.0.tar.gz", hash = "sha256:b47c1f9ccf73e67021df785332508f99379c68fa7d0684e8e3492cb1d4b23f89", size = 253586, upload-time = "2026-06-15T22:00:49.021Z" }
140
140
  wheels = [
@@ -234,7 +234,7 @@ name = "exceptiongroup"
234
234
  version = "1.3.1"
235
235
  source = { registry = "https://pypi.org/simple" }
236
236
  dependencies = [
237
- { name = "typing-extensions", marker = "(python_full_version < '3.11' and sys_platform == 'darwin') or (python_full_version < '3.11' and sys_platform == 'linux') or (python_full_version < '3.11' and sys_platform == 'win32')" },
237
+ { name = "typing-extensions" },
238
238
  ]
239
239
  sdist = { url = "https://files.pythonhosted.org/packages/50/79/66800aadf48771f6b62f7eb014e352e5d06856655206165d775e675a02c9/exceptiongroup-1.3.1.tar.gz", hash = "sha256:8b412432c6055b0b7d14c310000ae93352ed6754f70fa8f7c34141f91c4e3219", size = 30371, upload-time = "2025-11-21T23:01:54.787Z" }
240
240
  wheels = [
@@ -267,7 +267,7 @@ name = "fire"
267
267
  version = "0.7.1"
268
268
  source = { registry = "https://pypi.org/simple" }
269
269
  dependencies = [
270
- { name = "termcolor", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" },
270
+ { name = "termcolor" },
271
271
  ]
272
272
  sdist = { url = "https://files.pythonhosted.org/packages/c0/00/f8d10588d2019d6d6452653def1ee807353b21983db48550318424b5ff18/fire-0.7.1.tar.gz", hash = "sha256:3b208f05c736de98fb343310d090dcc4d8c78b2a89ea4f32b837c586270a9cbf", size = 88720, upload-time = "2025-08-16T20:20:24.175Z" }
273
273
  wheels = [
@@ -288,8 +288,8 @@ name = "httpcore"
288
288
  version = "1.0.9"
289
289
  source = { registry = "https://pypi.org/simple" }
290
290
  dependencies = [
291
- { name = "certifi", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" },
292
- { name = "h11", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" },
291
+ { name = "certifi" },
292
+ { name = "h11" },
293
293
  ]
294
294
  sdist = { url = "https://files.pythonhosted.org/packages/06/94/82699a10bca87a5556c9c59b5963f2d039dbd239f25bc2a63907a05a14cb/httpcore-1.0.9.tar.gz", hash = "sha256:6e34463af53fd2ab5d807f399a9b45ea31c3dfa2276f15a2c3f00afff6e176e8", size = 85484, upload-time = "2025-04-24T22:06:22.219Z" }
295
295
  wheels = [
@@ -301,10 +301,10 @@ name = "httpx"
301
301
  version = "0.28.1"
302
302
  source = { registry = "https://pypi.org/simple" }
303
303
  dependencies = [
304
- { name = "anyio", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" },
305
- { name = "certifi", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" },
306
- { name = "httpcore", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" },
307
- { name = "idna", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" },
304
+ { name = "anyio" },
305
+ { name = "certifi" },
306
+ { name = "httpcore" },
307
+ { name = "idna" },
308
308
  ]
309
309
  sdist = { url = "https://files.pythonhosted.org/packages/b1/df/48c586a5fe32a0f01324ee087459e112ebb7224f646c0b5023f5e79e9956/httpx-0.28.1.tar.gz", hash = "sha256:75e98c5f16b0f35b567856f597f06ff2270a374470a5c2392242528e3e3e42fc", size = 141406, upload-time = "2024-12-06T15:37:23.222Z" }
310
310
  wheels = [
@@ -419,7 +419,7 @@ name = "linkify-it-py"
419
419
  version = "2.1.0"
420
420
  source = { registry = "https://pypi.org/simple" }
421
421
  dependencies = [
422
- { name = "uc-micro-py", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" },
422
+ { name = "uc-micro-py" },
423
423
  ]
424
424
  sdist = { url = "https://files.pythonhosted.org/packages/2e/c9/06ea13676ef354f0af6169587ae292d3e2406e212876a413bf9eece4eb23/linkify_it_py-2.1.0.tar.gz", hash = "sha256:43360231720999c10e9328dc3691160e27a718e280673d444c38d7d3aaa3b98b", size = 29158, upload-time = "2026-03-01T07:48:47.683Z" }
425
425
  wheels = [
@@ -431,20 +431,20 @@ name = "litestar"
431
431
  version = "2.24.0"
432
432
  source = { registry = "https://pypi.org/simple" }
433
433
  dependencies = [
434
- { name = "anyio", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" },
435
- { name = "click", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" },
436
- { name = "exceptiongroup", marker = "(python_full_version < '3.11' and sys_platform == 'darwin') or (python_full_version < '3.11' and sys_platform == 'linux') or (python_full_version < '3.11' and sys_platform == 'win32')" },
437
- { name = "httpx", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" },
438
- { name = "litestar-htmx", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" },
439
- { name = "msgspec", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" },
440
- { name = "multidict", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" },
441
- { name = "multipart", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" },
442
- { name = "polyfactory", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" },
443
- { name = "pyyaml", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" },
444
- { name = "rich", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" },
445
- { name = "rich-click", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" },
446
- { name = "sniffio", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" },
447
- { name = "typing-extensions", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" },
434
+ { name = "anyio" },
435
+ { name = "click" },
436
+ { name = "exceptiongroup", marker = "python_full_version < '3.11'" },
437
+ { name = "httpx" },
438
+ { name = "litestar-htmx" },
439
+ { name = "msgspec" },
440
+ { name = "multidict" },
441
+ { name = "multipart" },
442
+ { name = "polyfactory" },
443
+ { name = "pyyaml" },
444
+ { name = "rich" },
445
+ { name = "rich-click" },
446
+ { name = "sniffio" },
447
+ { name = "typing-extensions" },
448
448
  ]
449
449
  sdist = { url = "https://files.pythonhosted.org/packages/42/f6/33619a1562c6732dab211a967b1e7af3285de29d655c0b37b3f379aa2700/litestar-2.24.0.tar.gz", hash = "sha256:8f4b137cb115554b9fbc12bd01d398d5bb40ba85f2d333dd73f4b747308bbb46", size = 383070, upload-time = "2026-06-11T11:27:00.519Z" }
450
450
  wheels = [
@@ -465,7 +465,7 @@ name = "markdown-it-py"
465
465
  version = "4.2.0"
466
466
  source = { registry = "https://pypi.org/simple" }
467
467
  dependencies = [
468
- { name = "mdurl", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" },
468
+ { name = "mdurl" },
469
469
  ]
470
470
  sdist = { url = "https://files.pythonhosted.org/packages/06/ff/7841249c247aa650a76b9ee4bbaeae59370dc8bfd2f6c01f3630c35eb134/markdown_it_py-4.2.0.tar.gz", hash = "sha256:04a21681d6fbb623de53f6f364d352309d4094dd4194040a10fd51833e418d49", size = 82454, upload-time = "2026-05-07T12:08:28.36Z" }
471
471
  wheels = [
@@ -474,10 +474,10 @@ wheels = [
474
474
 
475
475
  [package.optional-dependencies]
476
476
  linkify = [
477
- { name = "linkify-it-py", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" },
477
+ { name = "linkify-it-py" },
478
478
  ]
479
479
  plugins = [
480
- { name = "mdit-py-plugins", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" },
480
+ { name = "mdit-py-plugins" },
481
481
  ]
482
482
 
483
483
  [[package]]
@@ -485,7 +485,7 @@ name = "mdit-py-plugins"
485
485
  version = "0.6.1"
486
486
  source = { registry = "https://pypi.org/simple" }
487
487
  dependencies = [
488
- { name = "markdown-it-py", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" },
488
+ { name = "markdown-it-py" },
489
489
  ]
490
490
  sdist = { url = "https://files.pythonhosted.org/packages/59/fc/f8d0863f8862f25602c0404d75568e89fb6b4109804645e5cdfb1be5cf56/mdit_py_plugins-0.6.1.tar.gz", hash = "sha256:a2bca0f039f39dbd35fb74ae1b5f998608c437463371f0ff7f49a19a17a114d0", size = 56114, upload-time = "2026-05-13T09:03:38.91Z" }
491
491
  wheels = [
@@ -562,7 +562,7 @@ name = "multidict"
562
562
  version = "6.7.1"
563
563
  source = { registry = "https://pypi.org/simple" }
564
564
  dependencies = [
565
- { name = "typing-extensions", marker = "(python_full_version < '3.11' and sys_platform == 'darwin') or (python_full_version < '3.11' and sys_platform == 'linux') or (python_full_version < '3.11' and sys_platform == 'win32')" },
565
+ { name = "typing-extensions", marker = "python_full_version < '3.11'" },
566
566
  ]
567
567
  sdist = { url = "https://files.pythonhosted.org/packages/1a/c2/c2d94cbe6ac1753f3fc980da97b3d930efe1da3af3c9f5125354436c073d/multidict-6.7.1.tar.gz", hash = "sha256:ec6652a1bee61c53a3e5776b6049172c53b6aaba34f18c9ad04f82712bac623d", size = 102010, upload-time = "2026-01-26T02:46:45.979Z" }
568
568
  wheels = [
@@ -709,12 +709,12 @@ name = "mypy"
709
709
  version = "2.1.0"
710
710
  source = { registry = "https://pypi.org/simple" }
711
711
  dependencies = [
712
- { name = "ast-serialize", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" },
713
- { name = "librt", marker = "(platform_python_implementation != 'PyPy' and sys_platform == 'darwin') or (platform_python_implementation != 'PyPy' and sys_platform == 'linux') or (platform_python_implementation != 'PyPy' and sys_platform == 'win32')" },
714
- { name = "mypy-extensions", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" },
715
- { name = "pathspec", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" },
716
- { name = "tomli", marker = "(python_full_version < '3.11' and sys_platform == 'darwin') or (python_full_version < '3.11' and sys_platform == 'linux') or (python_full_version < '3.11' and sys_platform == 'win32')" },
717
- { name = "typing-extensions", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" },
712
+ { name = "ast-serialize" },
713
+ { name = "librt", marker = "platform_python_implementation != 'PyPy'" },
714
+ { name = "mypy-extensions" },
715
+ { name = "pathspec" },
716
+ { name = "tomli", marker = "python_full_version < '3.11'" },
717
+ { name = "typing-extensions" },
718
718
  ]
719
719
  sdist = { url = "https://files.pythonhosted.org/packages/82/15/cca9d88503549ed6fedeaa1d448cdddd542ee8a490232d732e278036fbf2/mypy-2.1.0.tar.gz", hash = "sha256:81e76ad12c2d804512e9b13240d1588316531bfba07558286078bfbce9613633", size = 3898359, upload-time = "2026-05-11T18:37:36.237Z" }
720
720
  wheels = [
@@ -934,8 +934,8 @@ name = "opencv-python"
934
934
  version = "4.13.0.92"
935
935
  source = { registry = "https://pypi.org/simple" }
936
936
  dependencies = [
937
- { name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" }, marker = "(python_full_version < '3.11' and sys_platform == 'darwin') or (python_full_version < '3.11' and sys_platform == 'linux') or (python_full_version < '3.11' and sys_platform == 'win32')" },
938
- { name = "numpy", version = "2.4.6", source = { registry = "https://pypi.org/simple" }, marker = "(python_full_version >= '3.11' and sys_platform == 'darwin') or (python_full_version >= '3.11' and sys_platform == 'linux') or (python_full_version >= '3.11' and sys_platform == 'win32')" },
937
+ { name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" },
938
+ { name = "numpy", version = "2.4.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" },
939
939
  ]
940
940
  wheels = [
941
941
  { url = "https://files.pythonhosted.org/packages/fc/6f/5a28fef4c4a382be06afe3938c64cc168223016fa520c5abaf37e8862aa5/opencv_python-4.13.0.92-cp37-abi3-macosx_13_0_arm64.whl", hash = "sha256:caf60c071ec391ba51ed00a4a920f996d0b64e3e46068aac1f646b5de0326a19", size = 46247052, upload-time = "2026-02-05T07:01:25.046Z" },
@@ -967,10 +967,10 @@ resolution-markers = [
967
967
  "python_full_version < '3.11' and sys_platform == 'win32'",
968
968
  ]
969
969
  dependencies = [
970
- { name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" }, marker = "(python_full_version < '3.11' and sys_platform == 'darwin') or (python_full_version < '3.11' and sys_platform == 'linux') or (python_full_version < '3.11' and sys_platform == 'win32')" },
971
- { name = "python-dateutil", marker = "(python_full_version < '3.11' and sys_platform == 'darwin') or (python_full_version < '3.11' and sys_platform == 'linux') or (python_full_version < '3.11' and sys_platform == 'win32')" },
972
- { name = "pytz", marker = "(python_full_version < '3.11' and sys_platform == 'darwin') or (python_full_version < '3.11' and sys_platform == 'linux') or (python_full_version < '3.11' and sys_platform == 'win32')" },
973
- { name = "tzdata", marker = "(python_full_version < '3.11' and sys_platform == 'darwin') or (python_full_version < '3.11' and sys_platform == 'linux') or (python_full_version < '3.11' and sys_platform == 'win32')" },
970
+ { name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" } },
971
+ { name = "python-dateutil" },
972
+ { name = "pytz" },
973
+ { name = "tzdata" },
974
974
  ]
975
975
  sdist = { url = "https://files.pythonhosted.org/packages/33/01/d40b85317f86cf08d853a4f495195c73815fdf205eef3993821720274518/pandas-2.3.3.tar.gz", hash = "sha256:e05e1af93b977f7eafa636d043f9f94c7ee3ac81af99c13508215942e64c993b", size = 4495223, upload-time = "2025-09-29T23:34:51.853Z" }
976
976
  wheels = [
@@ -1039,9 +1039,9 @@ resolution-markers = [
1039
1039
  "python_full_version >= '3.11' and python_full_version < '3.14' and sys_platform == 'win32'",
1040
1040
  ]
1041
1041
  dependencies = [
1042
- { name = "numpy", version = "2.4.6", source = { registry = "https://pypi.org/simple" }, marker = "(python_full_version >= '3.11' and sys_platform == 'darwin') or (python_full_version >= '3.11' and sys_platform == 'linux') or (python_full_version >= '3.11' and sys_platform == 'win32')" },
1043
- { name = "python-dateutil", marker = "(python_full_version >= '3.11' and sys_platform == 'darwin') or (python_full_version >= '3.11' and sys_platform == 'linux') or (python_full_version >= '3.11' and sys_platform == 'win32')" },
1044
- { name = "tzdata", marker = "python_full_version >= '3.11' and sys_platform == 'win32'" },
1042
+ { name = "numpy", version = "2.4.6", source = { registry = "https://pypi.org/simple" } },
1043
+ { name = "python-dateutil" },
1044
+ { name = "tzdata", marker = "sys_platform == 'win32'" },
1045
1045
  ]
1046
1046
  sdist = { url = "https://files.pythonhosted.org/packages/f8/87/4341c6252d1c47b08768c3d25ac487362bf403f0313ddae4a2a26c9b1b4c/pandas-3.0.3.tar.gz", hash = "sha256:696a4a00a2a2a35d4e5deb3fc946641b96c944f02230e4f76137fe35d806c4fc", size = 4651414, upload-time = "2026-05-11T18:54:29.21Z" }
1047
1047
  wheels = [
@@ -1224,8 +1224,8 @@ name = "polyfactory"
1224
1224
  version = "3.3.0"
1225
1225
  source = { registry = "https://pypi.org/simple" }
1226
1226
  dependencies = [
1227
- { name = "faker", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" },
1228
- { name = "typing-extensions", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" },
1227
+ { name = "faker" },
1228
+ { name = "typing-extensions" },
1229
1229
  ]
1230
1230
  sdist = { url = "https://files.pythonhosted.org/packages/85/68/7717bd9e63ed254617a7d3dc9260904fb736d6ea203e58ffddcb186c64e4/polyfactory-3.3.0.tar.gz", hash = "sha256:237258b6ff43edf362ffd1f68086bb796466f786adfa002b0ac256dbf2246e9a", size = 348668, upload-time = "2026-02-22T09:46:28.01Z" }
1231
1231
  wheels = [
@@ -1294,8 +1294,8 @@ name = "pycocotools"
1294
1294
  version = "2.0.11"
1295
1295
  source = { registry = "https://pypi.org/simple" }
1296
1296
  dependencies = [
1297
- { name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" }, marker = "(python_full_version < '3.11' and sys_platform == 'darwin') or (python_full_version < '3.11' and sys_platform == 'linux') or (python_full_version < '3.11' and sys_platform == 'win32')" },
1298
- { name = "numpy", version = "2.4.6", source = { registry = "https://pypi.org/simple" }, marker = "(python_full_version >= '3.11' and sys_platform == 'darwin') or (python_full_version >= '3.11' and sys_platform == 'linux') or (python_full_version >= '3.11' and sys_platform == 'win32')" },
1297
+ { name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" },
1298
+ { name = "numpy", version = "2.4.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" },
1299
1299
  ]
1300
1300
  sdist = { url = "https://files.pythonhosted.org/packages/a2/df/32354b5dda963ffdfc8f75c9acf8828ef7890723a4ed57bb3ff2dc1d6f7e/pycocotools-2.0.11.tar.gz", hash = "sha256:34254d76da85576fcaf5c1f3aa9aae16b8cb15418334ba4283b800796bd1993d", size = 25381, upload-time = "2025-12-15T22:31:46.148Z" }
1301
1301
  wheels = [
@@ -1341,10 +1341,10 @@ name = "pydantic"
1341
1341
  version = "2.13.4"
1342
1342
  source = { registry = "https://pypi.org/simple" }
1343
1343
  dependencies = [
1344
- { name = "annotated-types", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" },
1345
- { name = "pydantic-core", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" },
1346
- { name = "typing-extensions", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" },
1347
- { name = "typing-inspection", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" },
1344
+ { name = "annotated-types" },
1345
+ { name = "pydantic-core" },
1346
+ { name = "typing-extensions" },
1347
+ { name = "typing-inspection" },
1348
1348
  ]
1349
1349
  sdist = { url = "https://files.pythonhosted.org/packages/18/a5/b60d21ac674192f8ab0ba4e9fd860690f9b4a6e51ca5df118733b487d8d6/pydantic-2.13.4.tar.gz", hash = "sha256:c40756b57adaa8b1efeeced5c196f3f3b7c435f90e84ea7f443901bec8099ef6", size = 844775, upload-time = "2026-05-06T13:43:05.343Z" }
1350
1350
  wheels = [
@@ -1356,7 +1356,7 @@ name = "pydantic-core"
1356
1356
  version = "2.46.4"
1357
1357
  source = { registry = "https://pypi.org/simple" }
1358
1358
  dependencies = [
1359
- { name = "typing-extensions", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" },
1359
+ { name = "typing-extensions" },
1360
1360
  ]
1361
1361
  sdist = { url = "https://files.pythonhosted.org/packages/9d/56/921726b776ace8d8f5db44c4ef961006580d91dc52b803c489fafd1aa249/pydantic_core-2.46.4.tar.gz", hash = "sha256:62f875393d7f270851f20523dd2e29f082bcc82292d66db2b64ea71f64b6e1c1", size = 471464, upload-time = "2026-05-06T13:37:06.98Z" }
1362
1362
  wheels = [
@@ -1491,12 +1491,12 @@ version = "9.1.0"
1491
1491
  source = { registry = "https://pypi.org/simple" }
1492
1492
  dependencies = [
1493
1493
  { name = "colorama", marker = "sys_platform == 'win32'" },
1494
- { name = "exceptiongroup", marker = "(python_full_version < '3.11' and sys_platform == 'darwin') or (python_full_version < '3.11' and sys_platform == 'linux') or (python_full_version < '3.11' and sys_platform == 'win32')" },
1495
- { name = "iniconfig", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" },
1496
- { name = "packaging", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" },
1497
- { name = "pluggy", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" },
1498
- { name = "pygments", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" },
1499
- { name = "tomli", marker = "(python_full_version < '3.11' and sys_platform == 'darwin') or (python_full_version < '3.11' and sys_platform == 'linux') or (python_full_version < '3.11' and sys_platform == 'win32')" },
1494
+ { name = "exceptiongroup", marker = "python_full_version < '3.11'" },
1495
+ { name = "iniconfig" },
1496
+ { name = "packaging" },
1497
+ { name = "pluggy" },
1498
+ { name = "pygments" },
1499
+ { name = "tomli", marker = "python_full_version < '3.11'" },
1500
1500
  ]
1501
1501
  sdist = { url = "https://files.pythonhosted.org/packages/84/0e/b5858858d74958632c49b72cb25a3976ff9f632397626715be71c89d3971/pytest-9.1.0.tar.gz", hash = "sha256:41dd9148c08072446394cefd3d79701701335a9f4cae69ba92e39f6c7f5c061c", size = 1634181, upload-time = "2026-06-13T18:52:45.983Z" }
1502
1502
  wheels = [
@@ -1508,7 +1508,7 @@ name = "python-dateutil"
1508
1508
  version = "2.9.0.post0"
1509
1509
  source = { registry = "https://pypi.org/simple" }
1510
1510
  dependencies = [
1511
- { name = "six", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" },
1511
+ { name = "six" },
1512
1512
  ]
1513
1513
  sdist = { url = "https://files.pythonhosted.org/packages/66/c0/0c8b6ad9f17a802ee498c46e004a0eb49bc148f2fd230864601a86dcf6db/python-dateutil-2.9.0.post0.tar.gz", hash = "sha256:37dd54208da7e1cd875388217d5e00ebd4179249f90fb72437e91a35459a0ad3", size = 342432, upload-time = "2024-03-01T18:36:20.211Z" }
1514
1514
  wheels = [
@@ -1612,7 +1612,7 @@ name = "randomname"
1612
1612
  version = "0.2.1"
1613
1613
  source = { registry = "https://pypi.org/simple" }
1614
1614
  dependencies = [
1615
- { name = "fire", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" },
1615
+ { name = "fire" },
1616
1616
  ]
1617
1617
  sdist = { url = "https://files.pythonhosted.org/packages/e8/c2/525e9e9b458c3ca493d9bd0871f3ed9b51446d26fe82d462494de188f848/randomname-0.2.1.tar.gz", hash = "sha256:b79b98302ba4479164b0a4f87995b7bebbd1d91012aeda483341e3e58ace520e", size = 64242, upload-time = "2023-01-29T02:42:26.469Z" }
1618
1618
 
@@ -1621,9 +1621,9 @@ name = "rich"
1621
1621
  version = "13.9.4"
1622
1622
  source = { registry = "https://pypi.org/simple" }
1623
1623
  dependencies = [
1624
- { name = "markdown-it-py", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" },
1625
- { name = "pygments", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" },
1626
- { name = "typing-extensions", marker = "(python_full_version < '3.11' and sys_platform == 'darwin') or (python_full_version < '3.11' and sys_platform == 'linux') or (python_full_version < '3.11' and sys_platform == 'win32')" },
1624
+ { name = "markdown-it-py" },
1625
+ { name = "pygments" },
1626
+ { name = "typing-extensions", marker = "python_full_version < '3.11'" },
1627
1627
  ]
1628
1628
  sdist = { url = "https://files.pythonhosted.org/packages/ab/3a/0316b28d0761c6734d6bc14e770d85506c986c85ffb239e688eeaab2c2bc/rich-13.9.4.tar.gz", hash = "sha256:439594978a49a09530cff7ebc4b5c7103ef57baf48d5ea3184f21d9a2befa098", size = 223149, upload-time = "2024-11-01T16:43:57.873Z" }
1629
1629
  wheels = [
@@ -1635,10 +1635,10 @@ name = "rich-click"
1635
1635
  version = "1.9.8"
1636
1636
  source = { registry = "https://pypi.org/simple" }
1637
1637
  dependencies = [
1638
- { name = "click", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" },
1638
+ { name = "click" },
1639
1639
  { name = "colorama", marker = "sys_platform == 'win32'" },
1640
- { name = "rich", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" },
1641
- { name = "typing-extensions", marker = "(python_full_version < '3.11' and sys_platform == 'darwin') or (python_full_version < '3.11' and sys_platform == 'linux') or (python_full_version < '3.11' and sys_platform == 'win32')" },
1640
+ { name = "rich" },
1641
+ { name = "typing-extensions", marker = "python_full_version < '3.11'" },
1642
1642
  ]
1643
1643
  sdist = { url = "https://files.pythonhosted.org/packages/f7/ea/21e4867ea0ef881ffd4c0550fc21a061435e50d6324bcd034396633cbc18/rich_click-1.9.8.tar.gz", hash = "sha256:4008f921da88b5d91646c134ec881c1500e5a6b3f093e90e8f29400e09608371", size = 75363, upload-time = "2026-05-28T19:54:59.144Z" }
1644
1644
  wheels = [
@@ -1684,8 +1684,8 @@ name = "sentry-sdk"
1684
1684
  version = "2.63.0"
1685
1685
  source = { registry = "https://pypi.org/simple" }
1686
1686
  dependencies = [
1687
- { name = "certifi", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" },
1688
- { name = "urllib3", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" },
1687
+ { name = "certifi" },
1688
+ { name = "urllib3" },
1689
1689
  ]
1690
1690
  sdist = { url = "https://files.pythonhosted.org/packages/ba/c8/b3c970a5b186722d276cd40a05b3254e03bccc0208560aff20f612e018e8/sentry_sdk-2.63.0.tar.gz", hash = "sha256:2a1502bf864769275dbc8c2c9fc7a0f7f5e18358180b615d262d13a31ffba216", size = 912449, upload-time = "2026-06-16T12:45:57.553Z" }
1691
1691
  wheels = [
@@ -1694,7 +1694,7 @@ wheels = [
1694
1694
 
1695
1695
  [package.optional-dependencies]
1696
1696
  litestar = [
1697
- { name = "litestar", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" },
1697
+ { name = "litestar" },
1698
1698
  ]
1699
1699
 
1700
1700
  [[package]]
@@ -1729,10 +1729,10 @@ name = "textual"
1729
1729
  version = "0.89.1"
1730
1730
  source = { registry = "https://pypi.org/simple" }
1731
1731
  dependencies = [
1732
- { name = "markdown-it-py", extra = ["linkify", "plugins"], marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" },
1733
- { name = "platformdirs", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" },
1734
- { name = "rich", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" },
1735
- { name = "typing-extensions", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" },
1732
+ { name = "markdown-it-py", extra = ["linkify", "plugins"] },
1733
+ { name = "platformdirs" },
1734
+ { name = "rich" },
1735
+ { name = "typing-extensions" },
1736
1736
  ]
1737
1737
  sdist = { url = "https://files.pythonhosted.org/packages/4b/cb/b3ff0e45d812997a527cb581a4cd602f0b28793450aa26201969fd6ce42c/textual-0.89.1.tar.gz", hash = "sha256:66befe80e2bca5a8c876cd8ceeaf01752267b6b1dc1d0f73071f1f1e15d90cc8", size = 1517074, upload-time = "2024-12-05T15:17:12.903Z" }
1738
1738
  wheels = [
@@ -1819,7 +1819,7 @@ name = "typing-inspection"
1819
1819
  version = "0.4.2"
1820
1820
  source = { registry = "https://pypi.org/simple" }
1821
1821
  dependencies = [
1822
- { name = "typing-extensions", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" },
1822
+ { name = "typing-extensions" },
1823
1823
  ]
1824
1824
  sdist = { url = "https://files.pythonhosted.org/packages/55/e3/70399cb7dd41c10ac53367ae42139cf4b1ca5f36bb3dc6c9d33acdb43655/typing_inspection-0.4.2.tar.gz", hash = "sha256:ba561c48a67c5958007083d386c3295464928b01faa735ab8547c5692e87f464", size = 75949, upload-time = "2025-10-01T02:14:41.687Z" }
1825
1825
  wheels = [
@@ -1858,9 +1858,9 @@ name = "uvicorn"
1858
1858
  version = "0.49.0"
1859
1859
  source = { registry = "https://pypi.org/simple" }
1860
1860
  dependencies = [
1861
- { name = "click", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" },
1862
- { name = "h11", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" },
1863
- { name = "typing-extensions", marker = "(python_full_version < '3.11' and sys_platform == 'darwin') or (python_full_version < '3.11' and sys_platform == 'linux') or (python_full_version < '3.11' and sys_platform == 'win32')" },
1861
+ { name = "click" },
1862
+ { name = "h11" },
1863
+ { name = "typing-extensions", marker = "python_full_version < '3.11'" },
1864
1864
  ]
1865
1865
  sdist = { url = "https://files.pythonhosted.org/packages/c4/1f/fa18009dea8469069cca78a4e877a008ab78f08b064bfc9ab891579077ff/uvicorn-0.49.0.tar.gz", hash = "sha256:ebf4271aa580d9de97f93192d4595176df6e91f9aae919ca73e4fc07df1e66a3", size = 91284, upload-time = "2026-06-03T22:01:30.448Z" }
1866
1866
  wheels = [