caption-flow 0.5.6__tar.gz → 0.5.7__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 (77) hide show
  1. {caption_flow-0.5.6 → caption_flow-0.5.7}/PKG-INFO +45 -24
  2. {caption_flow-0.5.6 → caption_flow-0.5.7}/README.md +22 -1
  3. {caption_flow-0.5.6 → caption_flow-0.5.7}/setup.py +11 -11
  4. {caption_flow-0.5.6 → caption_flow-0.5.7}/src/caption_flow/__init__.py +1 -1
  5. {caption_flow-0.5.6 → caption_flow-0.5.7}/src/caption_flow/models.py +8 -5
  6. {caption_flow-0.5.6 → caption_flow-0.5.7}/src/caption_flow/orchestrator.py +21 -13
  7. {caption_flow-0.5.6 → caption_flow-0.5.7}/src/caption_flow/processors/webdataset.py +187 -15
  8. {caption_flow-0.5.6 → caption_flow-0.5.7}/src/caption_flow/storage/exporter.py +20 -6
  9. {caption_flow-0.5.6 → caption_flow-0.5.7}/src/caption_flow/storage/manager.py +14 -2
  10. {caption_flow-0.5.6 → caption_flow-0.5.7}/src/caption_flow/utils/chunk_tracker.py +45 -11
  11. {caption_flow-0.5.6 → caption_flow-0.5.7}/src/caption_flow/utils/image_processor.py +8 -3
  12. caption_flow-0.5.7/src/caption_flow/utils/output_policy.py +207 -0
  13. caption_flow-0.5.7/src/caption_flow/utils/retry_queue.py +50 -0
  14. caption_flow-0.5.7/src/caption_flow/utils/unicode_utils.py +30 -0
  15. {caption_flow-0.5.6 → caption_flow-0.5.7}/src/caption_flow/utils/vllm_config.py +39 -11
  16. {caption_flow-0.5.6 → caption_flow-0.5.7}/src/caption_flow/workers/caption.py +177 -206
  17. {caption_flow-0.5.6 → caption_flow-0.5.7}/src/caption_flow/workers/openai_compatible.py +71 -361
  18. caption_flow-0.5.7/src/caption_flow/workers/pipeline.py +127 -0
  19. {caption_flow-0.5.6 → caption_flow-0.5.7}/src/caption_flow.egg-info/PKG-INFO +45 -24
  20. {caption_flow-0.5.6 → caption_flow-0.5.7}/src/caption_flow.egg-info/SOURCES.txt +15 -0
  21. {caption_flow-0.5.6 → caption_flow-0.5.7}/src/caption_flow.egg-info/requires.txt +22 -22
  22. caption_flow-0.5.7/tests/test_caption_pipeline.py +413 -0
  23. caption_flow-0.5.7/tests/test_chunk_completion_index.py +193 -0
  24. {caption_flow-0.5.6 → caption_flow-0.5.7}/tests/test_config_reload.py +20 -0
  25. caption_flow-0.5.7/tests/test_dependency_config.py +71 -0
  26. {caption_flow-0.5.6 → caption_flow-0.5.7}/tests/test_exporter.py +76 -3
  27. {caption_flow-0.5.6 → caption_flow-0.5.7}/tests/test_openai_compatible_worker.py +2 -1
  28. caption_flow-0.5.7/tests/test_orchestrator_storage_order.py +32 -0
  29. caption_flow-0.5.7/tests/test_result_acceptance.py +229 -0
  30. caption_flow-0.5.7/tests/test_retry_queue.py +132 -0
  31. caption_flow-0.5.7/tests/test_unicode_utils.py +69 -0
  32. caption_flow-0.5.7/tests/test_webdataset_cache_wait.py +118 -0
  33. caption_flow-0.5.7/tests/test_webdataset_prefetch.py +140 -0
  34. {caption_flow-0.5.6 → caption_flow-0.5.7}/tests/test_webdataset_ranges.py +31 -2
  35. caption_flow-0.5.7/tests/test_webshart_dependency.py +27 -0
  36. caption_flow-0.5.7/tests/test_webshart_export_contract.py +183 -0
  37. {caption_flow-0.5.6 → caption_flow-0.5.7}/tests/test_worker_caption.py +9 -28
  38. {caption_flow-0.5.6 → caption_flow-0.5.7}/LICENSE +0 -0
  39. {caption_flow-0.5.6 → caption_flow-0.5.7}/pyproject.toml +0 -0
  40. {caption_flow-0.5.6 → caption_flow-0.5.7}/setup.cfg +0 -0
  41. {caption_flow-0.5.6 → caption_flow-0.5.7}/src/caption_flow/cli.py +0 -0
  42. {caption_flow-0.5.6 → caption_flow-0.5.7}/src/caption_flow/monitor.py +0 -0
  43. {caption_flow-0.5.6 → caption_flow-0.5.7}/src/caption_flow/processors/__init__.py +0 -0
  44. {caption_flow-0.5.6 → caption_flow-0.5.7}/src/caption_flow/processors/base.py +0 -0
  45. {caption_flow-0.5.6 → caption_flow-0.5.7}/src/caption_flow/processors/huggingface.py +0 -0
  46. {caption_flow-0.5.6 → caption_flow-0.5.7}/src/caption_flow/processors/local_filesystem.py +0 -0
  47. {caption_flow-0.5.6 → caption_flow-0.5.7}/src/caption_flow/storage/__init__.py +0 -0
  48. {caption_flow-0.5.6 → caption_flow-0.5.7}/src/caption_flow/utils/__init__.py +0 -0
  49. {caption_flow-0.5.6 → caption_flow-0.5.7}/src/caption_flow/utils/auth.py +0 -0
  50. {caption_flow-0.5.6 → caption_flow-0.5.7}/src/caption_flow/utils/caption_utils.py +0 -0
  51. {caption_flow-0.5.6 → caption_flow-0.5.7}/src/caption_flow/utils/certificates.py +0 -0
  52. {caption_flow-0.5.6 → caption_flow-0.5.7}/src/caption_flow/utils/checkpoint_tracker.py +0 -0
  53. {caption_flow-0.5.6 → caption_flow-0.5.7}/src/caption_flow/utils/json_utils.py +0 -0
  54. {caption_flow-0.5.6 → caption_flow-0.5.7}/src/caption_flow/utils/prompt_template.py +0 -0
  55. {caption_flow-0.5.6 → caption_flow-0.5.7}/src/caption_flow/viewer.py +0 -0
  56. {caption_flow-0.5.6 → caption_flow-0.5.7}/src/caption_flow/workers/base.py +0 -0
  57. {caption_flow-0.5.6 → caption_flow-0.5.7}/src/caption_flow/workers/data.py +0 -0
  58. {caption_flow-0.5.6 → caption_flow-0.5.7}/src/caption_flow.egg-info/dependency_links.txt +0 -0
  59. {caption_flow-0.5.6 → caption_flow-0.5.7}/src/caption_flow.egg-info/entry_points.txt +0 -0
  60. {caption_flow-0.5.6 → caption_flow-0.5.7}/src/caption_flow.egg-info/top_level.txt +0 -0
  61. {caption_flow-0.5.6 → caption_flow-0.5.7}/tests/test_caption_utils.py +0 -0
  62. {caption_flow-0.5.6 → caption_flow-0.5.7}/tests/test_certificates.py +0 -0
  63. {caption_flow-0.5.6 → caption_flow-0.5.7}/tests/test_cli.py +0 -0
  64. {caption_flow-0.5.6 → caption_flow-0.5.7}/tests/test_duplicate_job_assignments.py +0 -0
  65. {caption_flow-0.5.6 → caption_flow-0.5.7}/tests/test_fix_verification.py +0 -0
  66. {caption_flow-0.5.6 → caption_flow-0.5.7}/tests/test_huggingface_ranges.py +0 -0
  67. {caption_flow-0.5.6 → caption_flow-0.5.7}/tests/test_image_processor.py +0 -0
  68. {caption_flow-0.5.6 → caption_flow-0.5.7}/tests/test_json_utils.py +0 -0
  69. {caption_flow-0.5.6 → caption_flow-0.5.7}/tests/test_main.py +0 -0
  70. {caption_flow-0.5.6 → caption_flow-0.5.7}/tests/test_monitor.py +0 -0
  71. {caption_flow-0.5.6 → caption_flow-0.5.7}/tests/test_processors.py +0 -0
  72. {caption_flow-0.5.6 → caption_flow-0.5.7}/tests/test_range_level_distribution.py +0 -0
  73. {caption_flow-0.5.6 → caption_flow-0.5.7}/tests/test_storage_components.py +0 -0
  74. {caption_flow-0.5.6 → caption_flow-0.5.7}/tests/test_viewer.py +0 -0
  75. {caption_flow-0.5.6 → caption_flow-0.5.7}/tests/test_vllm_config.py +0 -0
  76. {caption_flow-0.5.6 → caption_flow-0.5.7}/tests/test_worker_reconnection_complete.py +0 -0
  77. {caption_flow-0.5.6 → caption_flow-0.5.7}/tests/test_worker_reconnection_sequence.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: caption-flow
