Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 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
1 change: 1 addition & 0 deletions .github/workflows/fern-docs-preview-comment.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ jobs:
HEAD_REF: ${{ steps.metadata.outputs.head_ref }}
working-directory: ./docs/fern
run: |
node scripts/filter-public-openapi.mjs
OUTPUT=$(npx -y fern-api@latest generate --docs --preview --id "$HEAD_REF" 2>&1)
echo "$OUTPUT"
URL=$(echo "$OUTPUT" | grep -oP 'Published docs to \K.*(?= \()')
Expand Down
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -143,3 +143,6 @@ docker/logs/*
# Linting artifact
python-sdk-lint/
site/

# Generated Fern-only public API reference spec
docs/fern/openapi/openapi.public.yaml
2 changes: 1 addition & 1 deletion docs/AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,4 +39,4 @@ These are generated; edit the source and regenerate:

## API reference

The REST API reference is rendered natively by Fern from the OpenAPI spec — `docs/fern/generators.yml` points at `docs/fern/openapi/openapi.yaml` (a symlink to the generated repo-root `openapi/openapi.yaml`), surfaced by the `- api: API Reference` nav node. No `<swagger-ui>` embed.
The REST API reference is rendered natively by Fern from a docs-only filtered OpenAPI spec. `docs/fern/openapi/openapi.yaml` is a symlink to the generated repo-root `openapi/openapi.yaml`, and `npm run prepare:openapi` writes `docs/fern/openapi/openapi.public.yaml` for Fern. `docs/fern/generators.yml` must point at the public filtered spec, surfaced by the `- api: API Reference` nav node. No `<swagger-ui>` embed.
4 changes: 2 additions & 2 deletions docs/agents/plugins.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ description: ""
---
<a id="agents-plugins"></a>

NeMo Platform 0.1.0 is extended through Python plugins and
NeMo Platform 0.2.0 is extended through Python plugins and
coding-agent skills. Plugins add runtime capabilities to the local platform.
Skills teach your coding agent how to operate those capabilities on your behalf.

Expand Down Expand Up @@ -103,5 +103,5 @@ Plugin authors use the `nemo-platform-plugin` package. A minimal plugin declares
points in `pyproject.toml`, implements the matching class, and restarts
`nemo services run` so the local platform discovers it.

Keep plugins local-first for OSS 0.1.0: avoid requiring a cluster,
Keep plugins local-first for OSS 0.2.0: avoid requiring a cluster,
container runtime, or external control plane for the documented launch path.
2 changes: 1 addition & 1 deletion docs/auth/authorization/managing-access.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ Add users to workspaces, assign roles, and control who can access your resources

<Note>

The SDK examples on this page use `NeMoPlatform()` with no arguments so that the client reads your active CLI context (set by `nemo auth login`). That is the right pattern for authorization workflows: you act as your logged-in identity and pass the workspace explicitly in each API call. For the standard local initialization pattern, see [CLI and SDK initialization](/documentation/get-started#setup-init).
The SDK examples on this page use `NeMoPlatform()` with no arguments so that the client reads your active CLI context (set by `nemo auth login`). That is the right pattern for authorization workflows: you act as your logged-in identity and pass the workspace explicitly in each API call. For the standard local initialization pattern, see [CLI and SDK initialization](/documentation/get-started#nemo-setup).

</Note>
## Creating Workspaces
Expand Down
16 changes: 0 additions & 16 deletions docs/auth/authorization/permissions-reference.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -58,22 +58,6 @@ PlatformAdmin is omitted — it bypasses permission checks entirely at the polic
| <code>inference.virtual-models.(read &#124; list)</code> | Read, list inference virtual-models | ✓ | ✓ | ✓ |
| <code>inference.virtual-models.(create &#124; update &#124; delete)</code> | Create, update, delete inference virtual-models | | ✓ | ✓ |

## Intake API

| Permission | Description | Viewer | Editor | Admin |
|------------|-------------|:------:|:------:|:-----:|
| <code>intake.annotations.(read &#124; list)</code> | Read, list intake annotations | ✓ | ✓ | ✓ |
| <code>intake.annotations.(create &#124; delete)</code> | Create, delete intake annotations | | ✓ | ✓ |
| <code>intake.evaluator-results.(read &#124; list)</code> | Read, list intake evaluator-results | ✓ | ✓ | ✓ |
| `intake.evaluator-results.create` | Create intake evaluator results | | ✓ | ✓ |
| `intake.experiment-groups.read` | Read intake experiment groups | ✓ | ✓ | ✓ |
| <code>intake.experiment-groups.(create &#124; update &#124; delete)</code> | Create, update, delete intake experiment-groups | | ✓ | ✓ |
| `intake.experiments.read` | Read intake experiments | ✓ | ✓ | ✓ |
| <code>intake.experiments.(create &#124; update &#124; delete)</code> | Create, update, delete intake experiments | | ✓ | ✓ |
| `intake.ingest.create` | Ingest traces into intake | | ✓ | ✓ |
| <code>intake.spans.(read &#124; list)</code> | Read, list intake spans | ✓ | ✓ | ✓ |
| `intake.traces.read` | Read intake traces | ✓ | ✓ | ✓ |

## Jobs API

| Permission | Description | Viewer | Editor | Admin |
Expand Down
1 change: 0 additions & 1 deletion docs/contributing/skills-spec.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@
title: "NeMo Platform Skills Spec"
description: ""
---
**Status:** Draft.

This document defines the conventions for skills shipped with NeMo Platform: how they're structured, what their frontmatter must contain, how they get tested, and where they live in the repo.

Expand Down
2 changes: 1 addition & 1 deletion docs/customizer/tutorials/format-training-dataset.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -236,7 +236,7 @@ For illustrative purposes only, we show an example entry as multi-line JSON.
</Accordion>
##### Shared Tools

When your dataset uses the same set of tools across all examples, include the shared tool definitions in **each training row** (Automodel jobs do not accept a separate `dataset_parameters` block). See the [Tool Calling tutorial](/documentation/example-applications/tool-calling) for a full workflow.
When your dataset uses the same set of tools across all examples, include the shared tool definitions in **each training row** (Automodel jobs do not accept a separate `dataset_parameters` block). The full tool-calling workflow is maintained in the gated example applications section and will be linked here when that section ships publicly.

```python
import os
Expand Down
2 changes: 1 addition & 1 deletion docs/evaluator/tutorials/index.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -32,4 +32,4 @@ Learn how to write a domain-specific Python metric, test it locally, and run it

## How It Works

For the conceptual overview of how Evaluator separates definition (library) from execution (platform), see [About Evaluating → How It Works](/documentation/evaluate-models#how-it-works-library-platform). For runnable SDK examples, see [SDK Resources](/documentation/evaluate-models/sdk-resources).
For the conceptual overview of how Evaluator separates definition (library) from execution (platform), see [About Evaluating → How It Works](/documentation/evaluate-models#how-it-works-library--platform). For runnable SDK examples, see [SDK Resources](/documentation/evaluate-models/sdk-resources).
Comment thread
mckornfield marked this conversation as resolved.
5 changes: 3 additions & 2 deletions docs/fern/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ docs/ # page content (.mdx), one tree per product area
├── docs.yml # Site config, theme, css, redirects, versions
├── generators.yml # OpenAPI spec wiring for the API reference
├── openapi/openapi.yaml # symlink -> repo-root openapi/openapi.yaml (generated)
├── openapi/openapi.public.yaml # generated Fern-only filtered spec (gitignored)
├── styles/ # CSS (notebook-viewer.css, button.css)
├── assets/ # logos, shared images
├── components/ # custom TSX MDX components
Expand Down Expand Up @@ -81,10 +82,10 @@ Avoid source-path links such as `/get-started/concepts/workspaces`, `/latest/get

The REST API reference is generated natively by Fern from the OpenAPI spec — no `<swagger-ui>` embed. Two pieces wire it up:

- `generators.yml` declares the spec: `api.specs[].openapi: ./openapi/openapi.yaml`.
- `generators.yml` declares the docs-only public spec: `api.specs[].openapi: ./openapi/openapi.public.yaml`.
- `versions/latest.yml` surfaces it with an `- api: API Reference` navigation node (under the **Reference** section).

`openapi/openapi.yaml` is a symlink to the repo-root `openapi/openapi.yaml` (the generated source of truth), so the reference tracks the Platform API automatically. Regenerate the spec with `make refresh-openapi` from the repo root, then run `npm run check`.
`openapi/openapi.yaml` is a symlink to the repo-root `openapi/openapi.yaml` (the generated source of truth). Fern reads `openapi/openapi.public.yaml`, which is generated by `scripts/filter-public-openapi.mjs` and excludes services that should stay out of public docs while remaining in the root spec for SDK generation and API validation. Regenerate the root spec with `make refresh-openapi` from the repo root, then run `npm run check`.

Fern groups endpoints by their OpenAPI tag in the sidebar (Customizer, Evaluator, Guardrails, …), which replaces the old per-service filter chips. Link to it from other pages with the nav URL `/documentation/reference/api-reference`.

Expand Down
2 changes: 1 addition & 1 deletion docs/fern/generators.yml
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
api:
specs:
- openapi: ./openapi/openapi.yaml
- openapi: ./openapi/openapi.public.yaml
13 changes: 7 additions & 6 deletions docs/fern/package.json
Original file line number Diff line number Diff line change
@@ -1,16 +1,17 @@
{
"private": true,
"scripts": {
"check": "npx -y fern-api@latest check && npm run validate-mdx && npm run check:gated-links",
"check:fern": "npx -y fern-api@latest check",
"prepare:openapi": "node scripts/filter-public-openapi.mjs",
"check": "npm run prepare:openapi && npx -y fern-api@latest check && npm run validate-mdx && npm run check:gated-links",
"check:fern": "npm run prepare:openapi && npx -y fern-api@latest check",
"validate-mdx": "node scripts/validate-mdx.mjs",
"check:gated-links": "node scripts/delink-gated.mjs",
"fix:gated-links": "node scripts/delink-gated.mjs --fix",
"broken-links": "npx -y fern-api@latest docs broken-links",
"dev": "npx -y fern-api@latest docs dev",
"broken-links": "npm run prepare:openapi && npx -y fern-api@latest docs broken-links",
"dev": "npm run prepare:openapi && npx -y fern-api@latest docs dev",
"watch": "node scripts/docs-watch.mjs",
"generate": "npx -y fern-api@latest generate --docs",
"preview": "npx -y fern-api@latest generate --docs --preview"
"generate": "npm run prepare:openapi && npx -y fern-api@latest generate --docs",
"preview": "npm run prepare:openapi && npx -y fern-api@latest generate --docs --preview"
},
"devDependencies": {
"@mdx-js/mdx": "^3.1.0"
Expand Down
72 changes: 58 additions & 14 deletions docs/fern/scripts/docs-watch.mjs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
// SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
// SPDX-License-Identifier: Apache-2.0

import { spawn } from "node:child_process";
import { spawn, spawnSync } from "node:child_process";
import { watch } from "node:fs";
import { utimes } from "node:fs/promises";
import { constants } from "node:os";
Expand All @@ -14,8 +14,12 @@ const docsRoot = path.resolve(fernDir, "..");
const reloadTrigger = path.join(fernDir, "docs.yml");
const reloadDebounceMs = 150;
const ignoredPrefix = "fern/";
const openapiInputPrefix = "fern/openapi/";
const publicOpenapiPath = "fern/openapi/openapi.public.yaml";
const filterOpenapiScriptPath = "fern/scripts/filter-public-openapi.mjs";

let debounceTimer = null;
let pendingReloadRequiresOpenapi = false;
let shuttingDown = false;

function log(message) {
Expand All @@ -36,36 +40,76 @@ function clearPendingReload() {
debounceTimer = null;
}

function scheduleReload(relativePath) {
function scheduleReload(relativePath, requiresOpenapiPrepare = false) {
clearPendingReload();
pendingReloadRequiresOpenapi = pendingReloadRequiresOpenapi || requiresOpenapiPrepare;

debounceTimer = setTimeout(() => {
debounceTimer = null;
touchReloadTrigger(relativePath).catch((error) => {
log(`failed to trigger reload: ${error.message}`);
});
const shouldRunOpenapiPrepare = pendingReloadRequiresOpenapi;
pendingReloadRequiresOpenapi = false;
Promise.resolve()
.then(() => {
if (shouldRunOpenapiPrepare) {
prepareOpenapi();
}
})
.then(() => touchReloadTrigger(relativePath))
.catch((error) => {
log(`failed to trigger reload: ${error.message}`);
});
}, reloadDebounceMs);
}

function shouldIgnore(relativePath) {
const normalized = path.posix.normalize(relativePath.split(path.sep).join("/"));
return normalized.startsWith(ignoredPrefix);
function normalizeWatchedPath(relativePath) {
return path.posix.normalize(relativePath.split(path.sep).join("/"));
}

const fern = spawn("npx", ["-y", "fern-api@latest", "docs", "dev"], {
cwd: fernDir,
stdio: "inherit",
});
function shouldPrepareOpenapi(normalizedPath) {
return (
normalizedPath === filterOpenapiScriptPath ||
(normalizedPath.startsWith(openapiInputPrefix) && normalizedPath !== publicOpenapiPath)
);
}

function shouldIgnore(normalizedPath) {
return normalizedPath.startsWith(ignoredPrefix) && !shouldPrepareOpenapi(normalizedPath);
}

function prepareOpenapi() {
const result = spawnSync("node", ["scripts/filter-public-openapi.mjs"], {
cwd: fernDir,
stdio: "inherit",
});

if (result.error) {
throw result.error;
}
if (result.status !== 0) {
process.exit(result.status ?? 1);
}
}

function spawnFernDev() {
prepareOpenapi();
return spawn("npx", ["-y", "fern-api@latest", "docs", "dev"], {
cwd: fernDir,
stdio: "inherit",
});
}

Comment thread
coderabbitai[bot] marked this conversation as resolved.
const fern = spawnFernDev();

const watcher = watch(
docsRoot,
{ recursive: true },
(_eventType, filename) => {
const relativePath = filename ? filename.toString() : "";
if (shouldIgnore(relativePath)) {
const normalizedPath = normalizeWatchedPath(relativePath);
if (shouldIgnore(normalizedPath)) {
return;
}
scheduleReload(relativePath);
scheduleReload(relativePath, shouldPrepareOpenapi(normalizedPath));
},
);

Expand Down
Loading
Loading