diff --git a/.agents/AGENTS.md b/.agents/AGENTS.md index a4b5168991..ffc490470f 100644 --- a/.agents/AGENTS.md +++ b/.agents/AGENTS.md @@ -53,6 +53,33 @@ This repository powers the Langfuse website hosted on `langfuse.com`, including 2. Regenerate docs with `bash scripts/update_cookbook_docs.sh`. 3. Do **not** hand-edit generated files in `content/guides/cookbook/`. 4. Avoid `pnpm build` for routine edits or small UI/content changes. Prefer targeted checks or `pnpm dev`, and only run the full production build when it is necessary or explicitly requested. +5. **Always run `pnpm run format` before committing or opening a PR if you edited any file Prettier formats** (see "Passing CI checks on the first try" below). The `format` CI job runs `pnpm run format:check` and fails the build on a single unformatted file. + +## Passing CI checks on the first try + +CI runs three required checks on every PR. Run these locally before pushing to avoid a re-run cycle: + +### 1. Prettier format (`format` job) — this is the check that most often fails + +CI runs `pnpm run format:check`. To fix locally, run `pnpm run format` and commit the result. + +- Prettier formats: `.js`, `.jsx`, `.mjs`, `.cjs`, `.ts`, `.tsx`, `.json`, `.css`, `.scss`, `.md`, `.mdx`, `.yaml`/`.yml`, `.html`. Any change to one of these — including tiny edits like a one-line tweak to an MDX page, a TSX component, or `next.config.mjs` — can trigger the check. +- Prettier does **not** format files matched by `.prettierignore` (generated cookbook docs in `content/guides/cookbook/**`, `content/workshop/**`, lockfiles, generated assets in `public/`, etc.). Source notebooks (`.ipynb` in `cookbook/`) are also skipped because Prettier has no built-in parser for them. +- Config (`.prettierrc.json`): `proseWrap: "preserve"` (don't reflow Markdown prose), `embeddedLanguageFormatting: "off"` (fenced code blocks are left alone), `trailingComma: "all"`. +- If you touched only files Prettier skips (e.g., a notebook, or a generated file listed in `.prettierignore`), the check will still pass — no need to run format. +- When in doubt, just run `pnpm run format` — it's fast and idempotent. + +### 2. H1 heading check (`check_h1` job) + +CI runs `node scripts/check-h1-headings.js`. It fails if any `.md`/`.mdx` file contains more than one top-level `# ` heading (code-fenced examples are ignored). Use exactly one H1 per markdown file; deeper sections use `##`, `###`, etc. + +### 3. Build + link/sitemap checks (`build-and-check-links`, `check-sitemap-links` jobs) + +These run `pnpm build` followed by `pnpm link-check` / `pnpm sitemap-check`. The full build is ~10 minutes — don't run it locally for routine edits. Instead, before pushing: + +- Check internal links you added/changed point to real pages or anchors. +- For anchor links (`...#some-id`), make sure the target page defines the anchor explicitly with `[#some-id]` at the end of the heading line. +- If you renamed or moved a page, also update any references and add a redirect in `next.config.mjs` if needed. ## Styling and implementation guidelines diff --git a/.agents/cursor/rules/_general-rules.mdc b/.agents/cursor/rules/_general-rules.mdc index 811148478e..32480eebc3 100644 --- a/.agents/cursor/rules/_general-rules.mdc +++ b/.agents/cursor/rules/_general-rules.mdc @@ -16,3 +16,9 @@ alwaysApply: true ## Frontend - When using tailwindcss, never use explicit colors, always use the default semantic color tokens. + +## Before committing or opening a PR + +- Run `pnpm run format` if you edited any file Prettier formats (`.js`, `.jsx`, `.mjs`, `.cjs`, `.ts`, `.tsx`, `.json`, `.css`, `.scss`, `.md`, `.mdx`, `.yaml`/`.yml`, `.html`). CI runs `pnpm run format:check` and fails on a single unformatted file — this is the format check that most often fails on the first try. +- Files in `.prettierignore` (`content/guides/cookbook/**`, `content/workshop/**`, generated assets in `public/`, lockfiles) are skipped, as are source notebooks (`.ipynb` in `cookbook/`) since Prettier has no parser for them. If you only touched those, no format step is needed. +- Each `.md`/`.mdx` file must have exactly one H1 (`# `) heading; CI's `check_h1` job rejects multiples. diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index acd333d5df..41455de5ed 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -5,6 +5,10 @@ name: "CI" on: pull_request: merge_group: + push: + branches: + - main + workflow_dispatch: jobs: # Add pre-job to skip duplicate actions in merge queues @@ -17,7 +21,8 @@ jobs: - id: skip_check uses: fkirc/skip-duplicate-actions@f75f66ce1886f00957d99748a42c724f4330bdcf # v5.3.1 with: - do_not_skip: '["workflow_dispatch"]' + skip_after_successful_duplicate: "true" + do_not_skip: '["workflow_dispatch", "schedule", "merge_group"]' check_h1: needs: @@ -50,7 +55,7 @@ jobs: with: persist-credentials: false - - uses: pnpm/action-setup@8912a9102ac27614460f54aedde9e1e7f9aec20d # v6.0.5 + - uses: pnpm/action-setup@0e279bb959325dab635dd2c09392533439d90093 # v6.0.8 with: version: 9.5.0 run_install: false @@ -115,7 +120,7 @@ jobs: fetch-depth: 0 persist-credentials: false - - uses: pnpm/action-setup@8912a9102ac27614460f54aedde9e1e7f9aec20d # v6.0.5 + - uses: pnpm/action-setup@0e279bb959325dab635dd2c09392533439d90093 # v6.0.8 id: pnpm-install with: version: 9.5.0 @@ -164,7 +169,7 @@ jobs: fetch-depth: 0 persist-credentials: false - - uses: pnpm/action-setup@8912a9102ac27614460f54aedde9e1e7f9aec20d # v6.0.5 + - uses: pnpm/action-setup@0e279bb959325dab635dd2c09392533439d90093 # v6.0.8 id: pnpm-install with: version: 9.5.0 @@ -221,3 +226,51 @@ jobs: if: ${{ contains(needs.*.result, 'failure') }} run: exit 1 working-directory: . + - name: Notify Slack + if: failure() && github.event_name == 'push' && (github.ref == 'refs/heads/main') + uses: slackapi/slack-github-action@45a88b9581bfab2566dc881e2cd66d334e621e2c # v3.0.3 + with: + webhook: ${{ secrets.SLACK_WEBHOOK_URL }} + webhook-type: incoming-webhook + payload: | + { + "text": "❌ CI failed on ${{ github.ref_name }}", + "blocks": [ + { + "type": "header", + "text": { + "type": "plain_text", + "text": "❌ CI Failed", + "emoji": true + } + }, + { + "type": "section", + "fields": [ + { + "type": "mrkdwn", + "text": "*Branch:*\n`${{ github.ref_name }}`" + }, + { + "type": "mrkdwn", + "text": "*Triggered by:*\n${{ github.actor }}" + } + ] + }, + { + "type": "actions", + "elements": [ + { + "type": "button", + "text": { + "type": "plain_text", + "text": "View Workflow Logs", + "emoji": true + }, + "url": "${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}", + "style": "danger" + } + ] + } + ] + } diff --git a/.github/workflows/nextjs_bundle_analysis.yml b/.github/workflows/nextjs_bundle_analysis.yml index f623dbe52b..a3c7cbde30 100644 --- a/.github/workflows/nextjs_bundle_analysis.yml +++ b/.github/workflows/nextjs_bundle_analysis.yml @@ -28,7 +28,7 @@ jobs: with: persist-credentials: false - - uses: pnpm/action-setup@8912a9102ac27614460f54aedde9e1e7f9aec20d # v6.0.5 + - uses: pnpm/action-setup@0e279bb959325dab635dd2c09392533439d90093 # v6.0.8 id: pnpm-install with: version: 9.5.0 diff --git a/.github/workflows/update-available-internal-links-cursor-rule.yml b/.github/workflows/update-available-internal-links-cursor-rule.yml index 39a3429998..326cbcc868 100644 --- a/.github/workflows/update-available-internal-links-cursor-rule.yml +++ b/.github/workflows/update-available-internal-links-cursor-rule.yml @@ -24,7 +24,7 @@ jobs: node-version: 22 - name: Setup pnpm - uses: pnpm/action-setup@8912a9102ac27614460f54aedde9e1e7f9aec20d # v6.0.5 + uses: pnpm/action-setup@0e279bb959325dab635dd2c09392533439d90093 # v6.0.8 with: version: "9.5.0" diff --git a/.github/workflows/zizmor.yml b/.github/workflows/zizmor.yml index 8e90c5e47d..60e1b2952d 100644 --- a/.github/workflows/zizmor.yml +++ b/.github/workflows/zizmor.yml @@ -26,7 +26,7 @@ jobs: with: persist-credentials: false - name: Run zizmor - uses: zizmorcore/zizmor-action@b1d7e1fb5de872772f31590499237e7cce841e8e # v0.5.3 + uses: zizmorcore/zizmor-action@5f14fd08f7cf1cb1609c1e344975f152c7ee938d # v0.5.6 with: advanced-security: ${{ github.event_name == 'push' && 'true' || 'false' }} min-severity: low diff --git a/.gitignore b/.gitignore index 8b4a3d4f2f..26880001d9 100644 --- a/.gitignore +++ b/.gitignore @@ -14,6 +14,7 @@ public/sitemap*.xml .sitemap-all-pages.json public/llms*.txt public/md-src +content/workshop/ # we use pnpm package-lock.json @@ -112,6 +113,7 @@ fabric.properties .playwright-mcp/ .mcp.json .claude/settings.json +.claude/settings.local.json .claude/launch.json .claude/skills/ .cursor/mcp.json diff --git a/.prettierignore b/.prettierignore index c1e048b337..d08b25c6a2 100644 --- a/.prettierignore +++ b/.prettierignore @@ -7,6 +7,7 @@ out content/guides/cookbook/** content/guides/cookbook/index.mdx content/guides/cookbook/meta.json +content/workshop/** data/generated/contributors.json pnpm-lock.yaml public/md-src diff --git a/README.md b/README.md index fb458a8760..a36ae8fa49 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -![GitHub Banner](https://github.com/langfuse/langfuse-docs/assets/2834609/e403ad73-73fa-43f0-9925-292b05ce58de) +hero-b # Langfuse Docs diff --git a/app/cloud/[[...path]]/page.tsx b/app/cloud/[[...path]]/page.tsx index 4c5ebde5ee..a851fffbe7 100644 --- a/app/cloud/[[...path]]/page.tsx +++ b/app/cloud/[[...path]]/page.tsx @@ -158,6 +158,7 @@ export default function CloudRegionSelectorPage() { handleRegionSelect(regionKey, event)} className="group flex items-center gap-4 px-4 py-4 transition-colors hover:bg-surface-1 sm:px-5" > diff --git a/app/launch-week-5/layout.tsx b/app/launch-week-5/layout.tsx new file mode 100644 index 0000000000..e1d9aa69d0 --- /dev/null +++ b/app/launch-week-5/layout.tsx @@ -0,0 +1,17 @@ +import type { ReactNode } from "react"; +import { HomeLayout } from "@/components/layout"; + +export default function LaunchWeek5Layout({ + children, +}: { + children: ReactNode; +}) { + return ( + + {children} + + ); +} diff --git a/app/launch-week-5/page.tsx b/app/launch-week-5/page.tsx new file mode 100644 index 0000000000..bbf42e95e7 --- /dev/null +++ b/app/launch-week-5/page.tsx @@ -0,0 +1,27 @@ +import type { Metadata } from "next"; +import { LaunchWeek5Landing } from "@/components/launch-week-5/LaunchWeek5Landing"; +import { buildDefaultSiteOgImageUrl } from "@/lib/og-url"; + +const ogImageUrl = buildDefaultSiteOgImageUrl(); + +export const metadata: Metadata = { + title: "Langfuse Launch Week #5", + description: + "Five days, five feature drops, May 25–29, 2026. New building blocks for taking AI from prototype to production — unveiled live at ClickHouse OpenHouse.", + alternates: { + canonical: "https://langfuse.com/launch-week-5", + }, + // Next.js replaces (not deep-merges) openGraph across segments, so the root + // layout's default `images` is dropped unless re-supplied here. + openGraph: { + title: "Langfuse Launch Week #5", + description: + "Five days, five feature drops, May 25–29, 2026. Live demos at ClickHouse OpenHouse.", + url: "https://langfuse.com/launch-week-5", + images: [{ url: ogImageUrl }], + }, +}; + +export default function LaunchWeek5Page() { + return ; +} diff --git a/app/layout.tsx b/app/layout.tsx index 83579849f6..413d34dfa9 100644 --- a/app/layout.tsx +++ b/app/layout.tsx @@ -32,6 +32,7 @@ const f37Analog = localFont({ weight: "500", }); import { Hubspot } from "@/components/analytics/hubspot"; +import { GoogleAds } from "@/components/analytics/google-ads"; import "../style.css"; import "@vidstack/react/player/styles/base.css"; import "../src/overrides.css"; @@ -90,6 +91,7 @@ export default function RootLayout({ {process.env.NODE_ENV === "production" && ( <> + + + ); +} diff --git a/components/home/FAQ.tsx b/components/home/FAQ.tsx index 5ee7ad6d98..c1397b9ae8 100644 --- a/components/home/FAQ.tsx +++ b/components/home/FAQ.tsx @@ -13,7 +13,7 @@ const faqs: FAQItem[] = [ { question: "What does Langfuse help me with?", answer: - "Langfuse helps you [debug LLM applications](/docs/observability/overview) with detailed traces that capture every step of your AI pipeline, including [agent graphs](/docs/observability/features/agent-graphs). You can [manage and version your prompts](/docs/prompt-management/overview) collaboratively, run [automated evaluations](/docs/evaluation/evaluation-methods/llm-as-a-judge) (including LLM-as-a-judge and [code-based evaluators](/docs/evaluation/overview)), track [costs and latency](/docs/observability/features/token-and-cost-tracking) across models and providers, and run [experiments on datasets](/docs/evaluation/experiments/overview) to measure improvements before shipping. It also supports [custom dashboards](/docs/metrics/features/custom-dashboards) for team-wide visibility.", + "Langfuse helps you [debug LLM applications](/docs/observability/overview) with detailed traces that capture every step of your AI pipeline, including [agent graphs](/docs/observability/features/agent-graphs). You can [manage and version your prompts](/docs/prompt-management/overview) collaboratively, run [automated evaluations](/docs/evaluation/evaluation-methods/llm-as-a-judge) (including LLM-as-a-judge and [code evaluators](/docs/evaluation/evaluation-methods/code-evaluators)), track [costs and latency](/docs/observability/features/token-and-cost-tracking) across models and providers, and run [experiments on datasets](/docs/evaluation/experiments/overview) to measure improvements before shipping. It also supports [custom dashboards](/docs/metrics/features/custom-dashboards) for team-wide visibility.", }, { question: "Can I use just tracing without the other features?", diff --git a/components/home/pricing/PricingTable.tsx b/components/home/pricing/PricingTable.tsx index 00161cb6cd..94fb49cbc4 100644 --- a/components/home/pricing/PricingTable.tsx +++ b/components/home/pricing/PricingTable.tsx @@ -32,6 +32,7 @@ import { import { TrustedBy } from "../components/TrustedBy"; import { trustedByData } from "@/data/trusted-by"; import Image from "next/image"; +import { isCloudAppHref } from "@/lib/google-ads"; // Reusable graduated pricing text with calculator link const GraduatedPricingText = () => { @@ -1463,6 +1464,9 @@ export function PricingPlans({ variant }: { variant: DeploymentOption }) { variant={tier.featured ? "primary" : "secondary"} size="default" href={tier.href} + {...(isCloudAppHref(tier.href) + ? { "data-launch-app-cta": "" } + : {})} wrapperClassName="flex-1" className={cn( "justify-center!", @@ -1487,6 +1491,9 @@ export function PricingPlans({ variant }: { variant: DeploymentOption }) { variant={tier.featured ? "primary" : "secondary"} size="default" href={tier.href} + {...(isCloudAppHref(tier.href) + ? { "data-launch-app-cta": "" } + : {})} className={cn( "justify-center!", !tier.featured && diff --git a/components/inkeep/InkeepSearchBar.tsx b/components/inkeep/InkeepSearchBar.tsx index a606d38c2e..846dbda2cb 100644 --- a/components/inkeep/InkeepSearchBar.tsx +++ b/components/inkeep/InkeepSearchBar.tsx @@ -22,6 +22,12 @@ type InkeepSearchButtonProps = { }; export function InkeepSearchButton({ className }: InkeepSearchButtonProps) { + const settings = useInkeepSettings(); + + if (!settings) { + return null; + } + return (