3
- Version: 0.5.6
3
+ Version: 0.5.7
4
4
  Summary: Self-contained distributed community captioning system
5
5
  Author: bghira
6
6
  Author-email: bghira <bghira@users.github.com>
@@ -31,7 +31,7 @@ Requires-Dist: trainingsample<0.4.0,>=0.3.2
31
31
  Requires-Dist: pandas<4.0.0,>=3.0.0
32
32
  Requires-Dist: datasets<6.0.0,>=5.0.0
33
33
  Requires-Dist: boto3<2.0.0,>=1.43.0
34
- Requires-Dist: webshart<0.6.0,>=0.5.2
34
+ Requires-Dist: webshart<0.7.0,>=0.6.0
35
35
  Requires-Dist: pylance<9.0.0,>=8.0.0
36
36
  Requires-Dist: duckdb<2.0.0,>=1.5.0
37
37
  Requires-Dist: aiohttp<4.0.0,>=3.13.3
@@ -43,35 +43,35 @@ Requires-Dist: requests<3.0.0,>=2.32.0
43
43
  Requires-Dist: tqdm<5.0.0,>=4.67.0
44
44
  Requires-Dist: urwid<5.0.0,>=3.0.2
45
45
  Provides-Extra: vllm
46
- Requires-Dist: vllm<0.26.0,>=0.25.1; extra == "vllm"
47
- Requires-Dist: torch==2.11.0; extra == "vllm"
48
- Requires-Dist: torchvision==0.26.0; extra == "vllm"
46
+ Requires-Dist: vllm<0.32.0,>=0.31.0; extra == "vllm"
47
+ Requires-Dist: torch==2.13.0; extra == "vllm"
48
+ Requires-Dist: torchvision==0.28.0; extra == "vllm"
49
49
  Requires-Dist: torchaudio==2.11.0; extra == "vllm"
