-
Notifications
You must be signed in to change notification settings - Fork 436
feat: Add video upload and list_objects functionality to ObjectStore #1195
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: develop
Are you sure you want to change the base?
feat: Add video upload and list_objects functionality to ObjectStore #1195
Conversation
Backend: - ObjectStoreListItem model (metadata only, no data) - S3ObjectStore with pagination and path-style addressing - InMemoryObjectStore for testing - FastAPI video routes using videos/ prefix organization - MinIO test fixture and 20 tests - Updated documentation with examples Frontend (nat-ui submodule): - VideoLibrary, VideoUpload, VideoLibraryModal components - videoUpload API client proxying through backend - Settings integration and route constants Signed-off-by: Alex Lin <[email protected]>
WalkthroughThe changes introduce a new Changes
Sequence DiagramssequenceDiagram
participant Client
participant FastAPI
participant ObjectStore
participant S3
rect rgb(200, 220, 255)
Note over Client,S3: list_objects() Flow
Client->>FastAPI: GET /videos?prefix=videos/
FastAPI->>ObjectStore: list_objects(prefix="videos/")
ObjectStore->>S3: list_objects_v2(Prefix="videos/")
S3-->>ObjectStore: [object_keys]
loop Per object
ObjectStore->>S3: head_object(key)
S3-->>ObjectStore: {size, content_type, metadata, last_modified}
end
ObjectStore-->>FastAPI: [ObjectStoreListItem, ...]
FastAPI-->>Client: [{key, size, content_type, ...}, ...]
end
rect rgb(220, 255, 220)
Note over Client,S3: Video Upload Flow
Client->>FastAPI: POST /videos (file + metadata)
FastAPI->>FastAPI: Validate content_type
FastAPI->>FastAPI: Sanitize filename, generate UUID
FastAPI->>ObjectStore: put_object(key="videos/{uuid}/{filename}", data, metadata)
ObjectStore->>S3: put_object(key, data, metadata)
S3-->>ObjectStore: Success
ObjectStore-->>FastAPI: Success
FastAPI-->>Client: {video_key, filename, content_type, size, uuid}
end
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~20 minutes
Pre-merge checks and finishing touches❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✨ Finishing touches
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 0
🧹 Nitpick comments (3)
packages/nvidia_nat_s3/tests/conftest.py (1)
31-55: LGTM! The MinIO fixture is well-designed.The
minio_serverfixture provides good developer experience:
- Automatically skips tests when MinIO isn't running
- Comprehensive docstring with docker commands for setup
- Session scope is appropriate for this shared resource
- Clear skip message guides developers to start MinIO
Consider adding a return type hint to improve type safety:
@pytest.fixture(scope="session") -def minio_server(): +def minio_server() -> dict[str, str]:This is a minor enhancement and fully optional.
src/nat/front_ends/fastapi/fastapi_front_end_plugin_worker.py (1)
751-778: Simplify redundant exception handling.The delete operation correctly implements:
- Security validation (key must start with
videos/)- Idempotent behavior (deleting non-existent videos returns success)
- Proper error handling with appropriate status codes
However, there's redundant exception handling with two consecutive
except NoSuchKeyErrorblocks (lines 760-762 and 765-767) that perform the same action.Simplify by removing the redundant catch block:
try: try: await object_store_client.delete_object(video_key) except NoSuchKeyError: logger.info(f"Video {video_key} not found during delete - treating as success (idempotent)") - return {"message": "Video deleted successfully (was already deleted)", "video_key": video_key} + return {"message": "Video deleted successfully", "video_key": video_key} - return {"message": "Video deleted successfully", "video_key": video_key} - except NoSuchKeyError as e: - logger.info(f"Video {video_key} not found: {e}") - return {"message": "Video deleted successfully (was already deleted)", "video_key": video_key} except HTTPException: raise except Exception as e:This maintains the same idempotent behavior with clearer logic flow.
docs/source/store-and-retrieve/object-store.md (1)
253-253: Consider adding example output to console command blocks. Markdownlint (MD014) flags lines 253, 257, and 261 as showing commands without output. For consistency with documentation conventions and to match similar patterns, consider either: (1) adding example response output below each command, or (2) removing the$prompt to format as plain code blocks. Note: Similar curl examples at lines 210, 214, 218, 222 use the same format, so this may be a false positive or style inconsistency that could be addressed project-wide.Also applies to: 257-257, 261-261
📜 Review details
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (10)
docs/source/store-and-retrieve/object-store.md(4 hunks)external/nat-ui(1 hunks)packages/nvidia_nat_s3/src/nat/plugins/s3/s3_object_store.py(3 hunks)packages/nvidia_nat_s3/tests/conftest.py(1 hunks)packages/nvidia_nat_test/src/nat/test/object_store_tests.py(2 hunks)src/nat/front_ends/fastapi/fastapi_front_end_plugin_worker.py(4 hunks)src/nat/object_store/in_memory_object_store.py(2 hunks)src/nat/object_store/interfaces.py(2 hunks)src/nat/object_store/models.py(2 hunks)tests/nat/front_ends/fastapi/test_video_upload_routes.py(1 hunks)
🧰 Additional context used
📓 Path-based instructions (5)
**/*
⚙️ CodeRabbit configuration file
**/*: # Code Review Instructions
- Ensure the code follows best practices and coding standards. - For Python code, follow
PEP 20 and
PEP 8 for style guidelines.- Check for security vulnerabilities and potential issues. - Python methods should use type hints for all parameters and return values.
Example:def my_function(param1: int, param2: str) -> bool: pass- For Python exception handling, ensure proper stack trace preservation:
- When re-raising exceptions: use bare
raisestatements to maintain the original stack trace,
and uselogger.error()(notlogger.exception()) to avoid duplicate stack trace output.- When catching and logging exceptions without re-raising: always use
logger.exception()
to capture the full stack trace information.Documentation Review Instructions - Verify that documentation and comments are clear and comprehensive. - Verify that the documentation doesn't contain any TODOs, FIXMEs or placeholder text like "lorem ipsum". - Verify that the documentation doesn't contain any offensive or outdated terms. - Verify that documentation and comments are free of spelling mistakes, ensure the documentation doesn't contain any
words listed in the
ci/vale/styles/config/vocabularies/nat/reject.txtfile, words that might appear to be
spelling mistakes but are listed in theci/vale/styles/config/vocabularies/nat/accept.txtfile are OK.Misc. - All code (except .mdc files that contain Cursor rules) should be licensed under the Apache License 2.0,
and should contain an Apache License 2.0 header comment at the top of each file.
- Confirm that copyright years are up-to date whenever a file is changed.
Files:
external/nat-uisrc/nat/object_store/models.pydocs/source/store-and-retrieve/object-store.mdtests/nat/front_ends/fastapi/test_video_upload_routes.pypackages/nvidia_nat_s3/tests/conftest.pypackages/nvidia_nat_s3/src/nat/plugins/s3/s3_object_store.pypackages/nvidia_nat_test/src/nat/test/object_store_tests.pysrc/nat/object_store/interfaces.pysrc/nat/object_store/in_memory_object_store.pysrc/nat/front_ends/fastapi/fastapi_front_end_plugin_worker.py
src/nat/**/*
⚙️ CodeRabbit configuration file
This directory contains the core functionality of the toolkit. Changes should prioritize backward compatibility.
Files:
src/nat/object_store/models.pysrc/nat/object_store/interfaces.pysrc/nat/object_store/in_memory_object_store.pysrc/nat/front_ends/fastapi/fastapi_front_end_plugin_worker.py
docs/source/**/*
⚙️ CodeRabbit configuration file
This directory contains the source code for the documentation. All documentation should be written in Markdown format. Any image files should be placed in the
docs/source/_staticdirectory.
Files:
docs/source/store-and-retrieve/object-store.md
tests/**/*.py
⚙️ CodeRabbit configuration file
tests/**/*.py: - Ensure that tests are comprehensive, cover edge cases, and validate the functionality of the code. - Test functions should be named using thetest_prefix, using snake_case. - Any frequently repeated code should be extracted into pytest fixtures. - Pytest fixtures should define the name argument when applying the pytest.fixture decorator. The fixture
function being decorated should be named using thefixture_prefix, using snake_case. Example:
@pytest.fixture(name="my_fixture")
def fixture_my_fixture():
pass
Files:
tests/nat/front_ends/fastapi/test_video_upload_routes.py
packages/**/*
⚙️ CodeRabbit configuration file
packages/**/*: - This directory contains optional plugin packages for the toolkit, each should contain apyproject.tomlfile. - Thepyproject.tomlfile should declare a dependency onnvidia-nator another package with a name starting
withnvidia-nat-. This dependency should be declared using~=<version>, and the version should be a two
digit version (ex:~=1.0).
- Not all packages contain Python code, if they do they should also contain their own set of tests, in a
tests/directory at the same level as thepyproject.tomlfile.
Files:
packages/nvidia_nat_s3/tests/conftest.pypackages/nvidia_nat_s3/src/nat/plugins/s3/s3_object_store.pypackages/nvidia_nat_test/src/nat/test/object_store_tests.py
🧠 Learnings (2)
📚 Learning: 2025-11-14T20:33:53.944Z
Learnt from: AnuradhaKaruppiah
Repo: NVIDIA/NeMo-Agent-Toolkit PR: 1181
File: packages/nvidia_nat_test/tests/test_test_llm.py:419-484
Timestamp: 2025-11-14T20:33:53.944Z
Learning: The NVIDIA NeMo-Agent-Toolkit project uses pytest-asyncio in strict mode (the default), which requires pytest.mark.asyncio decorator on all async test functions. All async tests in packages/nvidia_nat_test/tests/test_test_llm.py consistently follow this pattern.
Applied to files:
packages/nvidia_nat_s3/tests/conftest.py
📚 Learning: 2025-08-23T02:55:39.346Z
Learnt from: willkill07
Repo: NVIDIA/NeMo-Agent-Toolkit PR: 649
File: src/nat/cli/commands/object_store/object_store.py:32-42
Timestamp: 2025-08-23T02:55:39.346Z
Learning: In the NAT object store plugin architecture, config classes like S3ObjectStoreClientConfig, MySQLObjectStoreClientConfig, and RedisObjectStoreClientConfig are defined in the object_store.py files (e.g., nat.plugins.s3.object_store), while the implementation classes are in separate files (e.g., nat.plugins.s3.s3_object_store). The registration functions are also in the object_store.py files.
Applied to files:
packages/nvidia_nat_s3/src/nat/plugins/s3/s3_object_store.pypackages/nvidia_nat_test/src/nat/test/object_store_tests.pysrc/nat/object_store/in_memory_object_store.py
🧬 Code graph analysis (6)
tests/nat/front_ends/fastapi/test_video_upload_routes.py (3)
src/nat/object_store/in_memory_object_store.py (2)
in_memory_object_store(99-100)InMemoryObjectStoreConfig(30-34)packages/nvidia_nat_test/src/nat/test/functions.py (1)
EchoFunctionConfig(28-29)packages/nvidia_nat_test/src/nat/test/utils.py (1)
build_nat_client(93-123)
packages/nvidia_nat_s3/src/nat/plugins/s3/s3_object_store.py (3)
src/nat/object_store/interfaces.py (2)
ObjectStore(23-99)list_objects(88-99)src/nat/object_store/models.py (1)
ObjectStoreListItem(43-65)src/nat/object_store/in_memory_object_store.py (1)
list_objects(75-95)
packages/nvidia_nat_test/src/nat/test/object_store_tests.py (5)
examples/object_store/user_report/tests/test_objext_store_example_user_report_tool.py (1)
object_store(44-46)src/nat/object_store/models.py (2)
ObjectStoreListItem(43-65)ObjectStoreItem(23-40)src/nat/object_store/interfaces.py (4)
ObjectStore(23-99)put_object(32-44)list_objects(88-99)delete_object(75-85)packages/nvidia_nat_s3/src/nat/plugins/s3/s3_object_store.py (3)
put_object(96-125)list_objects(174-221)delete_object(158-172)src/nat/object_store/in_memory_object_store.py (3)
put_object(47-51)list_objects(75-95)delete_object(67-72)
src/nat/object_store/interfaces.py (3)
src/nat/object_store/models.py (1)
ObjectStoreListItem(43-65)packages/nvidia_nat_s3/src/nat/plugins/s3/s3_object_store.py (1)
list_objects(174-221)src/nat/object_store/in_memory_object_store.py (1)
list_objects(75-95)
src/nat/object_store/in_memory_object_store.py (3)
src/nat/object_store/models.py (1)
ObjectStoreListItem(43-65)src/nat/utils/type_utils.py (1)
override(56-57)packages/nvidia_nat_s3/src/nat/plugins/s3/s3_object_store.py (1)
list_objects(174-221)
src/nat/front_ends/fastapi/fastapi_front_end_plugin_worker.py (5)
src/nat/builder/workflow_builder.py (2)
get_object_store_client(887-891)get_object_store_client(1418-1426)packages/nvidia_nat_test/src/nat/test/tool_test_runner.py (1)
get_object_store_client(265-275)packages/nvidia_nat_s3/src/nat/plugins/s3/s3_object_store.py (3)
put_object(96-125)list_objects(174-221)delete_object(158-172)src/nat/object_store/in_memory_object_store.py (3)
put_object(47-51)list_objects(75-95)delete_object(67-72)src/nat/object_store/models.py (1)
ObjectStoreItem(23-40)
🪛 markdownlint-cli2 (0.18.1)
docs/source/store-and-retrieve/object-store.md
253-253: Dollar signs used before commands without showing output
(MD014, commands-show-output)
257-257: Dollar signs used before commands without showing output
(MD014, commands-show-output)
261-261: Dollar signs used before commands without showing output
(MD014, commands-show-output)
🪛 Ruff (0.14.5)
packages/nvidia_nat_s3/src/nat/plugins/s3/s3_object_store.py
179-179: Avoid specifying long messages outside the exception class
(TRY003)
219-219: Avoid specifying long messages outside the exception class
(TRY003)
219-219: Use explicit conversion flag
Replace with conversion flag
(RUF010)
src/nat/front_ends/fastapi/fastapi_front_end_plugin_worker.py
677-677: Do not perform function call File in argument defaults; instead, perform the call within the function, or read the default from a module-level singleton variable
(B008)
702-702: Use explicit conversion flag
Replace with conversion flag
(RUF010)
741-741: Consider moving this statement to an else block
(TRY300)
744-744: Use explicit conversion flag
Replace with conversion flag
(RUF010)
764-764: Consider moving this statement to an else block
(TRY300)
772-772: Use explicit conversion flag
Replace with conversion flag
(RUF010)
🔇 Additional comments (26)
external/nat-ui (1)
1-1: Submodule configuration is valid, but code verification requires access to the external repository.The
external/nat-uisubmodule is properly configured in.gitmodulesand points tohttps://github.com/NVIDIA/NeMo-Agent-Toolkit-UI.git. The commit SHA04efa5b129b962decf56238d3bde3f66e907d2abis a valid 40-character hex reference. However, to verify the actual frontend code changes (VideoLibrary, VideoUpload, VideoLibraryModal components, and videoUpload.ts API client), you will need to inspect the commit in the external NVIDIA/NeMo-Agent-Toolkit-UI repository directly.Confirm that the external repository commit contains the intended frontend implementation and that the changes integrate correctly with your video processing backend routes.
src/nat/object_store/models.py (2)
16-16: LGTM!The datetime import is correctly placed and necessary for the new
last_modifiedfield inObjectStoreListItem.
43-65: LGTM!The
ObjectStoreListItemmodel is well-structured with:
- Comprehensive docstring following numpy-style format
- Proper type hints for all fields
- Consistent with the existing
ObjectStoreItempattern- Appropriate use of optional fields for metadata that may not always be available
tests/nat/front_ends/fastapi/test_video_upload_routes.py (3)
29-44: LGTM!The test fixtures are well-structured:
object_store_nameprovides a configurable store identifierclientfixture properly configures an async test client with InMemoryObjectStore for isolated testing- Uses
build_nat_clientutility correctly- EchoFunctionConfig serves as an appropriate dummy workflow for these integration tests
47-121: LGTM!The upload and list test methods provide excellent coverage:
- Verifies all response fields (video_key, filename, content_type, size, uuid)
- Tests rejection of non-video files with proper status codes
- Validates listing functionality in both empty and populated states
- Uses set comprehensions effectively to verify presence of uploaded videos
122-181: LGTM!The delete and workflow tests are comprehensive:
- Validates successful deletion and removal from listings
- Tests idempotent behavior (deleting already-deleted videos returns 200)
- Enforces security constraint that video keys must start with "videos/"
- Full workflow test validates end-to-end integration across all operations
src/nat/object_store/in_memory_object_store.py (2)
27-27: LGTM!The import of
ObjectStoreListItemis correctly placed alongside the existingObjectStoreItemimport.
74-95: LGTM!The
list_objectsimplementation is correct:
- Proper concurrency control with
async with self._lock- Correct prefix filtering using
startswith- Appropriately skips directory markers (keys ending with
/)- Size calculation using
len(item.data)is correct for in-memory bytes- Setting
last_modified=Noneis appropriate since the in-memory store doesn't persist modification timestampssrc/nat/object_store/interfaces.py (2)
20-20: LGTM!The import of
ObjectStoreListItemis correctly placed with other model imports.
87-99: LGTM!The abstract
list_objectsmethod is well-defined:
- Properly decorated with
@abstractmethod- Clear docstring explaining the purpose, parameters, and return value
- Type hints present for all parameters and return type
- Signature is consistent with implementations in both
InMemoryObjectStoreandS3ObjectStorepackages/nvidia_nat_s3/tests/conftest.py (1)
22-28: LGTM!The
is_port_openhelper function is well-implemented:
- Proper type hints for all parameters and return value
- 1-second timeout is reasonable for local connections
- Catches appropriate exceptions for port connectivity checks
packages/nvidia_nat_test/src/nat/test/object_store_tests.py (2)
27-27: LGTM!The import of
ObjectStoreListItemis correctly placed for use in the newtest_list_objectsmethod.
120-178: LGTM!The
test_list_objectsmethod provides comprehensive coverage:
- Uses UUID for test isolation to prevent conflicts between parallel test runs
- Tests multiple prefix scenarios (exact match, partial match, non-existent)
- Verifies all
ObjectStoreListItemfields (key, size, content_type, metadata)- Includes proper cleanup to avoid test pollution
- Instance type checking (
isinstance(obj, ObjectStoreListItem)) ensures correct return typespackages/nvidia_nat_s3/src/nat/plugins/s3/s3_object_store.py (3)
20-20: LGTM!The new imports are correctly placed:
Configfrom botocore.client enables path-style addressing configurationObjectStoreListItemis needed for the return type oflist_objectsAlso applies to: 27-27
60-63: LGTM!The path-style addressing configuration is well-implemented:
- Correctly detects non-AWS endpoints (MinIO, local S3-compatible services)
- Case-insensitive check using
.lower()is appropriate- Clear comment explaining the rationale (avoiding DNS-based virtual host lookups)
- Essential for local S3-compatible storage systems like MinIO
174-221: LGTM! The S3 list_objects implementation is well-structured.The implementation correctly handles:
- Pagination using
list_objects_v2paginator for efficient handling of large buckets- Optional prefix filtering
- Skipping directory markers (keys ending with
/)- Fetching per-object metadata via
head_objectwith graceful fallback on failure- Proper error logging with
exc_info=Trueand exception chaining withfrom eThe error handling follows the coding guidelines correctly—using
logger.error()(notlogger.exception()) when raising a new exception type.src/nat/front_ends/fastapi/fastapi_front_end_plugin_worker.py (4)
21-21: LGTM!The new imports are appropriate:
uuidfor generating unique video identifiersFileandFormfrom FastAPI for handling multipart form data in video uploadsAlso applies to: 33-34
310-310: LGTM!The call to
add_video_upload_routeis properly placed alongside other route registrations in theadd_routesmethod.
677-710: LGTM! The upload_video function implements proper security measures.The implementation correctly handles:
- Content type validation (must start with
video/)- Path traversal prevention using
os.path.basename()- UUID-based naming to prevent collisions
- Proper error handling with appropriate HTTP status codes (409 for conflicts, 500 for errors)
- Comprehensive metadata storage (original filename and user metadata)
Note: The static analysis warning about
File(...)in argument defaults is a false positive—this is the idiomatic FastAPI pattern for dependency injection and is the correct way to declare file upload parameters.
717-749: LGTM! The list_videos function is well-implemented.The implementation correctly:
- Lists objects with the
videos/prefix- Parses the UUID-based key structure (
videos/{uuid}/{filename})- Handles filenames containing slashes using
'/'.join(parts[2:])- Provides sensible defaults (content_type defaults to
video/mp4)- Sorts results by upload time (most recent first)
- Handles missing
last_modifiedtimestamps gracefully (in-memory store case)docs/source/store-and-retrieve/object-store.md (6)
1-16: License header and copyright year are current.
41-52: ObjectStoreListItem documentation is clear and well-structured. The distinction fromObjectStoreItemand emphasis on memory efficiency is helpful.
54-84: ObjectStore interface documentation is accurate and complete. The type hints for the newlist_objects()method are correct, and the method is properly documented in the operations list.
173-185: Usage examples forlist_objects()are well-demonstrated. The examples clearly show both basic listing and prefix filtering patterns, with helpful explanation of efficiency benefits.
225-265: Video Upload Integration section is well-documented. Configuration examples are clear, HTTP endpoints are properly described, and the reference tolist_objects(prefix="videos/")for accessing uploaded videos integrates nicely with the earlier usage examples.
270-289: Running Tests and Error Handling sections provide valuable guidance. Test commands cover all components (in-memory store, FastAPI routes, S3 integration), and the updated error handling documentation correctly mentionsNoSuchKeyErrorfor retrieval/deletion operations.
Description
This PR adds video upload functionality to NAT, enabling multimodal workflows that require video processing. It integrates with NAT's existing object store infrastructure and includes a companion UI in the nat-ui submodule.
Key Changes
Backend:
list_objects()method toObjectStorebase class with implementation inS3ObjectStore(supports AWS S3 and MinIO). Returns metadata-only listings with optional prefix filtering and paginationPOST /videos,GET /videos,DELETE /videos/{video_key}) for video upload, listing, and deletion with UUID-based naming (videos/{uuid}/{filename})test_video_upload_routes.pycovering upload/list/delete operations with MinIO fixtureobject-store.mdwith video upload examples and configuration patternsFrontend:
NAT.UI.Demo.mp4
Related PRs
This PR depends on NVIDIA/NeMo-Agent-Toolkit-UI#62
Closes
By Submitting this PR I confirm:
Summary by CodeRabbit
Release Notes
New Features
Documentation
Tests
✏️ Tip: You can customize this high-level summary in your review settings.