Collection management Python API
RetrieverServiceClient is the supported boundary for long-lived agentic
applications. The client talks to the NeMo Retriever service; applications do
not open LanceDB, choose table names, or reproduce ingestion stages.
End-to-end workflow
import time
from nemo_retriever import RetrieverServiceClient
client = RetrieverServiceClient(
base_url="http://nemo-retriever:7670", # Published service endpoint
api_token="...",
scope="workspace-123",
)
collection = client.create_collection("research-session")
job = client.submit_documents(
collection.name,
["report.pdf"],
idempotency_key="agent-request-42",
)
# Submission means the job and uploads were accepted. It does not mean that
# extraction, OCR, splitting, captioning, embedding, and indexing are done.
while True:
job = client.get_job(job.job_id)
if job.status in {"completed", "failed", "partial_success"}:
break
time.sleep(2)
hits = client.query(
"What are the major findings?", collection_name=collection.name, top_k=10,
)
documents = client.list_documents(collection.name)
client.delete_document(collection.name, documents.items[0].document_id)
client.delete_collection(collection.name)
For local Docker Compose deployments, use the published gateway address, such
as http://localhost:7670. For other deployments, use the published gateway
endpoint reachable by the calling application. Authentication, tracing,
retryable upload handling, collection routing, and result normalization remain
server/SDK responsibilities.
Sync and async methods
Every lifecycle method has a native async equivalent prefixed with a:
create_collection/acreate_collection, submit_documents/asubmit_documents,
get_job/aget_job, list_documents/alist_documents, and
query/aquery. Use async methods inside an event loop.
Collection methods include create, get, list, update, and delete. Document
methods include get, list, delete, and atomic replace. Job methods expose the
aggregate and paginated per-file status. List operations use bounded limit
values and opaque continuation tokens; callers must not interpret tokens.
Append, idempotency, and replacement
Normal submission appends documents without changing existing documents. An
idempotency key replay with the same request returns the original job. The SDK
then safely replays every manifest entry, including after the client loses a
response before, during, or after upload. Each file has a deterministic
manifest_entry_id derived from its position, filename, and SHA-256. The
service returns the original acceptance for entries it already accepted,
without consuming capacity or starting duplicate processing. Reusing the key
or an entry ID with different content returns
RetrieverServiceConflictError (HTTP 409).
Before the first physical append, the VectorDB records a pending-version recovery marker and writes deterministic chunk IDs with an idempotent merge. After an interrupted write, reconciliation either finalizes committed chunks or removes an empty marker, so retrying the same document version does not duplicate chunks. Pending initial appends remain hidden from document reads and collection queries until reconciliation commits them.
Job document status separates attempt_id (one processing attempt) from
document_id (the stable collection identity). Append creates a new stable
document ID. Replacement creates a new attempt but retains the target document
ID. Collection document APIs show only indexed materializations; pending,
processing, and failed attempts remain visible through job APIs.
replace_document() submits one replacement file. NeMo Retriever records a
pending-version recovery marker, uses a single LanceDB merge transaction to
insert the new chunks and remove obsolete chunks for that document, and then
finalizes the catalog. The VectorDB reconciler inspects stored chunk versions
after a crash and either finalizes the new version or preserves the old one.
Failed processing never removes the prior version, and queries never expose
mixed versions.
Errors, scopes, expiration, and compatibility
The SDK raises RetrieverServiceNotFoundError,
RetrieverServiceConflictError, RetrieverServiceValidationError, or the base
RetrieverServiceError. Resources are isolated by scope; cross-scope reads
return 404. expires_at can be set at collection creation or update time for an
operator cleanup process. Deletion is retryable and if_exists=True makes
repeated deletion safe. Delete results report existed, deleted, status,
and cleanup_pending; synchronous completion returns HTTP 200 and a retryable
pending cleanup may return HTTP 202.
Production deployments map bearer tokens to allowed workspace scopes. Missing
or invalid credentials and valid tokens requesting an unauthorized scope
receive the same 401 response, preventing callers from distinguishing token
validity. Once authorized for a scope, looking up a resource owned by another
scope returns 404 so its existence is not disclosed. Configure either a single
token bound to default_scope, or mount a Secret-backed JSON file:
{"tokens":[{"token":"<secret>","scopes":["workspace-123"]}]}
Set allow_unscoped_dev only for an explicitly auth-disabled development
deployment. The gateway records the authorized scope on the request. Pod-only
callback routes and VectorDB calls require the separate internal credential;
an external bearer token is never used to authorize those internal routes or
forwarded to VectorDB.
expires_at must be timezone-aware RFC3339 and is normalized to UTC. For an
expiring collection, successful append and replace indexing activity refreshes
the expiration while preserving the configured window between updated_at and
expires_at. Collection metadata updates do the same when they omit
expires_at; supplying expires_at establishes a new window and setting it to
null disables expiration. Writes that do not commit vector data, including
empty writes, do not refresh collection activity. During recovery from an
interrupted write, NRL records activity refresh as durable recovery work. If
the collection update fails, reconciliation retains the marker and retries the
refresh. After the refresh succeeds, it clears the marker without refreshing
the collection again, so retries do not extend the expiration more than once.
Expired collections enter the
same retryable deletion state machine as explicit deletion. The local VectorDB
reconciler runs every 60 seconds by default, applies exponential retry capped at
one hour, and resumes replacement, document deletion, collection deletion, and
expiration cleanup after a crash.
Run one VectorDB replica while this reconciler is enabled; durable distributed
coordination remains separate infrastructure work. An interval of zero is
reserved for deployments where an external reconciler owns cleanup.
StoreOperator artifact persistence remains an independent pipeline and storage
concern. Collection deletion removes collection and document catalog entries,
chunk/vector rows, and the backend-owned physical collection table; it does not
delete extracted artifacts from S3, NFS, or the local filesystem. Configure
artifact retention and garbage collection at the storage/operator boundary,
where the corresponding credentials and ownership policy already live.
Legacy fixed-table ingestion and query remain available when
collection_name is omitted, but only against the operator-configured table.
No service request may specify a raw table name, storage URI, or physical
LanceDB location. /document is the canonical ingestion route and /whole is
supported; collection-aware /page returns 422 before work is registered.
Continuation tokens are versioned keyset cursors rather than offsets.
Collection cursors advance by collection name; document cursors advance by
(created_at, document_id). Tokens are bound to their resource type, scope,
and collection and return 422 when reused in another context. This keeps pages
stable while resources are inserted or deleted.
VectorDB health and metrics expose only aggregate catalog schema health, active/deleting/expired counts, pending cleanup count and oldest age, reconciliation successes/failures, and open-table cache size. Physical table names and tenant identifiers are never emitted as public values or labels.
Docker Compose operations
The default development stack lives at
nemo_retriever/dev/compose/service-mode.compose.yaml and runs the Retriever
and VectorDB as separate services. Set NRL_API_TOKEN to opt into a public
bearer credential and NRL_INTERNAL_VDB_TOKEN to protect the private service
hop; leaving them unset preserves the existing unauthenticated development
behavior. Runtime tokens must not be committed. Production deployments can
continue to use the service's Secret-backed multi-scope token-file support.
The same SDK workflow targets http://localhost:7670.
Application integration and query-result contract
External applications should construct a RetrieverServiceClient from the
service URL, token, and workspace scope, then call the SDK directly. Applications
should orchestrate calls and translate their own configuration only; NeMo
Retriever owns processing status, stable chunk/document identity, retrieval
ordering, citation provenance, retries, idempotency, and lifecycle truth. Clients
must not open LanceDB directly or reproduce the ingestion pipeline.
Collection query hits provide stable chunk_id and document_id, non-null
text, a finite native distance, filename, a one-based page number when
known, content type, source/source ID, stored image URI, bounding box, and
metadata. Collection queries use dense vector retrieval in this release;
lower distances are more similar and list order is authoritative. NRL does
not reinterpret distance as a normalized similarity or confidence. Consumers
that require a bounded score must translate the complete result set at their
own adapter boundary. page_number is null for non-paginated content or
invalid/unknown page provenance. Audio segments, video frames, and timestamps
keep their existing modality-specific metadata rather than being converted
into document pages. This contract is identical regardless of the network
path used to reach the service.
For format=evidence, each evidence item's score is the same native dense
vector distance, not a normalized confidence or probability. Lower is better,
and values are not comparable across queries.