50
- Requires-Dist: transformers<6.0.0,>=5.5.3; extra == "vllm"
50
+ Requires-Dist: transformers<5.18.0,>=5.10.4; extra == "vllm"
51
51
  Requires-Dist: qwen-vl-utils<0.1.0,>=0.0.14; extra == "vllm"
52
52
  Provides-Extra: captioning
53
- Requires-Dist: vllm<0.26.0,>=0.25.1; extra == "captioning"
54
- Requires-Dist: torch==2.11.0; extra == "captioning"
55
- Requires-Dist: torchvision==0.26.0; extra == "captioning"
53
+ Requires-Dist: vllm<0.32.0,>=0.31.0; extra == "captioning"
54
+ Requires-Dist: torch==2.13.0; extra == "captioning"
55
+ Requires-Dist: torchvision==0.28.0; extra == "captioning"
56
56
  Requires-Dist: torchaudio==2.11.0; extra == "captioning"
57
- Requires-Dist: transformers<6.0.0,>=5.5.3; extra == "captioning"
57
+ Requires-Dist: transformers<5.18.0,>=5.10.4; extra == "captioning"
58
58
  Requires-Dist: qwen-vl-utils<0.1.0,>=0.0.14; extra == "captioning"
59
59
  Provides-Extra: openai
60
60
  Provides-Extra: cpu
61
- Requires-Dist: torch>=2.11.0; extra == "cpu"
62
- Requires-Dist: torchvision>=0.26.0; extra == "cpu"
61
+ Requires-Dist: torch>=2.13.0; extra == "cpu"
62
+ Requires-Dist: torchvision>=0.28.0; extra == "cpu"
63
63
  Requires-Dist: torchaudio>=2.11.0; extra == "cpu"
