Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
115 changes: 56 additions & 59 deletions build-logic/convention/src/main/kotlin/GenerateDocumentationTask.kt
Original file line number Diff line number Diff line change
Expand Up @@ -165,9 +165,7 @@ abstract class GenerateDocumentationTask : DefaultTask() {

/**
* Short emoji-only rendering of a backend status, for use in the
* compact matrix cells. The long-form wording stays on the
* per-function backend-status table produced by
* [generateBackendStatusTable].
* compact matrix cells.
*
* The vocabulary covers both the planning-style strings
* (`supported` / `partial` / `not_supported` / `planned`) and
Expand Down Expand Up @@ -261,6 +259,7 @@ abstract class GenerateDocumentationTask : DefaultTask() {

private fun generateOperatorPage(operator: OperatorDoc, module: OperatorDocModule, outputDir: File) {
val operatorFile = File(outputDir, "${operator.name.lowercase()}.adoc")
val partialsRoot = derivePartialsRoot(outputDir)
operatorFile.writeText(buildString {
appendLine("= ${operator.name}")
appendLine("")
Expand All @@ -270,11 +269,32 @@ abstract class GenerateDocumentationTask : DefaultTask() {
appendLine("")

operator.functions.forEach { function ->
generateFunctionSection(operator, function, this)
generateFunctionSection(operator, function, this, partialsRoot)
}
})
}

/**
* If [outputDir] lives under an Antora `modules/<name>/pages/...` tree,
* return the sibling `partials/` directory where hand-written prose
* snippets live. Returns `null` for flat doc layouts, in which case the
* generator assumes no partials exist and skips include directives.
*
* Antora resolves `partial$...` through its content catalog, and unlike
* plain AsciiDoctor it does *not* honor the `optional` attribute for
* missing resources — it logs an "unresolved include" error. So the
* generator must only emit include directives for partials that are
* actually present on disk.
*/
private fun derivePartialsRoot(outputDir: File): File? {
val path = outputDir.absolutePath.replace(File.separatorChar, '/')
val marker = "/pages/"
val idx = path.indexOf(marker)
if (idx < 0) return null
val moduleRoot = File(path.substring(0, idx))
return File(moduleRoot, "partials")
}

/**
* Per-function section layout fuses auto-derived facts (signature,
* parameters, return type, backend matrix) with optional hand-written
Expand All @@ -284,8 +304,15 @@ abstract class GenerateDocumentationTask : DefaultTask() {
* single file per function carries all the human content, and missing
* tags render as empty via `optional`, keeping un-prosed ops valid.
*/
private fun generateFunctionSection(operator: OperatorDoc, function: FunctionDoc, builder: StringBuilder) {
val partialBase = "ops/${operator.name.lowercase()}/${function.name.lowercase()}.adoc"
private fun generateFunctionSection(
operator: OperatorDoc,
function: FunctionDoc,
builder: StringBuilder,
partialsRoot: File?,
) {
val partialRelative = "ops/${operator.name.lowercase()}/${function.name.lowercase()}.adoc"
val partialFile = partialsRoot?.let { File(it, partialRelative) }
val hasPartial = partialFile?.isFile == true
builder.apply {
appendLine("== ${function.name}")
appendLine("")
Expand Down Expand Up @@ -314,24 +341,24 @@ abstract class GenerateDocumentationTask : DefaultTask() {
appendLine("`${function.returnType}`")
appendLine("")

// Human prose: math first so LaTeX sits right under the signature,
// then intuition and examples before the backend table, references
// last. All optional — ops with no partial still render cleanly.
appendLine("=== Definition")
appendLine("")
appendLine("include::partial\$$partialBase[tag=math,optional]")
appendLine("")
appendLine("=== Intuition")
appendLine("")
appendLine("include::partial\$$partialBase[tag=intuition,optional]")
appendLine("")
appendLine("=== Examples")
appendLine("")
appendLine("include::partial\$$partialBase[tag=examples,optional]")
appendLine("")

if (includeBackendStatus.getOrElse(true) && function.statusByBackend.isNotEmpty()) {
generateBackendStatusTable(function, this)
// Human prose: only emitted when a partial actually exists on
// disk. Antora does not honor the `optional` attribute for
// `partial$` resource refs, so emitting includes for missing
// partials produces "unresolved include" errors on the
// published site.
if (hasPartial) {
appendLine("=== Definition")
appendLine("")
appendLine("include::partial\$$partialRelative[tag=math,optional]")
appendLine("")
appendLine("=== Intuition")
appendLine("")
appendLine("include::partial\$$partialRelative[tag=intuition,optional]")
appendLine("")
appendLine("=== Examples")
appendLine("")
appendLine("include::partial\$$partialRelative[tag=examples,optional]")
appendLine("")
}

if (function.notes.isNotEmpty()) {
Expand All @@ -343,42 +370,12 @@ abstract class GenerateDocumentationTask : DefaultTask() {
}
}

appendLine("=== References")
appendLine("")
appendLine("include::partial\$$partialBase[tag=references,optional]")
appendLine("")
}
}

private fun generateBackendStatusTable(function: FunctionDoc, builder: StringBuilder) {
builder.apply {
appendLine("=== Backend Support")
appendLine("")
appendLine("[cols=\"1,1,3\", options=\"header\"]")
appendLine("|===")
appendLine("| Backend | Status | Notes")

function.statusByBackend.forEach { (backend, status) ->
val formattedStatus = formatStatus(status)
val notes = function.notes
.filter { it.backend.equals(backend, ignoreCase = true) }
.joinToString("; ") { it.message }

appendLine("| $backend | $formattedStatus | ${notes.ifEmpty { "-" }}")
if (hasPartial) {
appendLine("=== References")
appendLine("")
appendLine("include::partial\$$partialRelative[tag=references,optional]")
appendLine("")
}

appendLine("|===")
appendLine("")
}
}

private fun formatStatus(status: String): String {
return when (status.lowercase()) {
"supported" -> "✅ Supported"
"partial" -> "⚠️ Partial"
"not_supported" -> "❌ Not Supported"
"planned" -> "📋 Planned"
else -> status
}
}

Expand Down
12 changes: 12 additions & 0 deletions docs/antora-playbook.yml
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,13 @@ content:
branches: HEAD

asciidoc:
attributes:
# LaTeX syntax for stem:[…] and [stem] blocks. Without this the
# partials under partials/ops/**/*.adoc would be parsed as AsciiMath,
# which doesn't understand `\in`, `\mathbb`, `\sum_{…}^{…}` etc. and
# passes them through as literal text. MathJax is loaded by
# supplemental-ui/partials/footer-scripts.hbs.
stem: latexmath
extensions:
# Local mermaid block processor — renders every `[mermaid]` block
# inline by invoking the @mermaid-js/mermaid-cli binary baked into
Expand All @@ -36,6 +43,11 @@ ui:
# this URL to a newer version is a one-line PR.
url: https://github.com/SKaiNET-developers/skainet-docs-ui/releases/download/v1.1.1/ui-bundle.zip
snapshot: true
# Local overrides layered on top of the UI bundle. The footer-scripts
# partial is replaced to inject MathJax so stem:[…] / [stem] blocks
# render properly; upstream the change to skainet-docs-ui once the
# math-in-ops-docs surface stabilises.
supplemental_files: ./supplemental-ui

output:
dir: ./build/site
Original file line number Diff line number Diff line change
@@ -1,13 +1,12 @@
= AI-NET Operators Reference

Generated from version `1.0.0` on 2026-04-14
Generated from version `0.18.0` on 2026-04-15

== Operators by Modality

=== Core

* xref:reference/operators/generated/tensorops.adoc[TensorOps]
* xref:reference/operators/generated/voidtensorops.adoc[VoidTensorOps]

=== Composite

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,40 +16,17 @@ fun cosineDistance(other:Tensor, dim:Int, eps:Double): Tensor
=== Parameters

* `other: Tensor`
The other tensor to calculate the distance to.
* `dim: Int`
The dimension along which to calculate the cosine distance. Default is -1 (last dimension).
* `eps: Double`
A small value to avoid division by zero. Default is 1e-8.

=== Return Type

`Tensor`

=== Definition

include::partial$ops/similarity/cosinedistance.adoc[tag=math,optional]

=== Intuition

include::partial$ops/similarity/cosinedistance.adoc[tag=intuition,optional]

=== Examples

include::partial$ops/similarity/cosinedistance.adoc[tag=examples,optional]

=== Backend Support

[cols="1,1,3", options="header"]
|===
| Backend | Status | Notes
| cpu | implemented | -
| wasm | implemented | -
| apple | implemented | -
|===

=== Notes

TIP: *all*:

=== References

include::partial$ops/similarity/cosinedistance.adoc[tag=references,optional]

Loading
Loading