zmq-vision 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 (68) hide show
  1. zmq_vision-0.3.0/.env.example +8 -0
  2. zmq_vision-0.3.0/CHANGELOG.md +81 -0
  3. zmq_vision-0.3.0/CONTRIBUTING.md +19 -0
  4. zmq_vision-0.3.0/LICENSE +21 -0
  5. zmq_vision-0.3.0/MANIFEST.in +7 -0
  6. zmq_vision-0.3.0/Makefile +25 -0
  7. zmq_vision-0.3.0/PKG-INFO +278 -0
  8. zmq_vision-0.3.0/README.md +234 -0
  9. zmq_vision-0.3.0/docs/architecture.md +73 -0
  10. zmq_vision-0.3.0/docs/assets/banner.svg +16 -0
  11. zmq_vision-0.3.0/docs/assets/demo-output.txt +6 -0
  12. zmq_vision-0.3.0/docs/assets/demo.svg +17 -0
  13. zmq_vision-0.3.0/docs/assets.md +5 -0
  14. zmq_vision-0.3.0/docs/migration.md +20 -0
  15. zmq_vision-0.3.0/docs/releases/0.3.0.md +12 -0
  16. zmq_vision-0.3.0/docs/releasing.md +17 -0
  17. zmq_vision-0.3.0/docs/validation.md +30 -0
  18. zmq_vision-0.3.0/examples/custom_backend.py +32 -0
  19. zmq_vision-0.3.0/examples/legacy/README.md +5 -0
  20. zmq_vision-0.3.0/examples/legacy/client_async.py +43 -0
  21. zmq_vision-0.3.0/examples/legacy/client_loop.py +55 -0
  22. zmq_vision-0.3.0/examples/legacy/inference_server_tf.py +176 -0
  23. zmq_vision-0.3.0/examples/legacy/inference_server_torch.py +133 -0
  24. zmq_vision-0.3.0/examples/sample.png +0 -0
  25. zmq_vision-0.3.0/pyproject.toml +75 -0
  26. zmq_vision-0.3.0/scripts/check_distribution.py +60 -0
  27. zmq_vision-0.3.0/scripts/render_assets.py +56 -0
  28. zmq_vision-0.3.0/setup.cfg +4 -0
  29. zmq_vision-0.3.0/src/zmq_inference/__init__.py +22 -0
  30. zmq_vision-0.3.0/src/zmq_inference/__main__.py +5 -0
  31. zmq_vision-0.3.0/src/zmq_inference/cli/__init__.py +1 -0
  32. zmq_vision-0.3.0/src/zmq_inference/cli/main.py +217 -0
  33. zmq_vision-0.3.0/src/zmq_inference/client/__init__.py +6 -0
  34. zmq_vision-0.3.0/src/zmq_inference/client/async_client.py +110 -0
  35. zmq_vision-0.3.0/src/zmq_inference/client/base.py +65 -0
  36. zmq_vision-0.3.0/src/zmq_inference/client/sync_client.py +109 -0
  37. zmq_vision-0.3.0/src/zmq_inference/core/__init__.py +12 -0
  38. zmq_vision-0.3.0/src/zmq_inference/core/config.py +85 -0
  39. zmq_vision-0.3.0/src/zmq_inference/core/logging.py +57 -0
  40. zmq_vision-0.3.0/src/zmq_inference/core/protocol.py +159 -0
  41. zmq_vision-0.3.0/src/zmq_inference/data/__init__.py +1 -0
  42. zmq_vision-0.3.0/src/zmq_inference/data/imagenet_classes.json +1 -0
  43. zmq_vision-0.3.0/src/zmq_inference/demo.py +45 -0
  44. zmq_vision-0.3.0/src/zmq_inference/py.typed +0 -0
  45. zmq_vision-0.3.0/src/zmq_inference/server/__init__.py +5 -0
  46. zmq_vision-0.3.0/src/zmq_inference/server/base.py +176 -0
  47. zmq_vision-0.3.0/src/zmq_inference/server/demo_server.py +32 -0
  48. zmq_vision-0.3.0/src/zmq_inference/server/tf_server.py +133 -0
  49. zmq_vision-0.3.0/src/zmq_inference/server/torch_server.py +124 -0
  50. zmq_vision-0.3.0/src/zmq_inference/utils/__init__.py +12 -0
  51. zmq_vision-0.3.0/src/zmq_inference/utils/image.py +77 -0
  52. zmq_vision-0.3.0/src/zmq_inference/utils/labels.py +43 -0
  53. zmq_vision-0.3.0/src/zmq_vision.egg-info/PKG-INFO +278 -0
  54. zmq_vision-0.3.0/src/zmq_vision.egg-info/SOURCES.txt +66 -0
  55. zmq_vision-0.3.0/src/zmq_vision.egg-info/dependency_links.txt +1 -0
  56. zmq_vision-0.3.0/src/zmq_vision.egg-info/entry_points.txt +3 -0
  57. zmq_vision-0.3.0/src/zmq_vision.egg-info/requires.txt +23 -0
  58. zmq_vision-0.3.0/src/zmq_vision.egg-info/top_level.txt +1 -0
  59. zmq_vision-0.3.0/tests/__init__.py +1 -0
  60. zmq_vision-0.3.0/tests/conftest.py +104 -0
  61. zmq_vision-0.3.0/tests/test_backends.py +50 -0
  62. zmq_vision-0.3.0/tests/test_cli.py +85 -0
  63. zmq_vision-0.3.0/tests/test_config.py +34 -0
  64. zmq_vision-0.3.0/tests/test_image_utils.py +90 -0
  65. zmq_vision-0.3.0/tests/test_integration.py +279 -0
  66. zmq_vision-0.3.0/tests/test_protocol.py +151 -0
  67. zmq_vision-0.3.0/tests/test_server.py +75 -0
  68. zmq_vision-0.3.0/tests/test_sync_client.py +21 -0