64
64
  Provides-Extra: cuda
65
- Requires-Dist: torch>=2.11.0; extra == "cuda"
66
- Requires-Dist: torchvision>=0.26.0; extra == "cuda"
65
+ Requires-Dist: torch>=2.13.0; extra == "cuda"
66
+ Requires-Dist: torchvision>=0.28.0; extra == "cuda"
67
67
  Requires-Dist: torchaudio>=2.11.0; extra == "cuda"
68
68
  Provides-Extra: cuda13
69
- Requires-Dist: torch>=2.11.0; extra == "cuda13"
70
- Requires-Dist: torchvision>=0.26.0; extra == "cuda13"
69
+ Requires-Dist: torch>=2.13.0; extra == "cuda13"
70
+ Requires-Dist: torchvision>=0.28.0; extra == "cuda13"
71
71
  Requires-Dist: torchaudio>=2.11.0; extra == "cuda13"
72
72
  Provides-Extra: rocm
73
- Requires-Dist: torch>=2.11.0; extra == "rocm"
74
- Requires-Dist: torchvision>=0.26.0; extra == "rocm"
73
+ Requires-Dist: torch>=2.13.0; extra == "rocm"
74
+ Requires-Dist: torchvision>=0.28.0; extra == "rocm"
75
75
  Requires-Dist: torchaudio>=2.11.0; extra == "rocm"
76
76
  Provides-Extra: apple
77
77
  Requires-Dist: torch>=2.13.0; extra == "apple"
@@ -82,16 +82,16 @@ Provides-Extra: dev
82
82
  Requires-Dist: pytest<9.0.0,>=8.0.0; extra == "dev"
83
83
  Requires-Dist: pytest-asyncio<2.0.0,>=1.1.0; extra == "dev"
84
84
  Requires-Dist: pytest-cov<7.0.0,>=6.0.0; extra == "dev"
85
- Requires-Dist: transformers<6.0.0,>=5.5.3; extra == "dev"
85
+ Requires-Dist: transformers<5.18.0,>=5.10.4; extra == "dev"
86
86
  Requires-Dist: black<26.0.0,>=25.0.0; extra == "dev"
87
87
  Requires-Dist: ruff<1.0.0,>=0.12.0; extra == "dev"
88
88
  Requires-Dist: mypy<2.0.0,>=1.17.0; extra == "dev"
89
89
  Provides-Extra: all
90
- Requires-Dist: vllm<0.26.0,>=0.25.1; extra == "all"
91
- Requires-Dist: torch==2.11.0; extra == "all"
92
- Requires-Dist: torchvision==0.26.0; extra == "all"
90
+ Requires-Dist: vllm<0.32.0,>=0.31.0; extra == "all"
91
+ Requires-Dist: torch==2.13.0; extra == "all"
92
+ Requires-Dist: torchvision==0.28.0; extra == "all"
93
93
  Requires-Dist: torchaudio==2.11.0; extra == "all"
94
- Requires-Dist: transformers<6.0.0,>=5.5.3; extra == "all"
94
+ Requires-Dist: transformers<5.18.0,>=5.10.4; extra == "all"
95
95
  Requires-Dist: qwen-vl-utils<0.1.0,>=0.0.14; extra == "all"
96
96
  Dynamic: author
97
97
  Dynamic: license-file
@@ -128,6 +128,11 @@ pip install --upgrade pip
128
128
  pip install "caption-flow[vllm]"
129
129
  ```
130
130
 
131
+ The Linux GPU worker extra targets vLLM 0.31.x with PyTorch 2.13.0,
132
+ torchvision 0.28.0, and torchaudio 2.11.0 (as required by vLLM), plus
133
+ Transformers 5.10.4–5.17.x. To upgrade an existing worker from a checkout,
134
+ run `pip install --upgrade -e ".[vllm]"` in its environment and restart it.
135
+
131
136
  An OpenAI-compatible API worker does not need the GPU dependencies:
132
137
 
133
138
  ```bash
@@ -220,7 +225,7 @@ Options:
220
225
  * **csv**: exports CSV-compatible data columns to the `--output` path containing incomplete metadata
