Skip to content
Open
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
206 changes: 193 additions & 13 deletions .github/workflows/linkcheck-pr.yml
Original file line number Diff line number Diff line change
@@ -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 = '<!-- lychee-link-checker-comment -->';

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
});
}
30 changes: 22 additions & 8 deletions .github/workflows/linkcheck-prod.yml
Original file line number Diff line number Diff line change
@@ -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 '<loc>[^<]*</loc>' | \
sed 's/<loc>//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
Expand Down
3 changes: 3 additions & 0 deletions get-started.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,9 @@ Welcome to Weights & Biases! Before getting started with our products, it's impo
<Card title="W&B 101 Course" href="https://wandb.ai/site/courses/101/">
A video-led course that emphasizes experiment tracking and features quizzes to ensure comprehension.
</Card>
<Card title="Example Code" href="https://github.com/wandb/nonexistent-repo-12345">
Check out example code for getting started (this is a broken test link).
</Card>
<Card title="YouTube Tutorial" href="https://www.youtube.com/watch?v=tHAFujRhZLA">
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.
</Card>
Expand Down
1 change: 1 addition & 0 deletions index.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -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.
<br/>
<br/>
• <a href="https://this-domain-definitely-does-not-exist-12345.com">Totally invalid link</a><br/>
• <a href="/models/">Introduction</a><br/>
• <a href="/models/quickstart/">Quickstart</a><br/>
• <a href="https://www.youtube.com/watch?v=tHAFujRhZLA">YouTube Tutorial</a><br/>
Expand Down
53 changes: 53 additions & 0 deletions lychee.toml
Original file line number Diff line number Diff line change
@@ -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",
]
2 changes: 1 addition & 1 deletion models/quickstart.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -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.

<Note>
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).
</Note>

## Sign up and create an API key
Expand Down
4 changes: 2 additions & 2 deletions weave/cookbooks/source/dspy_prompt_optimization.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -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."
]
},
{
Expand Down Expand Up @@ -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."
]
Expand Down
4 changes: 2 additions & 2 deletions weave/guides/integrations/notdiamond.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -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
Loading
Loading