@@ -0,0 +1,8 @@
1
+ # Reference only: the library reads exported variables, not .env files.
2
+ # On a trusted LAN, explicitly change the server bind host to 0.0.0.0.
3
+ ZMQ_HOST=127.0.0.1
4
+ ZMQ_PORT=5576
5
+ ZMQ_MODEL=mobilenetv2_100
6
+ ZMQ_DEVICE=auto
7
+ ZMQ_SERVER_HOST=127.0.0.1
8
+ ZMQ_TIMEOUT_MS=30000
@@ -0,0 +1,81 @@
1
+ # Changelog
2
+
3
+ All notable changes to this project will be 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
+ ---
9
+
10
+ ## [0.3.0] - 2026-10-25
11
+
12
+ ### Added
13
+
14
+ - ZMQ Vision branding and `zmq-vision` distribution/CLI, retaining `zmq_inference` imports.
15
+ - Download-free TCP demo, readiness protocol, environment diagnostics, and JSON CLI output.
16
+ - Bounded model workers and admission, validation limits, overload responses, and startup failure reporting.
17
+ - Real TCP, concurrent client, timeout, cancellation, shutdown, CLI, and optional real-model tests.
18
+ - CI across supported Python versions, distribution checks, tagged GitHub releases, and manual OIDC PyPI publishing.
19
+ - Architecture, migration, contribution, and release guides; captured demo visualization; missing MIT license file.
20
+
21
+ ### Changed
22
+
23
+ - Moved library to `src/`, sample to `examples/`, and original scripts to `examples/legacy/`.
24
+ - Consolidated metadata into `pyproject.toml`; removed obsolete GPU requirements and duplicate label data.
25
+ - Updated dependency ranges to current supported releases, with ML frameworks kept optional.
26
+ - Included the Windows-only Tornado selector bridge required by PyZMQ asyncio.
27
+ - Applied model-specific timm transforms and direct TensorFlow/Keras inference.
28
+ - Made localhost binding and one client attempt the defaults; health no longer performs inference.
29
+ - Replaced request threads and the in-process proxy with bounded futures and one socket-owning thread.
30
+ - Reset client sockets after timeout/cancellation and correlate every response.
31
+
32
+ See [migration notes](docs/migration.md) for API and default changes.
33
+
34
+ ---
35
+
36
+ ## [0.2.0] - 2026-05-24
37
+
38
+ ### Changed
39
+
40
+ - **Complete package overhaul**: flat scripts migrated to the `zmq_inference` Python package.
41
+ - **Abstract base classes** for server (`BaseInferenceServer`) and client (`BaseInferenceClient`) with clean extension points.
42
+ - **Dataclass-based configuration** (`ServerConfig`, `ClientConfig`) with full environment-variable support.
43
+ - **Typed protocol layer** (`InferenceRequest`, `InferenceResponse`, `Prediction`) with JSON serialisation helpers.
44
+ - **Click CLI** entry point (`zmq-inference serve | infer | health`).
45
+ - **Full test suite** (`tests/`) with pytest, pytest-asyncio, and pytest-cov.
46
+ - **pyproject.toml** build system with optional extras (`torch`, `tf`, `dev`).
47
+ - **Async client** (`AsyncClient`) using `zmq.asyncio`.
48
+ - **Structured logging** via `zmq_inference.core.logging`.
49
+ - **ImageNet labels** bundled as package data under `zmq_inference/data/`.
50
+ - **Makefile** for common development tasks.
51
+ - Updated dependencies to modern versions (pyzmq>=25, Pillow>=9, numpy>=1.23).
52
+ - TensorFlow server modernised: removed `tf.compat.v1` session usage; uses `model.predict` with `tf.function` warm-up.
53
+
54
+ ### Added
55
+
56
+ - `zmq_inference/server/base.py` — abstract `BaseInferenceServer` (threading.Thread + ZMQ loop).
57
+ - `zmq_inference/server/torch_server.py` — PyTorch/timm implementation.
58
+ - `zmq_inference/server/tf_server.py` — TensorFlow/Keras implementation (TF2 style).
59
+ - `zmq_inference/client/base.py` — abstract `BaseInferenceClient`.
60
+ - `zmq_inference/client/sync_client.py` — synchronous client with retry logic.
61
+ - `zmq_inference/client/async_client.py` — asyncio client.
62
+ - `zmq_inference/core/config.py` — `ServerConfig` and `ClientConfig` dataclasses.
63
+ - `zmq_inference/core/protocol.py` — request/response dataclasses.
64
+ - `zmq_inference/core/logging.py` — structured logging helpers.
65
+ - `zmq_inference/utils/image.py` — `encode_image`, `decode_image`, `preprocess_for_display`.
66
+ - `zmq_inference/utils/labels.py` — `load_imagenet_labels`, `get_label` (uses `importlib.resources`).
67
+ - `zmq_inference/cli/main.py` — Click CLI: `serve`, `infer`, `health` commands.
68
+ - `.env.example` — environment variable reference.
69
+ - `.gitignore` — comprehensive Python + ML project ignore rules.
70
+
71
+ ---
72
+
73
+ ## [0.1.0] - 2024-06-12
74
+
75
+ ### Added
76
+
77
+ - Initial ZMQ inference server supporting PyTorch (timm) and TensorFlow (Keras) backends.
78
+ - Synchronous polling client (`client_loop.py`).
79
+ - Async client using `asyncio.to_thread` (`client_async.py`).
80
+ - `imagenet_classes.json` label file.
81
+ - `requirements.txt` with pinned dependencies.
@@ -0,0 +1,19 @@
1
+ # Contributing
2
+
3
+ Use Python 3.10+ and install `python -m pip install -e ".[dev]"` in a virtual environment. The `src/` layout ensures tests exercise an installed package.
4
+
5
+ Before submitting a change:
6
+
7
+ ```bash
8
+ ruff check .
9
+ ruff format --check .
10
+ python -m pytest -m "not torch and not tf"
11
+ python -m build
12
+ python -m twine check dist/*
13
+ ```
14
+
15
+ Test transport changes with real localhost sockets. Keep ML imports lazy so the core package remains usable without either framework. Exercise optional model changes using `tests/test_backends.py`; document which backend, version, device, and weights were tested. Random weights validate plumbing, not classification accuracy.
16
+
17
+ Preserve the `zmq_inference` API unless a breaking change is documented in the changelog. Add a regression test for behavior changes, update affected examples, and describe user-visible outcomes in pull requests. Do not commit weights, tokens, `.env`, or generated build artifacts.
18
+
19
+ See [architecture](docs/architecture.md) for threading and delivery constraints.
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2024-2026 Jamal Saeedi
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,7 @@
1
+ include CHANGELOG.md CONTRIBUTING.md Makefile .env.example
2
+ recursive-include docs *.md *.svg *.txt
3
+ recursive-include examples *.py *.md *.png
4
+ recursive-include scripts *.py
5
+ recursive-include tests *.py
6
+ prune **/__pycache__
7
+ global-exclude *.py[cod]
@@ -0,0 +1,25 @@
1
+ PYTHON ?= python
2
+
3
+ .PHONY: install install-torch install-tf test test-fast lint format format-check build check-dist demo
4
+ install:
5
+ $(PYTHON) -m pip install -e '.[dev]'
6
+ install-torch:
7
+ $(PYTHON) -m pip install -e '.[torch,dev]'
8
+ install-tf:
9
+ $(PYTHON) -m pip install -e '.[tf,dev]'
10
+ test:
11
+ $(PYTHON) -m pytest --cov=zmq_inference --cov-report=term-missing
12
+ test-fast:
13
+ $(PYTHON) -m pytest -m 'not torch and not tf' -q
14
+ lint:
15
+ $(PYTHON) -m ruff check .
16
+ format:
17
+ $(PYTHON) -m ruff format .
18
+ format-check:
19
+ $(PYTHON) -m ruff format --check .
20
+ build:
21
+ $(PYTHON) -m build
22
+ check-dist:
23
+ $(PYTHON) -m twine check dist/*
24
+ demo:
25
+ $(PYTHON) -m zmq_inference demo
@@ -0,0 +1,278 @@
1
+ Metadata-Version: 2.4
2
+ Name: zmq-vision
3
+ Version: 0.3.0
4
+ Summary: Serve image models over ZeroMQ with sync and async Python clients
5
+ Author-email: Jamal Saeedi <jamal.saeedi7@gmail.com>
6
+ License-Expression: MIT
7
+ Project-URL: Homepage, https://github.com/jamal-saeedi/zmq-vision
8
+ Project-URL: Repository, https://github.com/jamal-saeedi/zmq-vision
9
+ Project-URL: Issues, https://github.com/jamal-saeedi/zmq-vision/issues
10
+ Project-URL: Changelog, https://github.com/jamal-saeedi/zmq-vision/blob/main/CHANGELOG.md
11
+ Keywords: zeromq,inference,computer-vision,pytorch,tensorflow
12
+ Classifier: Development Status :: 4 - Beta
13
+ Classifier: Intended Audience :: Developers
14
+ Classifier: Intended Audience :: Science/Research
15
+ Classifier: Programming Language :: Python :: 3
16
+ Classifier: Programming Language :: Python :: 3.10
17
+ Classifier: Programming Language :: Python :: 3.11
18
+ Classifier: Programming Language :: Python :: 3.12
19
+ Classifier: Programming Language :: Python :: 3.13
20
+ Classifier: Programming Language :: Python :: 3.14
21
+ Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
22
+ Requires-Python: >=3.10
23
+ Description-Content-Type: text/markdown
24
+ License-File: LICENSE
25
+ Requires-Dist: pyzmq<28,>=27.2
26
+ Requires-Dist: Pillow<14,>=12.1
27
+ Requires-Dist: click<9,>=8.3
28
+ Requires-Dist: tornado<7,>=6.5.10; sys_platform == "win32"
29
+ Provides-Extra: torch
30
+ Requires-Dist: torch<3,>=2.11; extra == "torch"
31
+ Requires-Dist: torchvision<1,>=0.26; extra == "torch"
32
+ Requires-Dist: timm<2,>=1.0.30; extra == "torch"
33
+ Provides-Extra: tf
34
+ Requires-Dist: tensorflow<3,>=2.21; extra == "tf"
35
+ Provides-Extra: dev
36
+ Requires-Dist: pytest<10,>=8.4; extra == "dev"
37
+ Requires-Dist: pytest-asyncio<2,>=1; extra == "dev"
38
+ Requires-Dist: pytest-cov<8,>=6; extra == "dev"
39
+ Requires-Dist: numpy<3,>=2.2; extra == "dev"
40
+ Requires-Dist: ruff<1,>=0.14; extra == "dev"
41
+ Requires-Dist: build<2,>=1.3; extra == "dev"
42
+ Requires-Dist: twine<8,>=6; extra == "dev"
43
+ Dynamic: license-file
44
+
45
+ <p align="center">
46
+ <img src="https://raw.githubusercontent.com/jamal-saeedi/zmq-vision/main/docs/assets/banner.svg" alt="ZMQ Vision — image models, one messaging interface" width="920">
47
+ </p>
48
+
49
+ <p align="center">
50
+ <a href="https://github.com/jamal-saeedi/zmq-vision/actions/workflows/ci.yml"><img src="https://github.com/jamal-saeedi/zmq-vision/actions/workflows/ci.yml/badge.svg" alt="CI"></a>
51
+ <img src="https://img.shields.io/badge/Python-3.10%E2%80%933.14-3776AB" alt="Python 3.10–3.14">
52
+ <img src="https://img.shields.io/badge/PyZMQ-27.2-7C3AED" alt="PyZMQ 27.2">
53
+ <a href="LICENSE"><img src="https://img.shields.io/badge/license-MIT-10B981" alt="MIT license"></a>
54
+ </p>
55
+
56
+ # ZMQ Vision
57
+
58
+ **Serve image models over ZeroMQ with the same synchronous, asynchronous, and command-line clients.**
59
+
60
+ A small Python library that separates model execution from messaging. Run an ImageNet classifier with PyTorch/timm or TensorFlow/Keras, or implement three methods to serve your own image model. The core package uses PyZMQ, Pillow, and Click, plus a Windows-only Tornado bridge for asyncio.
61
+
62
+ Built by [Jamal Saeedi](https://github.com/jamal-saeedi). This project demonstrates transport design, bounded concurrency, model integration, Python packaging, and tests against real TCP sockets.
63
+
64
+ [Quick start](#quick-start) · [Model backends](#use-a-trained-model) · [Python API](#python-api) · [Architecture](#architecture) · [Development](#development)
65
+
66
+ ## Quick start
67
+
68
+ Python 3.10 or newer; a virtual environment is recommended.
69
+
70
+ ```bash
71
+ git clone https://github.com/jamal-saeedi/zmq-vision.git
72
+ cd zmq-vision
73
+ python -m venv .venv
74
+ # Linux / macOS
75
+ source .venv/bin/activate
76
+ # Windows PowerShell: .venv\Scripts\Activate.ps1
77
+ python -m pip install --upgrade pip
78
+ python -m pip install .
79
+ zmq-vision demo
80
+ ```
81
+
82
+ The demo starts a server on an available localhost TCP port, checks readiness, sends generated images through both clients, checks their responses, and shuts down. It downloads no weights and needs no GPU. Its backend ranks mean RGB intensity; it is **not a trained classifier**.
83
+
84
+ <p align="center">
85
+ <img src="https://raw.githubusercontent.com/jamal-saeedi/zmq-vision/main/docs/assets/demo.svg" alt="Captured terminal output from the working sync and async TCP demo" width="920">
86
+ </p>
87
+
88
+ The image is captured from a real run; timings vary by machine. Run `zmq-vision demo --json` for inspectable output or `zmq-vision doctor` for installed versions.
89
+
90
+ Install directly from GitHub without cloning:
91
+
92
+ ```bash
93
+ python -m pip install "git+https://github.com/jamal-saeedi/zmq-vision.git"
94
+ ```
95
+
96
+ The distribution is named `zmq-vision`. PyPI publication is a separate release step; the source install above works before publication. Existing `zmq_inference` imports and the `zmq-inference` command remain supported. See [migration notes](docs/migration.md).
97
+
98
+ ## Use a trained model
99
+
100
+ Choose one extra; neither ML framework is required by the core package.
101
+
102
+ | Backend | Install from this checkout | Model | First run |
103
+ | --- | --- | --- | --- |
104
+ | Demo | `python -m pip install .` | Mean RGB intensity | Offline |
105
+ | PyTorch | `python -m pip install ".[torch]"` | ImageNet-1k models from timm | Downloads weights |
106
+ | TensorFlow | `python -m pip install ".[tf]"` | Keras MobileNet | Downloads weights |
107
+
108
+ ```bash
109
+ # Terminal 1: PyTorch, CPU
110
+ zmq-vision serve --backend torch --model mobilenetv2_100 --device cpu
111
+
112
+ # Terminal 2
113
+ zmq-vision health
114
+ zmq-vision infer examples/sample.png
115
+ zmq-vision infer examples/sample.png --async --json
116
+ ```
117
+
118
+ Or start TensorFlow with `zmq-vision serve --backend tf --device cpu`.
119
+
120
+ For a fully offline transport example, use `zmq-vision serve --backend demo`, then send any image. `--no-pretrained` is available for ML smoke tests; randomly initialized model predictions are not meaningful. Framework and model-weight licenses apply separately. CPU/GPU installation and Python support depend on the selected framework's wheels; use Python 3.12 for the tested ML configuration.
121
+
122
+ ## Python API
123
+
124
+ ### Synchronous
125
+
126
+ ```python
127
+ from zmq_inference import ClientConfig
128
+ from zmq_inference.client import SyncClient
129
+
130
+ with SyncClient(ClientConfig(host="127.0.0.1", port=5576, timeout_ms=5000)) as client:
131
+ assert client.ping().ok
132
+ response = client.infer("examples/sample.png")
133
+ if not response.ok:
134
+ raise RuntimeError(response.error)
135
+ for prediction in response.predictions:
136
+ print(prediction.label, prediction.confidence)
137
+ ```
138
+
139
+ ### Asynchronous
140
+
141
+ ```python
142
+ import asyncio
143
+ from zmq_inference.client.async_client import AsyncClient
144
+
145
+
146
+ async def main():
147
+ async with AsyncClient() as client:
148
+ response = await client.infer("examples/sample.png")
149
+ print(response)
150
+
151
+
152
+ asyncio.run(main())
153
+ ```
154
+
155
+ One async client serializes its exchanges so concurrent callers cannot consume each other's replies. Use multiple clients for concurrent server requests. A synchronous client belongs to the thread that connected it.
156
+
157
+ ### Your own backend
158
+
159
+ Implement `load_model()`, `preprocess(image)`, and `predict(input)` on `BaseInferenceServer`. Transport, validation, error responses, queue limits, and lifecycle are provided. See the complete [custom backend example](examples/custom_backend.py).
160
+
161
+ ```python
162
+ from zmq_inference import ServerConfig
163
+ from zmq_inference.server.demo_server import DemoInferenceServer
164
+
165
+ server = DemoInferenceServer(ServerConfig(port=5576))
166
+ server.start()
167
+ try:
168
+ server.wait_until_ready(timeout=10) # surfaces model-load and bind errors
169
+ server.join()
170
+ except KeyboardInterrupt:
171
+ pass
172
+ finally:
173
+ server.stop()
174
+ server.join()
175
+ ```
176
+
177
+ ## Architecture
178
+
179
+ ```mermaid
180
+ flowchart LR
181
+ A[Sync client] --> D[DEALER sockets]
182
+ B[Async clients] --> D
183
+ C[CLI] --> D
184
+ D <-->|TCP · JSON · request IDs| R[ROUTER\nnetwork thread]
185
+ R --> Q[Bounded pending work]
186
+ Q --> W[Model worker pool]
187
+ W --> M{Backend}
188
+ M --> P[PyTorch / timm]
189
+ M --> T[TensorFlow / Keras]
190
+ M --> X[Demo / custom model]
191
+ X --> F[Completed responses]
192
+ P --> F
193
+ T --> F
194
+ F --> R
195
+ ```
196
+
197
+ - **Socket ownership:** only the network thread touches the server socket. Workers return results through futures.
198
+ - **Bounded work:** one model worker and at most 16 accepted requests by default; overload returns an explicit busy response. Increase workers only for a thread-safe backend.
199
+ - **Response correlation:** clients match request IDs and replace their socket after a timeout or cancellation. Late replies cannot be returned as the next request's result.
200
+ - **Predictable lifecycle:** readiness means the model loaded and TCP bound. Shutdown stops admission, cancels queued work, and waits for active predictions.
201
+ - **Model preprocessing:** PyTorch uses the model's timm data configuration; TensorFlow uses MobileNet preprocessing and bundled ImageNet labels.
202
+
203
+ The earlier ROUTER/DEALER in-process proxy and thread-per-request design have been replaced. The client/server TCP connection still uses DEALER/ROUTER. [Protocol and limits](docs/architecture.md) cover delivery semantics and tradeoffs.
204
+
205
+ ## Configuration
206
+
207
+ Explicit Python arguments or CLI options override environment defaults. `.env` files are not loaded automatically; `.env.example` documents variables you can export.
208
+
209
+ | Variable | Default | Used by |
210
+ | --- | --- | --- |
211
+ | `ZMQ_HOST` | `127.0.0.1` | Server bind host |
212
+ | `ZMQ_SERVER_HOST` | `127.0.0.1` | Client destination |
213
+ | `ZMQ_PORT` | `5576` | Both |
214
+ | `ZMQ_MODEL` | `mobilenetv2_100` | PyTorch model |
215
+ | `ZMQ_DEVICE` | `auto` | `auto`, `cpu`, or `cuda` |
216
+ | `ZMQ_TIMEOUT_MS` | `30000` | Client timeout per attempt |
217
+
218
+ Use `zmq-vision serve --help` and `zmq-vision infer --help` for options. Python `ServerConfig` also exposes message and decoded-image limits: 8 MiB per frame and 16 million pixels by default. Oversized frames are disconnected by libzmq; the client reaches its timeout. `--top-k` on `infer` limits displayed results; `serve --top-k` controls how many predictions are returned.
219
+
220
+ Defaults bind to localhost. For a trusted LAN, explicitly use `--host 0.0.0.0`; clients specify the server's address. This protocol has no built-in authentication or encryption. Use a secured private network or tunnel for remote traffic.
221
+
222
+ ## Development
223
+
224
+ ```bash
225
+ python -m pip install -e ".[dev]"
226
+ python -m pytest -m "not torch and not tf"
227
+ ruff check .
228
+ ruff format --check .
229
+ python -m build
230
+ python -m twine check dist/*
231
+ ```
232
+
233
+ Optional real-model tests (CPU, random weights, no downloads):
234
+
235
+ ```bash
236
+ python -m pip install -e ".[torch,tf,dev]"
237
+ python -m pytest tests/test_backends.py
238
+ # Also download and exercise pretrained weights:
239
+ ZMQ_VISION_TEST_PRETRAINED=1 python -m pytest tests/test_backends.py
240
+ ```
241
+
242
+ Core CI covers Python 3.10–3.14 on Linux, with additional Windows and macOS jobs. Separate jobs exercise both ML backends on Python 3.12. Distribution checks install a built wheel in a new environment and run the demo outside the checkout. See the [recorded validation](docs/validation.md), [contributing guide](CONTRIBUTING.md), and [release instructions](docs/releasing.md).
243
+
244
+ ```text
245
+ src/zmq_inference/ Installable library; legacy import name preserved
246
+ cli/ serve, infer, health, demo, doctor
247
+ client/ Synchronous and asyncio transports
248
+ core/ Configuration, protocol, logging
249
+ server/ Shared lifecycle and optional model backends
250
+ data/ Bundled ImageNet labels
251
+ utils/ Image serialization and resource loading
252
+ examples/ Runnable custom backend and original sample image
253
+ legacy/ Archived 2024 scripts; separate wire protocol
254
+ tests/ Unit, real TCP, CLI, and optional ML tests
255
+ docs/ Architecture, migration, release guide, visual assets
256
+ .github/workflows/ Checks, tagged GitHub releases, manual PyPI publishing
257
+ ```
258
+
259
+ ## Scope and limitations
260
+
261
+ This is an image inference transport library. It does not train models, batch requests, guarantee exactly-once execution, or provide multi-host scheduling. Retries can repeat inference; treat custom predictors as side-effect free. Health checks share worker admission and may time out under load. A hung model call can delay shutdown because Python cannot safely kill its thread.
262
+
263
+ A language model is not included: text generation needs a text protocol, token limits, and different response semantics. Adding a small LLM without that design would dilute the working vision interface. The backend extension point supports future image-oriented models without making their dependencies mandatory.
264
+
265
+ ## Troubleshooting
266
+
267
+ | Symptom | Check |
268
+ | --- | --- |
269
+ | Timeout | `zmq-vision health --host HOST --port PORT`; confirm server readiness, firewall, and model load |
270
+ | Missing backend | Install the corresponding `[torch]` or `[tf]` extra |
271
+ | Address already in use | Choose another `--port`; startup failures return a nonzero exit status |
272
+ | Unexpected model result | Use pretrained weights, an ImageNet-1k model, and a suitable input image |
273
+ | Runtime / GPU failure | Run `zmq-vision doctor`, try `--device cpu`, and verify framework installation |
274
+ | Server busy | Reduce concurrent clients or size `--max-pending` for your deployment |
275
+
276
+ ## License
277
+
278
+ [MIT](LICENSE). Original sample image and ImageNet label mapping are retained from this repository's earlier versions; their original upstream provenance was not recorded. See [asset notes](docs/assets.md).