221
226
  * **json**: creates a `.json` file for each sample inside the `--output` subdirectory containing **complete** metadata; useful for webdatasets
222
227
  * **txt**: creates `.txt` file for each sample inside the `--output` subdirectory containing ONLY captions
223
- * **webshart**: updates an **existing per-shard metadata `.json` file** by writing captions under the plural `captions` key. for this format, pass `--output` as the path to the existing shard metadata JSON file when exporting one shard. if you export multiple shards, pass `--output` as a directory containing one existing `{shard_name}.json` file per shard.
228
+ * **webshart**: updates an **existing per-shard metadata `.json` file** by writing captions under the plural `captions` key. Strings, native JSON objects, and mixed lists are supported. JSON objects stored as text in Lance output columns are decoded during export, preserving nested values instead of writing Python dictionary representations. Native objects supplied through `Caption.outputs` are encoded as JSON only for Lance persistence. Requires webshart 0.6.0 or newer for native JSON caption support (included in the base dependencies). For this format, pass `--output` as the path to the existing shard metadata JSON file when exporting one shard. If you export multiple shards, pass `--output` as a directory containing one existing `{shard_name}.json` file per shard.
224
229
  * **huggingface_hub**: creates a dataset on Hugging Face Hub, possibly `--private` and `--nsfw` where necessary
225
230
  * **all**: creates the directory/file-generating export formats in a specified `--output` directory. prefer a directory here; `webshart` is a special case that expects existing per-shard metadata `.json` files rather than creating new metadata files.
226
231
 
@@ -279,6 +284,13 @@ prompts, sampling, and output fields. Existing `vllm:` configurations are also
279
284
  accepted by API workers for backward compatibility; endpoint-local `model`
280
285
  values take precedence over the broadcast model name.
281
286
 
287
+ Direct vLLM and API workers share JSON validation, output transformations,
288
+ refusal detection, and semantic retries. Configure those under
289
+ `inference.output_processing`; use `response_format` and `retry_response_format`
290
+ for constrained decoding on either backend. See the
291
+ [structured JSON recipe](recipes/STRUCTURED_JSON.md) for examples and native
292
+ vLLM tuning details.
293
+
282
294
  ---
283
295
 
284
296
  ## dataset formats
@@ -288,6 +300,15 @@ values take precedence over the broadcast model name.
288
300
  * webdatasets shards containing full image data; also can be hosted on the hub
289
301
  * local folder filled with images; orchestrator will serve the data to workers
290
302
 
303
+ For indexed Webshart reads, set `orchestrator.webshart_prefetch_shards: 1` to
304
+ download the next shard in the background while captioning the current one.
305
+ The default is `0` (disabled). This works with both direct vLLM and API workers,
306
+ does not move the sample cursor, and is ignored for explicit remote-range reads.
307
+ Size `shard_cache_gb` to hold the current shard plus the lookahead. Prefetch
308
+ retries are spaced by `webshart_cache_retry_interval` (30 seconds by default);
309
+ a prefetch failure never marks samples processed or prevents reading the
310
+ current cached shard.
311
+
291
312
  ## configuration path
292
313
 
293
314
  ### config discovery order
@@ -27,6 +27,11 @@ pip install --upgrade pip
27
27
  pip install "caption-flow[vllm]"
28
28
  ```
29
29
 
30
+ The Linux GPU worker extra targets vLLM 0.31.x with PyTorch 2.13.0,
31
+ torchvision 0.28.0, and torchaudio 2.11.0 (as required by vLLM), plus
32
+ Transformers 5.10.4–5.17.x. To upgrade an existing worker from a checkout,
33
+ run `pip install --upgrade -e ".[vllm]"` in its environment and restart it.
34
+
30
35
  An OpenAI-compatible API worker does not need the GPU dependencies:
31
36
 
32
37
  ```bash
@@ -119,7 +124,7 @@ Options:
119
124
  * **csv**: exports CSV-compatible data columns to the `--output` path containing incomplete metadata
120
125
  * **json**: creates a `.json` file for each sample inside the `--output` subdirectory containing **complete** metadata; useful for webdatasets
121
126
  * **txt**: creates `.txt` file for each sample inside the `--output` subdirectory containing ONLY captions
