Skip to content

Update user-side processing#13

Open
anashen wants to merge 8 commits intomasterfrom
update-api-interface
Open

Update user-side processing#13
anashen wants to merge 8 commits intomasterfrom
update-api-interface

Conversation

@anashen
Copy link
Member

@anashen anashen commented Feb 6, 2026

This PR proposes changes to enable the following:

  • Whether or not the annotation is successful is tracked, for improved error handling and to prevent output download in case of processing failure
  • The default server to send requests to is azimuthapi.satijalab.org
  • Azimuth metadata is only added to cells in the assay passed to CloudAzimuth (important in handling certain types of multi-assay objects)
  • The check for normalized data is more robust

@anashen anashen marked this pull request as ready for review February 12, 2026 22:10
@anashen anashen requested a review from Copilot February 12, 2026 22:10
Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR updates the client-side Azimuth cloud annotation flow to better handle processing failures, switch the default API host, scope metadata updates to only the processed assay/cells, and make the normalization check more robust.

Changes:

  • Update CloudAzimuth() default server host and strengthen the normalized-data check.
  • Track server-side processing success via SSE updates and stop before downloading outputs on failure.
  • Restrict Azimuth metadata propagation to the processed cells/assay to better support multi-assay objects.

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 6 comments.

File Description
R/cloud.R Changes default host, normalization check, metadata propagation logic, and gates output download on server success.
R/api_interface.R Updates SSE listener to return a success flag derived from streamed messages.
DESCRIPTION Bumps package version to 0.2.0.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +20 to +21
data_check <- if (!IsMatrixEmpty(data)) data[,1:min(5,ncol(data))] else NULL
if (is.null(data_check) || (max(data_check) > 15 || isTRUE(all.equal(data_check, floor(data_check))))) {
Copy link

Copilot AI Feb 12, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

IsMatrixEmpty() is not defined anywhere in this package, and it is not imported/qualified. This will error at runtime when CloudAzimuth() runs. Use a locally defined emptiness check (eg based on ncol, nrow, length, or Matrix::nnzero) or explicitly call a namespaced function that is guaranteed to exist (eg SeuratObject::...) and add the appropriate import.

Copilot uses AI. Check for mistakes.
Comment on lines +59 to +61
# Initialize column with NA only if it doesn't exist
if (!i %in% colnames(object@meta.data)) {
object@meta.data[, i] <- NA
Copy link

Copilot AI Feb 12, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Initializing a new metadata column with a scalar NA creates a logical column; the subsequent assignment from srt@meta.data will be coerced into logicals (eg numerics become TRUE/FALSE), corrupting metadata types. Initialize the column using the same type as srt@meta.data[[i]] (eg rep(NA_<type>, nrow(object@meta.data))) or build the full vector via match and assign it once.

Suggested change
# Initialize column with NA only if it doesn't exist
if (!i %in% colnames(object@meta.data)) {
object@meta.data[, i] <- NA
# Initialize column with type-appropriate NA only if it doesn't exist
if (!i %in% colnames(object@meta.data)) {
template <- srt@meta.data[[i]]
n_cells <- nrow(object@meta.data)
if (is.factor(template)) {
# Preserve factor levels
na_factor <- factor(NA, levels = levels(template))
object@meta.data[, i] <- rep(na_factor, n_cells)
} else if (is.numeric(template) && !is.integer(template)) {
object@meta.data[, i] <- rep(NA_real_, n_cells)
} else if (is.integer(template)) {
object@meta.data[, i] <- rep(NA_integer_, n_cells)
} else if (is.character(template)) {
object@meta.data[, i] <- rep(NA_character_, n_cells)
} else {
# Logical or other types fall back to logical NA
object@meta.data[, i] <- rep(NA, n_cells)
}

Copilot uses AI. Check for mistakes.
Comment on lines +63 to +64
# Update only the processed cells
object@meta.data[assay_cells, i] <- srt@meta.data[assay_cells, i]
Copy link

Copilot AI Feb 12, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Indexing object@meta.data[assay_cells, i] will throw if any assay_cells are not present in object@meta.data rownames (which is plausible given the comment about differing cell ID formats). Update using an intersection/match of cell names so missing cells are ignored rather than erroring.

Suggested change
# Update only the processed cells
object@meta.data[assay_cells, i] <- srt@meta.data[assay_cells, i]
# Update only the processed cells that also exist in the original object metadata
common_cells <- intersect(assay_cells, rownames(object@meta.data))
if (length(common_cells) > 0) {
object@meta.data[common_cells, i] <- srt@meta.data[common_cells, i]
}

Copilot uses AI. Check for mistakes.
Comment on lines +80 to +84
success <- listen_to_progress(progress_url, file_path, ...)

if (isFALSE(success)) {
stop("Processing failed on the server. Please check the error messages above.")
}
Copy link

Copilot AI Feb 12, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

process_rds_file() now treats any listen_to_progress() result other than explicit TRUE as failure. Since listen_to_progress() initializes success to FALSE, if the server never emits a success field the client will always stop even when processing succeeded. Consider initializing success to NA and only stopping on an explicit FALSE (or alternatively treat stream completion as success unless an error status is received).

Copilot uses AI. Check for mistakes.
Comment on lines +24 to +25
# Track whether processing succeeded
success <- NULL
Copy link

Copilot AI Feb 12, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

success is initialized to FALSE, so if the SSE stream never includes a success field, this function will return FALSE even on success. Initialize to NA and only update when a success field is received, or set success <- TRUE on normal stream completion and flip to FALSE only when an error/success flag indicates failure.

Suggested change
# Track whether processing succeeded
success <- NULL
# Track whether processing succeeded; default to TRUE and override if API sends a flag
success <- TRUE

Copilot uses AI. Check for mistakes.
Comment on lines 73 to 77
#' @param api_base_url Base URL for the API
#' @param input_file Path to input RDS file
#' @return NULL
#' @importFrom httr POST GET upload_file content status_code
process_rds_file <- function(api_base_url, file_path, ...) {
Copy link

Copilot AI Feb 12, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The roxygen docs for process_rds_file() list @param input_file, but the function argument is file_path. Update the parameter name in the documentation to match the signature (and consider updating @return to mention it errors on failure now).

Copilot uses AI. Check for mistakes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant

Comments