diff --git a/.github/workflows/linkcheck-pr.yml b/.github/workflows/linkcheck-pr.yml
index b498e5973b..e6a473b4b6 100644
--- a/.github/workflows/linkcheck-pr.yml
+++ b/.github/workflows/linkcheck-pr.yml
@@ -1,33 +1,213 @@
-name: Lychee PR link checker
+name: Link checker - PR changed files
+
+# Avoid collisions by ensuring only one run per ref
+concurrency:
+ group: linkcheck-pr-${{ github.ref_name }}
+ cancel-in-progress: false
on:
+ workflow_dispatch:
deployment_status:
+ pull_request:
+ types: [opened, synchronize, reopened]
+ paths:
+ - '**.md'
+ - '**.mdx'
permissions:
contents: read
deployments: read
+ pull-requests: write
jobs:
linkChecker:
runs-on: ubuntu-latest
-
- # Only run when Mintlify PR deployment succeeds
+
+ # Run on: manual trigger, successful Mintlify deployment, or PR events (for forks)
if: |
- github.event.deployment_status.state == 'success' &&
- github.event.deployment.environment == 'staging' &&
- contains(github.event.deployment_status.creator.login, 'mintlify') &&
- contains(github.event.deployment_status.environment_url, 'mintlify')
+ github.event_name == 'workflow_dispatch' ||
+ github.event_name == 'pull_request' ||
+ (github.event_name == 'deployment_status' &&
+ github.event.deployment_status.state == 'success' &&
+ github.event.deployment.environment == 'staging' &&
+ contains(github.event.deployment_status.creator.login, 'mintlify') &&
+ contains(github.event.deployment_status.environment_url, 'mintlify'))
+
steps:
- # check URLs with Lychee
- uses: actions/checkout@v6
+ with:
+ # Needed to diff base..head for the associated PR
+ fetch-depth: 0
+
+ - name: Resolve PR and deployment URL
+ id: pr-context
+ if: github.event_name == 'deployment_status'
+ uses: actions/github-script@v7
+ with:
+ script: |
+ const { owner, repo } = context.repo;
+ const sha = context.payload.deployment?.sha;
+ const deployUrl =
+ context.payload.deployment_status?.environment_url ||
+ context.payload.deployment_status?.target_url ||
+ '';
+
+ core.info(`Deployment SHA: ${sha}`);
+ core.info(`Deployment URL: ${deployUrl}`);
+
+ // Find PR(s) associated with this deployment commit SHA
+ const prsResp = await github.rest.repos.listPullRequestsAssociatedWithCommit({
+ owner,
+ repo,
+ commit_sha: sha,
+ });
+
+ const pr = prsResp.data?.[0];
+ if (!pr) {
+ core.warning(`No PR associated with commit ${sha}. Skipping linkcheck + PR comment.`);
+ core.setOutput('pr_number', '');
+ core.setOutput('deploy_url', deployUrl);
+ return;
+ }
+
+ core.info(`Associated PR: #${pr.number} (${pr.html_url})`);
+ core.setOutput('pr_number', String(pr.number));
+ core.setOutput('base_sha', pr.base.sha);
+ core.setOutput('head_sha', pr.head.sha);
+ core.setOutput('deploy_url', deployUrl);
+
+ - name: Get changed documentation files
+ id: changed-files
+ if: steps.pr-context.outputs.pr_number != '' || github.event_name == 'workflow_dispatch' || github.event_name == 'pull_request'
+ uses: tj-actions/changed-files@v47.0.1
+ with:
+ base_sha: ${{ steps.pr-context.outputs.base_sha || github.event.pull_request.base.sha }}
+ sha: ${{ steps.pr-context.outputs.head_sha || github.event.pull_request.head.sha }}
+ files: |
+ **/*.md
+ **/*.mdx
- name: Link Checker
id: lychee
+ if: steps.changed-files.outputs.any_changed == 'true' || github.event_name == 'workflow_dispatch'
uses: lycheeverse/lychee-action@v2
with:
- args: "--threads 5 --max-retries 5 --retry-wait-time 2 --include '^https?://' --include '^http?://' --base-url='${{ github.event.deployment_status.environment_url }}' '${{ github.event.deployment_status.environment_url }}'"
- format: markdown
fail: false
- env:
- # to be used in case rate limits are surpassed
- GITHUB_TOKEN: ${{secrets.GITHUB_TOKEN}}
+ # Don't fail if no files to check
+ failIfEmpty: false
+ # Output format for reports
+ format: markdown
+ # GitHub token for API rate limiting
+ token: ${{ secrets.GITHUB_TOKEN }}
+ # Override base_url with deployment URL (if available) or use production
+ # For forks without Mintlify preview: checks against production site
+ args: >-
+ --base-url ${{ steps.pr-context.outputs.deploy_url || 'https://docs.wandb.ai' }}
+ ${{ steps.changed-files.outputs.all_changed_files || '.' }}
+
+ - name: Comment PR with link check results
+ if: (steps.pr-context.outputs.pr_number != '' || github.event_name == 'pull_request') && (steps.changed-files.outputs.any_changed == 'true' || github.event_name == 'workflow_dispatch')
+ uses: actions/github-script@v7
+ with:
+ script: |
+ const fs = require('fs');
+ const identifier = '';
+
+ let commentBody = identifier + '\n';
+
+ const isFork = context.payload.pull_request?.head?.repo?.fork || false;
+ const deployUrl = '${{ steps.pr-context.outputs.deploy_url }}';
+
+ if (${{ steps.lychee.outputs.exit_code }} === 0) {
+ // Success - no broken links
+ commentBody += '## 🔗 Link Checker Results\n\n';
+ commentBody += '✅ **All links are valid!**\n\n';
+ commentBody += 'No broken links were detected in the changed files.\n';
+ if (isFork && !deployUrl) {
+ commentBody += '\n_Note: Checked against production site (https://docs.wandb.ai) since preview deployments are not available for forks._\n';
+ }
+
+ // Check if there were redirects in the report
+ try {
+ const report = fs.readFileSync('./lychee/out.md', 'utf8');
+ if (report.includes('Redirect') || report.includes('redirect')) {
+ commentBody += '\n\n> [!TIP]\n';
+ commentBody += '> **Redirects detected**: If you see redirects for internal docs.wandb.ai links, check if they have trailing slashes.\n';
+ commentBody += '> \n';
+ commentBody += '> Mintlify automatically removes trailing slashes, causing redirects like:\n';
+ commentBody += '> - `https://docs.wandb.ai/models/` → `https://docs.wandb.ai/models`\n';
+ commentBody += '> \n';
+ commentBody += '> **Fix**: Remove trailing slashes from links to avoid unnecessary redirects.\n';
+ }
+ } catch (e) {
+ // Ignore if report file doesn't exist
+ }
+ } else {
+ // Issues found - include report
+ const report = fs.readFileSync('./lychee/out.md', 'utf8');
+
+ commentBody += '## 🔗 Link Checker Results\n\n';
+ commentBody += '> [!NOTE]\n';
+ if (isFork && !deployUrl) {
+ commentBody += '> This PR is from a fork, so links were checked against the **production site** (https://docs.wandb.ai).\n';
+ commentBody += '> \n';
+ commentBody += '> Links to **newly created files** in this PR will be reported as broken until the PR is merged.\n';
+ } else {
+ commentBody += '> Links to **newly created files** in this PR may be reported as broken because this checks links against the **preview deployment**.\n';
+ }
+ commentBody += '> \n';
+ commentBody += '> Warnings about **new** files in this PR can be safely ignored.\n\n';
+
+ // Add trailing slash tip if redirects are present
+ if (report.includes('Redirect') || report.includes('redirect')) {
+ commentBody += '> [!TIP]\n';
+ commentBody += '> **Redirects detected**: If you see redirects for internal docs.wandb.ai links, check if they have trailing slashes.\n';
+ commentBody += '> \n';
+ commentBody += '> Mintlify automatically removes trailing slashes, causing redirects like:\n';
+ commentBody += '> - `https://docs.wandb.ai/models/` → `https://docs.wandb.ai/models`\n';
+ commentBody += '> - `/weave/quickstart/` → `/weave/quickstart`\n';
+ commentBody += '> \n';
+ commentBody += '> **Fix**: Remove trailing slashes from links to avoid unnecessary redirects.\n\n';
+ }
+
+ commentBody += '---\n\n';
+ commentBody += report;
+ }
+
+ // Determine PR number
+ const prNumber = Number('${{ steps.pr-context.outputs.pr_number }}') ||
+ context.payload.pull_request?.number;
+
+ if (!prNumber) {
+ core.info('No PR number available, skipping comment');
+ return;
+ }
+
+ // Find existing comment
+ const { data: comments } = await github.rest.issues.listComments({
+ owner: context.repo.owner,
+ repo: context.repo.repo,
+ issue_number: prNumber,
+ });
+
+ const existingComment = comments.find(comment =>
+ comment.body?.includes(identifier) && comment.user?.login === 'github-actions[bot]'
+ );
+
+ if (existingComment) {
+ // Update existing comment
+ await github.rest.issues.updateComment({
+ owner: context.repo.owner,
+ repo: context.repo.repo,
+ comment_id: existingComment.id,
+ body: commentBody
+ });
+ } else {
+ // Create new comment
+ await github.rest.issues.createComment({
+ owner: context.repo.owner,
+ repo: context.repo.repo,
+ issue_number: prNumber,
+ body: commentBody
+ });
+ }
diff --git a/.github/workflows/linkcheck-prod.yml b/.github/workflows/linkcheck-prod.yml
index a72662d688..62f0d2dfc1 100644
--- a/.github/workflows/linkcheck-prod.yml
+++ b/.github/workflows/linkcheck-prod.yml
@@ -1,30 +1,44 @@
-name: Lychee production link checker
+name: Link checker - production site
+
on:
repository_dispatch:
workflow_dispatch:
schedule:
- - cron: "5 0 1 * *" # In UTC, currently 12:05 AM on the 1st of each month
+ - cron: "5 0 1 * *" # Monthly on the 1st at 12:05 AM UTC
jobs:
linkChecker:
runs-on: ubuntu-latest
permissions:
- issues: write # required for peter-evans/create-issue-from-file
+ issues: write # Required for creating issues
steps:
- # check URLs with Lychee
- uses: actions/checkout@v6
+ - name: Download and parse sitemap
+ run: |
+ echo "Fetching sitemap from https://docs.wandb.ai/sitemap.xml..."
+ curl -s https://docs.wandb.ai/sitemap.xml | \
+ grep -o '[^<]*' | \
+ sed 's///g; s/<\/loc>//g' > urls.txt
+
+ URL_COUNT=$(wc -l < urls.txt | tr -d ' ')
+ echo "Found ${URL_COUNT} URLs in sitemap"
+
+ # Show first few URLs for verification
+ echo "Sample URLs:"
+ head -5 urls.txt
+
- name: Link Checker
id: lychee
uses: lycheeverse/lychee-action@v2
with:
- args: "--threads 5 --max-retries 5 --retry-wait-time 2 --include '^https?://' --include '^http?://' --base-url='http://docs.wandb.ai' 'http://docs.wandb.ai'"
+ # Configuration is in lychee.toml
+ # Check all URLs from sitemap
+ args: "urls.txt"
output: ./lychee-report.md
format: markdown
fail: false
- env:
- # to be used in case rate limits are surpassed
- GITHUB_TOKEN: ${{secrets.GITHUB_TOKEN}}
+ token: ${{ secrets.GITHUB_TOKEN }}
- name: Create Issue From File
if: steps.lychee.outputs.exit_code != 0
diff --git a/get-started.mdx b/get-started.mdx
index 9fb9d882b7..7afa28a84f 100644
--- a/get-started.mdx
+++ b/get-started.mdx
@@ -26,6 +26,9 @@ Welcome to Weights & Biases! Before getting started with our products, it's impo
A video-led course that emphasizes experiment tracking and features quizzes to ensure comprehension.
+
+ Check out example code for getting started (this is a broken test link).
+
Learn how models are trained, evaluated, developed, and deployed and how you can use wandb at each step of that lifecycle to help build better performing models faster.
diff --git a/index.mdx b/index.mdx
index b9b26147e9..a472bd6bfa 100644
--- a/index.mdx
+++ b/index.mdx
@@ -19,6 +19,7 @@ import {ProductCard} from "/snippets/ProductCard.jsx";
Use W&B Models to manage AI model development. Features include training, fine-tuning, reporting, automating hyperparameter sweeps, and utilizing the model registry for versioning and reproducibility.
+ • Totally invalid link
• Introduction
• Quickstart
• YouTube Tutorial
diff --git a/lychee.toml b/lychee.toml
new file mode 100644
index 0000000000..a62763ed06
--- /dev/null
+++ b/lychee.toml
@@ -0,0 +1,53 @@
+# Lychee link checker configuration
+# See https://lychee.cli.rs/guides/config/ for full documentation
+
+# Base URL for resolving relative links
+# This will be overridden at runtime by the workflows
+base_url = 'https://docs.wandb.ai'
+
+# Don't check heading anchors (fragments) - Mintlify generates these dynamically
+include_fragments = false
+
+# Suppress progress bars in CI for cleaner logs
+no_progress = true
+
+# Exclude private/internal network links
+exclude_all_private = true
+
+# Retry configuration for handling transient failures
+max_retries = 5
+retry_wait_time = 2
+
+# Accept these HTTP status codes as valid
+accept = [
+ 200, # OK
+ 429, # Too Many Requests (rate limit - treat as success)
+]
+
+# Only check HTTP/HTTPS URLs
+scheme = [
+ "https",
+ "http",
+]
+
+# Only check URLs matching these patterns (required when checking URL lists)
+include = [
+ '^https?://', # Match all http:// and https:// URLs
+]
+
+# Logging verbosity
+verbose = "info"
+
+# URL patterns to exclude from checking
+exclude = [
+ # Exclude images - Mintlify rewrites paths during build
+ '\.(png|jpg|jpeg|gif|svg|webp|ico)$',
+]
+
+# File/directory paths to exclude from checking
+exclude_path = [
+ # Exclude non-documentation directories
+ "scripts",
+ "node_modules",
+ ".github",
+]
diff --git a/models/quickstart.mdx b/models/quickstart.mdx
index 720caf5fdd..8d6bad7da7 100644
--- a/models/quickstart.mdx
+++ b/models/quickstart.mdx
@@ -8,7 +8,7 @@ import ApiKeyCreate from "/snippets/en/_includes/api-key-create.mdx";
Install W&B to track, visualize, and manage machine learning experiments of any size.
-Are you looking for information on W&B Weave? See the [Weave Python SDK quickstart](/weave/quickstart) or [Weave TypeScript SDK quickstart](/weave/reference/generated_typescript_docs/intro-notebook).
+Are you looking for information on W&B Weave? See the [Weave Python SDK quickstart](/weave/quickstart) or [Weave TypeScript SDK quickstart](/weave/reference/generated_typescript_docs/intro-notebook). For advanced features, see the [Advanced Guide](/models/nonexistent-advanced-guide-xyz123).
## Sign up and create an API key
diff --git a/weave/cookbooks/source/dspy_prompt_optimization.ipynb b/weave/cookbooks/source/dspy_prompt_optimization.ipynb
index 14afde3f63..8ce172ec72 100644
--- a/weave/cookbooks/source/dspy_prompt_optimization.ipynb
+++ b/weave/cookbooks/source/dspy_prompt_optimization.ipynb
@@ -127,7 +127,7 @@
"source": [
"## Load the BIG-Bench Hard Dataset\n",
"\n",
- "We will load this dataset from HuggingFace Hub, split into training and validation sets, and [publish](../../guides/core-types/datasets.md) them on Weave, this will let us version the datasets, and also use [`weave.Evaluation`](../../guides/core-types/evaluations.md) to evaluate our prompting strategy."
+ "We will load this dataset from HuggingFace Hub, split into training and validation sets, and [publish](/weave/guides/core-types/datasets) them on Weave, this will let us version the datasets, and also use [`weave.Evaluation`](/weave/guides/core-types/evaluations) to evaluate our prompting strategy."
]
},
{
@@ -287,7 +287,7 @@
"source": [
"## Evaluating our DSPy Program\n",
"\n",
- "Now that we have a baseline prompting strategy, let's evaluate it on our validation set using [`weave.Evaluation`](../../guides/core-types/evaluations.md) on a simple metric that matches the predicted answer with the ground truth. Weave will take each example, pass it through your application and score the output on multiple custom scoring functions. By doing this, you'll have a view of the performance of your application, and a rich UI to drill into individual outputs and scores.\n",
+ "Now that we have a baseline prompting strategy, let's evaluate it on our validation set using [`weave.Evaluation`](/weave/guides/core-types/evaluations) on a simple metric that matches the predicted answer with the ground truth. Weave will take each example, pass it through your application and score the output on multiple custom scoring functions. By doing this, you'll have a view of the performance of your application, and a rich UI to drill into individual outputs and scores.\n",
"\n",
"First, we need to create a simple weave evaluation scoring function that tells whether the answer from the baseline module's output is the same as the ground truth answer or not. Scoring functions need to have a `model_output` keyword argument, but the other arguments are user defined and are taken from the dataset examples. It will only take the necessary keys by using a dictionary key based on the argument name."
]
diff --git a/weave/guides/integrations/notdiamond.mdx b/weave/guides/integrations/notdiamond.mdx
index 638b5c0c60..0fba392dfd 100644
--- a/weave/guides/integrations/notdiamond.mdx
+++ b/weave/guides/integrations/notdiamond.mdx
@@ -107,10 +107,10 @@ Visit the [docs] or [send us a message][support] for further support.
[chat]: https://chat.notdiamond.ai
[custom router]: https://docs.notdiamond.ai/docs/router-training-quickstart
[docs]: https://docs.notdiamond.ai
-[evals]: ../../guides/core-types/evaluations.mdx
+[evals]: /weave/guides/core-types/evaluations
[keys]: https://app.notdiamond.ai/keys
[nd]: https://www.notdiamond.ai/
-[ops]: ../../guides/tracking/ops.mdx
+[ops]: /weave/guides/tracking/ops
[python]: https://github.com/Not-Diamond/notdiamond-python
[quickstart guide]: https://docs.notdiamond.ai/docs/quickstart
[support]: mailto:support@notdiamond.ai
diff --git a/weave/quickstart.mdx b/weave/quickstart.mdx
index 0f0b3e07da..895cbf101a 100644
--- a/weave/quickstart.mdx
+++ b/weave/quickstart.mdx
@@ -11,6 +11,7 @@ import WeaveQuickstartPrereqs from "/snippets/en/_includes/weave-quickstart-prer
Learn how to track LLM calls with Weave by adding tracing to your code. This quickstart walks you through tracing a request to OpenAI and viewing the results in the Weave UI.
@@ -159,4 +160,4 @@ Click the link in your terminal or paste it into your browser to open the Weave
## Next Steps
-[Get started evaluating your app](/weave/tutorial-eval) and then see how to [evaluate a RAG application](/weave/tutorial-rag).
\ No newline at end of file
+[Get started evaluating your app](/weave/tutorial-eval) and then see how to [evaluate a RAG application](/weave/tutorial-rag).
diff --git a/weave/reference/generated_typescript_docs/intro-notebook.mdx b/weave/reference/generated_typescript_docs/intro-notebook.mdx
index e2fc3d080d..8fb47c5974 100644
--- a/weave/reference/generated_typescript_docs/intro-notebook.mdx
+++ b/weave/reference/generated_typescript_docs/intro-notebook.mdx
@@ -106,7 +106,7 @@ async function demonstrateNestedTracking() {
## Dataset management
-You can create and manage datasets with Weave using the [`weave.Dataset`](../../guides/core-types/datasets.mdx) class. Similar to [Weave `Models`](../../guides/core-types/models.mdx), `weave.Dataset` helps:
+You can create and manage datasets with Weave using the [`weave.Dataset`](/weave/guides/core-types/datasets) class. Similar to [Weave `Models`](/weave/guides/core-types/models), `weave.Dataset` helps:
- Track and version your data
- Organize test cases
@@ -144,7 +144,7 @@ function createGrammarDataset(): weave.Dataset {
## Evaluation framework
-Weave supports evaluation-driven development with the [`Evaluation` class](../../guides/core-types/evaluations.mdx). Evaluations help you reliably iterate on your GenAI application. The `Evaluation` class does the following:
+Weave supports evaluation-driven development with the [`Evaluation` class](/weave/guides/core-types/evaluations). Evaluations help you reliably iterate on your GenAI application. The `Evaluation` class does the following:
- Assesses `Model` performance on a `Dataset`
- Applies custom scoring functions
@@ -224,4 +224,4 @@ async function main() {
console.error('Error running demonstrations:', error);
}
}
-```
\ No newline at end of file
+```