122
- * **webshart**: updates an **existing per-shard metadata `.json` file** by writing captions under the plural `captions` key. for this format, pass `--output` as the path to the existing shard metadata JSON file when exporting one shard. if you export multiple shards, pass `--output` as a directory containing one existing `{shard_name}.json` file per shard.
127
+ * **webshart**: updates an **existing per-shard metadata `.json` file** by writing captions under the plural `captions` key. Strings, native JSON objects, and mixed lists are supported. JSON objects stored as text in Lance output columns are decoded during export, preserving nested values instead of writing Python dictionary representations. Native objects supplied through `Caption.outputs` are encoded as JSON only for Lance persistence. Requires webshart 0.6.0 or newer for native JSON caption support (included in the base dependencies). For this format, pass `--output` as the path to the existing shard metadata JSON file when exporting one shard. If you export multiple shards, pass `--output` as a directory containing one existing `{shard_name}.json` file per shard.
123
128
  * **huggingface_hub**: creates a dataset on Hugging Face Hub, possibly `--private` and `--nsfw` where necessary
124
129
  * **all**: creates the directory/file-generating export formats in a specified `--output` directory. prefer a directory here; `webshart` is a special case that expects existing per-shard metadata `.json` files rather than creating new metadata files.
125
130
 
@@ -178,6 +183,13 @@ prompts, sampling, and output fields. Existing `vllm:` configurations are also
178
183
  accepted by API workers for backward compatibility; endpoint-local `model`
179
184
  values take precedence over the broadcast model name.
180
185
 
186
+ Direct vLLM and API workers share JSON validation, output transformations,
187
+ refusal detection, and semantic retries. Configure those under
188
+ `inference.output_processing`; use `response_format` and `retry_response_format`
189
+ for constrained decoding on either backend. See the
190
+ [structured JSON recipe](recipes/STRUCTURED_JSON.md) for examples and native
191
+ vLLM tuning details.
192
+
181
193
  ---
182
194
 
183
195
  ## dataset formats
@@ -187,6 +199,15 @@ values take precedence over the broadcast model name.
187
199
  * webdatasets shards containing full image data; also can be hosted on the hub
188
200
  * local folder filled with images; orchestrator will serve the data to workers
189
201
 
202
+ For indexed Webshart reads, set `orchestrator.webshart_prefetch_shards: 1` to
203
+ download the next shard in the background while captioning the current one.
204
+ The default is `0` (disabled). This works with both direct vLLM and API workers,
205
+ does not move the sample cursor, and is ignored for explicit remote-range reads.
206
+ Size `shard_cache_gb` to hold the current shard plus the lookahead. Prefetch
207
+ retries are spaced by `webshart_cache_retry_interval` (30 seconds by default);
208
+ a prefetch failure never marks samples processed or prevents reading the
209
+ current cached shard.
210
+
190
211
  ## configuration path
191
212
 
192
213
  ### config discovery order
