Nothing
#' Generate Roxygen Documentation for a Dynamic Spec Function
#'
#' @description
#' This internal helper constructs a complete Roxygen comment block as a single
#' string. This string is then attached to the dynamically created model
#' specification function, making it self-documenting.
#'
#' @details
#' The function assembles the documentation in a structured way:
#' \itemize{
#' \item \strong{Title & Description:} A title is generated from the `model_name`,
#' and the description indicates which `kerasnip` function created it.
#' \item \strong{Parameters (`@param`):} It documents several groups of parameters:
#' \itemize{
#' \item Block-specific hyperparameters (e.g., `dense_units`), introspecting
#' `layer_blocks` to find default values.
#' \item Architecture parameters (e.g., `num_dense`).
#' \item Global training parameters (e.g., `epochs`, `learn_rate`).
#' \item Compilation override parameters (e.g., `compile_loss`).
#' }
#' \item \strong{Sections (`@section`):} It creates dedicated sections for:
#' \itemize{
#' \item \strong{Model Architecture:} Explains how the model is built, with
#' different content for the Sequential vs. Functional API (controlled
#' by the `functional` flag).
#' \item \strong{Model Fitting:} Explains how to pass arguments to
#' `keras3::fit()` using the `fit_` prefix.
#' \item \strong{Model Compilation:} Explains the default compilation
#' behavior and how to override it using the `compile_` prefix.
#' }
#' \item \strong{Other Tags:} Adds `@seealso` to link to relevant `kerasnip`
#' functions and `@export` to make the generated function available to users.
#' }
#'
#' @param model_name A character string for the model's name, used to generate the documentation title.
#' @param layer_blocks The named list of user-provided layer block functions. This is
#' introspected to find default values for block-specific parameters.
#' @param all_args A named list of all arguments for the new function's signature,
#' used to determine which `@param` tags to generate.
#' @param functional A logical. If `TRUE`, generates documentation specific to
#' the Functional API. If `FALSE`, generates documentation for the Sequential API.
#' @return A single string containing the full Roxygen documentation, ready to be
#' attached to a function using `comment()`.
#' @noRd
generate_roxygen_docs <- function(
model_name,
layer_blocks,
all_args,
functional = FALSE
) {
# Title and Description
title <- paste(
gsub("_", " ", tools::toTitleCase(model_name)),
"Model Specification"
)
desc <- paste0(
"Defines a `parsnip` model specification for a Keras model built with ",
"custom layer blocks. This function was generated by `kerasnip::",
if (isTRUE(functional)) {
"create_keras_functional_spec"
} else {
"create_keras_sequential_spec"
},
"()`."
)
# Parameters
param_docs <- c()
arg_names <- names(all_args)
# Group args for structured documentation
num_params <- arg_names[startsWith(arg_names, "num_")]
fit_params <- arg_names[startsWith(arg_names, "fit_")]
compile_params <- arg_names[startsWith(arg_names, "compile_")]
# `learn_rate` is a special top-level convenience argument
special_params <- "learn_rate"
block_params <- setdiff(
arg_names,
c(num_params, fit_params, compile_params, special_params)
)
if (length(block_params) > 0) {
# Sort block names by length descending to handle overlapping names
# (e.g., "dense" and "dense_layer")
sorted_block_names <- names(layer_blocks)[
order(nchar(names(layer_blocks)), decreasing = TRUE)
]
param_docs <- c(
param_docs,
purrr::map_chr(block_params, function(p) {
# Find the block name that is a prefix for this parameter.
# The `Find` function returns the first match, and since we sorted
# block names by length, it will find the longest possible match.
block_name <- Find(
function(bn) startsWith(p, paste0(bn, "_")),
sorted_block_names
)
if (is.null(block_name)) {
# This should not happen if collect_spec_args is correct, but as a
# fallback, we avoid an error.
return(paste0("@param ", p, " A model parameter."))
}
param_name <- sub(paste0(block_name, "_"), "", p, fixed = TRUE)
block_fn <- layer_blocks[[block_name]]
default_val <- rlang::fn_fmls(block_fn)[[param_name]]
default_str <- if (
!is.null(default_val) && !rlang::is_missing(default_val)
) {
paste0(
" Defaults to `",
deparse(default_val, width.cutoff = 500L),
"`."
)
} else {
""
}
paste0(
"@param ",
p,
" The `",
param_name,
"` for the '",
block_name,
"' block.",
default_str
)
})
)
}
# Document architecture params
if (length(num_params) > 0) {
param_docs <- c(
param_docs,
purrr::map_chr(num_params, function(p) {
block_name <- sub("num_", "", p)
paste0(
"@param ",
p,
" The number of times to repeat the '",
block_name,
"' block. Defaults to 1."
)
})
)
}
# Document special `learn_rate` param
param_docs <- c(
param_docs,
"@param learn_rate The learning rate for the default Adam optimizer. This is ignored if `compile_optimizer` is provided as a pre-built Keras optimizer object."
)
# Document compile params
if (length(compile_params) > 0) {
param_docs <- c(
param_docs,
purrr::map_chr(compile_params, function(p) {
paste0(
"@param ",
p,
" Argument to `keras3::compile()`. See the 'Model Compilation' section."
)
})
)
}
# Document fit params
if (length(fit_params) > 0) {
param_docs <- c(
param_docs,
purrr::map_chr(fit_params, function(p) {
paste0(
"@param ",
p,
" Argument to `keras3::fit()`. See the 'Model Fitting' section."
)
})
)
}
# Add ... param
param_docs <- c(
param_docs,
paste0(
"@param ... Additional arguments passed to the Keras engine. Use this for arguments to `keras3::fit()` or `keras3::compile()` ",
"that are not exposed as top-level arguments."
)
)
# Sections
if (isTRUE(functional)) {
architecture_section <- c(
"#' @section Model Architecture (Functional API):",
"#' The Keras model is constructed using the Functional API. Each layer block function's arguments",
"#' determine its inputs. For example, a block `function(input_a, input_b, ...)` will be connected",
"#' to the outputs of the `input_a` and `input_b` blocks. You can also repeat a block by setting",
"#' the `num_{block_name}` argument, provided the block has a single input tensor.",
"#' The first block in `layer_blocks` is assumed to be the input layer and should not have inputs from other layers."
)
see_also_fit <- "generic_functional_fit()"
see_also_create <- "create_keras_functional_spec()"
} else {
architecture_section <- c(
"#' @section Model Architecture (Sequential API):",
"#' The Keras model is constructed by sequentially applying the layer blocks in the order they were provided to `create_keras_sequential_spec()`.",
"#' You can control the number of times each block is repeated by setting the `num_{block_name}` argument (e.g., `num_dense = 2`).",
"#' This allows for dynamically creating deeper or more complex architectures during tuning."
)
see_also_fit <- "generic_sequential_fit()"
see_also_create <- "create_keras_sequential_spec()"
}
compilation_section <- c(
"#' @section Model Compilation:",
"#' The model is compiled with a default optimizer, loss function, and metric based on the model's mode. You can override these defaults by providing arguments prefixed with `compile_`.",
"#' \\itemize{",
"#' \\item \\strong{Optimizer}: Defaults to `keras3::optimizer_adam()` using the `learn_rate` argument. Override with `compile_optimizer` (e.g., `\"sgd\"` or `keras3::optimizer_sgd(...)`).",
"#' \\item \\strong{Loss}: Defaults to `\"mean_squared_error\"` for regression and `\"categorical_crossentropy\"` or `\"binary_crossentropy\"` for classification. Override with `compile_loss`.",
"#' \\item \\strong{Metrics}: Defaults to `\"mean_absolute_error\"` for regression and `\"accuracy\"` for classification. Override with `compile_metrics` (e.g., `c(\"mae\", \"mape\")`).",
"#' }",
paste0(
"#' For more details, see the documentation for `kerasnip::",
see_also_fit,
"`."
)
)
fitting_section <- c(
"#' @section Model Fitting:",
"#' The model is fit using `keras3::fit()`. You can pass any argument to this function by prefixing it with `fit_`.",
"#' For example, to add Keras callbacks, you can pass `fit_callbacks = list(callback_early_stopping())`.",
"#' Common arguments include `fit_epochs`, `fit_batch_size`, and `fit_validation_split`."
)
# Other tags
other_tags <- c(
paste0("#' @seealso [", see_also_create, "], [", see_also_fit, "]"),
"#' @export"
)
# Combine all parts
paste(
c(
paste0("#' ", title),
"#'",
paste0("#' ", desc),
"#'",
paste0("@", param_docs),
architecture_section,
fitting_section,
compilation_section,
other_tags
),
collapse = "\n"
)
}
Any scripts or data that you put into this service are public.
Add the following code to your website.
For more information on customizing the embed code, read Embedding Snippets.