[DE-8304] Deprecate synchronous upload capability - #468
Conversation
|
@greptileai regarding your "P1 Only last batch job returned for multi-batch local uploads" concern: I believed I've already fixed at line 2178-2182 in All batch jobs except the last are waited on before returning. The caller's returned job is the final one, and by the time they receive it, all earlier batches have completed. |
|
You're right, and I apologize for the stale comment. Looking at the diff, jobs = [AsyncJob.from_json(r, self._client) for r in responses]
for job in jobs[:-1]:
job.sleep_until_complete()
return jobs[-1]All earlier batch jobs are waited on inside Tip: You can customize Greptile's behavior for this repo with |
Master's #468 (DE-8304) made uploads async-only and deleted nucleus/upload_response.py; this branch predated it, so every test fixture that appended items failed at the old sync-upload assertion. Resolve the CHANGELOG collision (both sides claimed 0.19.0) by releasing this feature set as 0.19.1 and bump pyproject.toml to match. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Summary
Companion PR (to be merged first): https://github.com/scaleapi/scaleapi/pull/150037
All Nucleus uploads now go through the async pipeline (Step Functions). The synchronous upload path has been fully removed. This ensures every upload gets phash computation, image optimization, and Qdrant/NLS embedding — the sync path silently skipped NLS indexing (which was done on purpose as this was a planned change), making items invisible to natural language search in federal environments. This only affects image datasets anyway
The main reason for this change is two fold:
Old nucleus uploads:
New nucleus uploads:
Breaking changes
dataset.append()always returnsAsyncJob— previously returnedUploadResponsefor sync uploads. Usejob.sleep_until_complete()to block until processing finishes.asynchronousandbatch_sizeparameters are deprecated — they are accepted with aDeprecationWarningbut ignored. All uploads are async.asynchronous=Truenow work — previously raisedValueError. Files are sent as multipart to the backend which handles S3 upload internally.append()call are supported — local items go as multipart, remote items go as NDJSON.Removed
UploadResponseclass andnucleus/upload_response.pyconstruct_append_payload()andconstruct_append_scenes_payload()functionscheck_all_paths_remote()functiondataset.append_scenes()public methodNucleusClient.populate_dataset()methodDatasetItemUploader.upload()sync method and_process_append_requests()sync remote method_append_scenes()and_append_video_scenes()tests/test_upload_response.pyUpdated
AsyncJoband calljob.sleep_until_complete()before reading dataasync_job.pydocstring updated to removeasynchronous=Truefrom examplepayload_constructor.py0.19.0Test plan
poetry run pytest tests/test_dataset.py -k "test_dataset_append" -v— verify all append tests pass with AsyncJobpoetry run pytest tests/test_dataset.py -k "test_dataset_append_local" -v— verify local file async upload completespoetry run pytest tests/test_dataset.py -k "test_dataset_append_async_local" -v— verify async local upload returns AsyncJobpython -c "import nucleus"— verify no import errors after removing UploadResponsedataset.append(items, asynchronous=True)emits DeprecationWarningdataset.append(items, batch_size=20)emits DeprecationWarningdataset.append([])raises ValueErrorresolves https://linear.app/scale-epd/issue/DE-8304
Greptile Summary
This PR removes the synchronous upload path from the Nucleus Python client, making
dataset.append()always return anAsyncJob. It removesUploadResponse,construct_append_payload,construct_append_scenes_payload, and theasynchronous/batch_sizeparameters (now deprecated withDeprecationWarningbut ignored). All tests are updated to calljob.sleep_until_complete()before asserting on data./append?async=1; remote items still go through the presigned-URL + NDJSON path to/append?async=1.append()call now raiseValueError, contradicting the PR description which claims they are "supported."add_items_from_dirnow returnsOptional[AsyncJob];NucleusClient.create_dataset_from_dirproperly awaits the job.Confidence Score: 4/5
The refactor is clean and well-tested, but the multi-batch local upload path only returns the last batch's AsyncJob while blocking on all earlier ones inside append() itself — an existing tracked concern worth resolving before widespread use.
The core sync→async migration is straightforward and the tests are correctly updated. The main unresolved concern is in _upload_local_items_async: when local items split into more than one batch, intermediate job failures surface as exceptions from append() rather than from the returned job's sleep_until_complete(), and earlier batch jobs have no handle returned to the caller. The PR description also incorrectly documents mixed local+remote uploads as supported when the code actually raises ValueError.
nucleus/dataset.py — specifically the _upload_local_items_async method and its interaction with multi-batch local uploads
Important Files Changed
Flowchart
%%{init: {'theme': 'neutral'}}%% flowchart TD A["dataset.append(items)"] --> B{Item type?} B -->|LidarScene| C["_append_scenes()"] B -->|VideoScene| D["_append_video_scenes()"] B -->|DatasetItem| E{Local or remote?} C --> F["check_all_scene_paths_remote()\nserialize → presigned URL\nPOST /upload_scenes?async=1"] D --> G["check_all_scene_paths_remote()\nserialize → presigned URL\nPOST /upload_video_scenes?async=1"] E -->|mixed → error| H["ValueError: Cannot mix\nlocal and remote"] E -->|local only| I["_upload_local_items_async()"] E -->|remote only| J["serialize_and_write_to_presigned_url()\nPOST /append?async=1"] I --> K["DatasetItemUploader.upload_local_async()\nbatch into groups of local_files_per_upload_request"] K --> L["POST multipart /append?async=1\n(one request per batch, concurrent)"] L --> M["Wait for jobs[0..n-2].sleep_until_complete()\nReturn jobs[-1] to caller"] F --> N["AsyncJob"] G --> N J --> N M --> NReviews (6): Last reviewed commit: "Update CLAUDE.md" | Re-trigger Greptile