@@ -38,7 +38,7 @@ base_deps = [
38
38
  "pandas>=3.0.0,<4.0.0",
39
39
  "datasets>=5.0.0,<6.0.0",
40
40
  "boto3>=1.43.0,<2.0.0",
41
- "webshart>=0.5.2,<0.6.0",
41
+ "webshart>=0.6.0,<0.7.0",
42
42
  "pylance>=8.0.0,<9.0.0",
43
43
  "duckdb>=1.5.0,<2.0.0",
44
44
  "aiohttp>=3.13.3,<4.0.0",
@@ -51,21 +51,21 @@ base_deps = [
51
51
  "urwid>=3.0.2,<5.0.0",
52
52
  ]
53
53
 
54
- # vLLM 0.25.1 currently selects the PyTorch 2.11.0 family. Keep these
55
- # explicit so a worker install cannot silently combine incompatible torch
56
- # packages when another dependency broadens its requirements.
54
+ # Match vLLM 0.31's published requirements, including torchaudio 2.11.0.
55
+ # Keep these explicit so a worker install cannot silently combine incompatible
56
+ # torch packages when another dependency broadens its requirements.
57
57
  vllm_deps = [
58
- "vllm>=0.25.1,<0.26.0",
59
- "torch==2.11.0",
60
- "torchvision==0.26.0",
58
+ "vllm>=0.31.0,<0.32.0",
59
+ "torch==2.13.0",
60
+ "torchvision==0.28.0",
61
61
  "torchaudio==2.11.0",
62
- "transformers>=5.5.3,<6.0.0",
62
+ "transformers>=5.10.4,<5.18.0",
63
63
  "qwen-vl-utils>=0.0.14,<0.1.0",
64
64
  ]
65
65
 
66
66
  PYTORCH_DEPENDENCIES = [
67
- "torch>=2.11.0",
68
- "torchvision>=0.26.0",
67
+ "torch>=2.13.0",
68
+ "torchvision>=0.28.0",
69
69
  "torchaudio>=2.11.0",
70
70
  ]
71
71
 
@@ -92,7 +92,7 @@ extras_require = {
92
92
  "pytest>=8.0.0,<9.0.0",
93
93
  "pytest-asyncio>=1.1.0,<2.0.0",
94
94
  "pytest-cov>=6.0.0,<7.0.0",
95
- "transformers>=5.5.3,<6.0.0",
95
+ "transformers>=5.10.4,<5.18.0",
96
96
  "black>=25.0.0,<26.0.0",
97
97
  "ruff>=0.12.0,<1.0.0",
98
98
  "mypy>=1.17.0,<2.0.0",
@@ -1,6 +1,6 @@
1
1
  """CaptionFlow - Distributed community captioning system."""
2
2
 
3
- __version__ = "0.5.6"
3
+ __version__ = "0.5.7"
4
4
 
5
5
  from .monitor import Monitor
6
6
  from .orchestrator import Orchestrator
@@ -6,7 +6,7 @@ import os
6
6
  from dataclasses import dataclass, field
7
7
  from datetime import datetime
8
8
  from enum import Enum
9
- from typing import Any, Dict, List, Optional, Tuple
9
+ from typing import Any, Dict, List, Optional, Tuple, Union
10
10
 
11
11
  from PIL import Image
12
12
 
@@ -128,7 +128,9 @@ class Caption:
128
128
  caption_count: int = 1 # Number of captions generated for this item
129
129
  caption: Optional[str] = None
130
130
  captions: Optional[List[str]] = None
131
- outputs: Dict[str, List[str]] = field(default_factory=dict)
131
+ outputs: Dict[str, Union[str, Dict[str, Any], List[Union[str, Dict[str, Any]]]]] = field(
132
+ default_factory=dict
133
+ )
132
134
  quality_score: Optional[float] = None
133
135
  quality_scores: Optional[List[float]] = None
134
136
 
@@ -179,12 +181,13 @@ class ProcessingStage:
179
181
  dtype: Optional[str] = None
180
182
  gpu_memory_utilization: Optional[float] = None
181
183
 
182
- # Optional semantic fallback for empty/refused caption responses. API
183
- # workers can omit the image on the fallback request when an upstream
184
- # provider rejects the image before inference.
184
+ # Caption policy and recovery apply to every inference backend.
185
185
  retry_prompt: Optional[str] = None
186
186
  retry_without_image: bool = False
187
187
  retry_sampling: Optional[Dict[str, Any]] = None
188
+ output_processing: Optional[Dict[str, Any]] = None
189
+ response_format: Optional[Dict[str, Any]] = None
190
+ retry_response_format: Optional[Dict[str, Any]] = None
188
191
 
189
192
 
190
193
  @dataclass
@@ -51,7 +51,7 @@ class Orchestrator:
51
51
  assert (
52
52
  processor_type is not None
53
53
  ), "You must supply processor_type in your orchestrator dataset configuration."
54
- processor_config = ProcessorConfig(processor_type=processor_type, config=config)
54
+ self.processor_config = ProcessorConfig(processor_type=processor_type, config=config)
55
55
 
56
56
  # Initialize processor
57
57
  if processor_type == "webdataset":
@@ -70,7 +70,6 @@ class Orchestrator:
70
70
  caption_buffer_size=storage_config.get("caption_buffer_size", 1000),
71
71
  )
72
72
  self.auth = AuthManager(config.get("auth", {}))
73
- self.processor.initialize(processor_config, self.storage)
74
73
 
75
74
  # Processing configuration
76
75
  self.chunks_per_request = config.get("chunks_per_request", 2)
@@ -132,6 +131,8 @@ class Orchestrator:
132
131
 
133
132
  # Initialize storage
134
133
  await self.storage.initialize()
134
+ # Checkpoints can include results that had not reached a durable flush.
135
+ self.processor.initialize(self.processor_config, self.storage)
135
136
 
136
137
  # Start background tasks
137
138
  asyncio.create_task(self._heartbeat_loop())
@@ -410,7 +411,9 @@ class Orchestrator:
410
411
 
411
412
  elif msg_type == "work_complete":
412
413
  unit_id = data["unit_id"]
413
- self.processor.mark_completed(unit_id, worker_id)
414
+ if self.processor.mark_completed(unit_id, worker_id) is False:
415
+ logger.warning(f"Work unit {unit_id} reported complete before all results arrived")
416
+ return
414
417
  logger.debug(f"Work unit {unit_id} completed by worker {worker_id}")
415
418
  await self._send_activity(f"Chunk completed: {unit_id}")
416
419
 
@@ -428,9 +431,8 @@ class Orchestrator:
428
431
  logger.debug(f"Heartbeat from {worker_id}: {data}")
429
432
 
430
433
  async def _handle_results_submission(self, worker_id: str, data: Dict):
431
- """Process results submission from worker - fires off async task and returns immediately."""
432
- # Fire and forget - process in background
433
- asyncio.create_task(self._process_result_async(worker_id, data))
434
+ """Accept results in connection order before handling a completion message."""
435
+ await self._process_result_async(worker_id, data)
434
436
 
435
437
  async def _process_result_async(self, worker_id: str, data: Dict):
436
438
  """Actually process the result in background."""
@@ -444,6 +446,9 @@ class Orchestrator:
444
446
  shard_name = job_id.shard_id
445
447
  chunk_name = job_id.chunk_id
446
448
 
449
+ if data.get("error") or not data.get("outputs"):
450
+ raise ValueError("A failed result cannot be recorded as completed work")
451
+
447
452
  result = WorkResult(
448
453
  unit_id=data["unit_id"],
449
454
  source_id=shard_name,
@@ -455,11 +460,6 @@ class Orchestrator:
455
460
  processing_time_ms=data.get("processing_time_ms", 0),
456
461
  )
457
462
 
458
- # Let processor handle any custom processing - this updates chunk tracker
459
- # IMPORTANT: Call this BEFORE saving to storage so chunk tracker is updated
460
- # regardless of whether the item is a duplicate
461
- processed = self.processor.handle_result(result)
462
-
463
463
  # Create caption record for storage
464
464
  total_outputs = sum(len(v) for v in result.outputs.values())
465
465
 
@@ -480,7 +480,7 @@ class Orchestrator:
480
480
  caption = Caption(
481
481
  job_id=job_id,
482
482
  dataset=result.dataset,
483
- shard=processed["source_id"],
483
+ shard=result.source_id,
484
484
  chunk_id=chunk_name,
485
485
  item_key=item_key,
486
486
  captions=result.outputs.get("captions", []),
@@ -501,6 +501,11 @@ class Orchestrator:
501
501
  # Save to storage (might skip if duplicate)
502
502
  saved = await self.storage.save_caption(caption)
503
503
 
504
+ # Progress may only follow storage acceptance. A duplicate is also
505
+ # accepted: its caption is already present. Startup reconstructs
506
+ # progress from durable storage if buffered results are lost.
507
+ self.processor.handle_result(result)
508
+
504
509
  # Update contributor stats only if actually saved
505
510
  if saved:
506
511
  contributor = await self.storage.get_contributor(worker_user)
@@ -509,8 +514,11 @@ class Orchestrator:
509
514
  await self.storage.save_contributor(contributor)
510
515
 
511
516
  except Exception as e:
517
+ if data.get("unit_id"):
518
+ self.processor.mark_failed(data["unit_id"], worker_id, "Result acceptance failed")
512
519
  logger.error(
513
- f"Error processing result from {worker_id} for unit {data.get('unit_id', 'unknown')}: {e}",
520
+ f"Error processing result from {worker_id} "
521
+ f"for unit {data.get('unit_id', 'unknown')}: {e}",
514
522
  exc_info=True,
515
523
  )
516
524