![Langfuse GitHub Banner](https://github.com/langfuse/langfuse/assets/121163007/6035f0f3-d691-4963-b5d0-10cf506e9d42)

First off, thanks for taking the time to contribute! ❤️

Views0
PublishedFeb 1, 2026

Loading actions...

5 minBeginnerpromptSingle file

Skill content

Main instructions and any bundled files for this skill.

markdown

Langfuse GitHub Banner

Contributing to Langfuse

First off, thanks for taking the time to contribute! ❤️

The best ways to contribute to Langfuse:

  • Submit and vote on Ideas
  • Create and comment on Issues
  • Open a PR.

We welcome contributions through GitHub pull requests. This document outlines our conventions regarding development workflow, commit message formatting, contact points, and other resources. Our goal is to simplify the process and ensure that your contributions are easily accepted.

We gratefully welcome improvements to documentation (docs repo), the core application (this repo) and the SDKs (Python, JS).

The maintainers are available on Discord in case you have any questions.

And if you like the project, but just don't have time to contribute code, that's fine. There are other easy ways to support the project and show your appreciation, which we would also be very happy about:

  • Star the project;
  • Tweet about it;
  • Refer to this project in your project's readme;
  • Submit and vote on Ideas;
  • Create and comment on Issues;
  • Mention the project at local meetups and tell your friends/colleagues.

Making a change

Before making any significant changes, please open an issue. Discussing your proposed changes ahead of time will make the contribution process smooth for everyone. Changes that were not discussed in an issue may be rejected.

Once we've discussed your changes and you've got your code ready, make sure that tests are passing and open your pull request.

A good first step is to search for open issues. Issues are labeled, and some good issues to start with are labeled: good first issue.

Project Overview

We recommend checking out DeepWiki to familiarize yourself with the project:

Ask DeepWiki

Technologies we use

  • Application (this repository)
    • NextJS 14, pages router
    • NextAuth.js / Auth.js
    • tRPC: Frontend APIs
    • Prisma ORM
    • Zod v4
    • Tailwind CSS
    • shadcn/ui tailwind components (using Radix and tanstack)
    • Fern: generate OpenAPI spec and Pydantic models
  • JS SDK (langfuse/langfuse-js)
    • openapi-typescript to generated types based on OpenAPI spec
  • Python SDK (langfuse/langfuse-python)
    • Pydantic for input validation, models generated by fern

Architecture Overview

See this diagram for an overview of the architecture.

Network Overview

flowchart TB
    User["UI, API, SDKs"]
    subgraph vpc["VPC"]
        Web["Web Server<br/>(langfuse/langfuse)"]
        Worker["Async Worker<br/>(langfuse/worker)"]
        Postgres["Postgres - OLTP<br/>(Transactional Data)"]
        Cache["Redis/Valkey<br/>(Cache, Queue)"]
        Clickhouse["Clickhouse - OLAP<br/>(Observability Data)"]
        S3["S3 / Blob Storage<br/>(Raw events, multi-modal attachments)"]
    end
    LLM["LLM API/Gateway<br/>(optional)"]

    User --> Web
    Web --> S3
    Web --> Postgres
    Web --> Cache
    Web --> Clickhouse
    Web -.->|"optional for playground"| LLM

    Cache --> Worker
    Worker --> Clickhouse
    Worker --> Postgres
    Worker --> S3
    Worker -.->|"optional for evals"| LLM

Database Overview

The diagram below may not show all relationships if the foreign key is not defined in the database schema. For instance, trace_id in the observation table is not defined as a foreign key to the trace table to allow unordered ingestion of these objects, but it is still a foreign key in the application code.

Full database schema: packages/shared/prisma/schema.prisma

Repository Structure

We built a monorepo using pnpm and turbo to manage the dependencies and build process. The monorepo contains the following packages:

  • web: is the main application package providing Frontend and Backend APIs for Langfuse.
  • worker: contains an application for asynchronous processing of tasks.
  • packages:
    • shared: contains shared code between the above packages.
    • config-eslint: contains eslint configurations which are shared between the above packages.
    • config-typescript: contains typescript configurations which are shared between the above packages.
  • ee: contains all enterprise features. See EE README for more details.

Development Setup

Requirements

  • Node.js 24 as specified in the .nvmrc
  • Pnpm v.11.4.0
  • Docker to run the database locally
  • Clickhouse client

Note: You can also simply run Langfuse in a GitHub Codespace via the provided devcontainer. To do this, click on the green "Code" button in the top right corner of the repository and select "Open with Codespaces".

Codex Cloud Setup

You can also attach this repository to an OpenAI Codex cloud environment. The cloud environment itself is configured in the Codex UI, while the repo-owned bootstrap is versioned in:

  • scripts/codex/setup.sh
  • scripts/codex/maintenance.sh

Recommended Codex UI configuration:

  1. Create a new cloud environment for this repository in the Codex UI.

  2. Choose a base environment with Node.js 24 support.

  3. Set the setup script to:

    bash scripts/codex/setup.sh
    
  4. Set the maintenance script to:

    bash scripts/codex/maintenance.sh
    
  5. Keep internet access disabled by default, or only allow the minimum domains needed for your task.

  6. Add secrets and environment variables in the Codex UI instead of committing them to the repository.

Notes:

  • This Codex setup is intended for repository tasks such as code changes, linting, typechecking, and targeted tests.
  • It does not start the full Langfuse stack. Local development still uses Docker and pnpm run dx / pnpm run dev.
  • Running the full application inside Codex requires external services for PostgreSQL, Redis, ClickHouse, and object storage, plus matching environment variables in the Codex UI.

Shared Agent Setup

This repository keeps the shared agent setup in source control so developers using different tools can work against the same instructions, bootstrap, and MCP server catalog.

  • Canonical shared docs:
    • .agents/AGENTS.md
  • Root discovery symlinks:
    • AGENTS.md
    • CLAUDE.md
  • Shared agent setup overview: .agents/README.md
  • Shared skills: .agents/skills/
  • Shared tool/bootstrap/MCP config: .agents/config.json
  • Tool-specific MCP configs generated locally from that catalog and not committed:
    • .mcp.json
    • .cursor/mcp.json
    • .vscode/mcp.json
    • .codex/config.toml
  • Tool-specific runtime shims generated locally from the shared config and not committed:
    • .claude/settings.json
    • .codex/environments/environment.toml
    • .cursor/environment.json
  • Tool-specific skill projections generated locally and not committed:
    • .claude/skills/*
  • Shared bootstrap for agent environments: bash scripts/codex/setup.sh

When you change the shared MCP setup:

  1. Edit .agents/config.json
  2. Run pnpm run agents:sync
  3. Run pnpm run agents:check
  4. Do not commit the generated MCP config files or runtime shims

Steps

  1. Install development dependencies:

  2. Fork the repository and clone it locally

    git clone https://github.com/langfuse/langfuse.git
    cd langfuse
    
  3. Install dependencies and set up pre-commit hooks

    pnpm install
    pnpm run prepare  # Sets up Husky pre-commit hooks for code formatting
    

    The pre-commit hook runs formatting and lint checks. To skip only the lint check for a commit, set LANGFUSE_PRE_COMMIT_SKIP_LINT, for example:

    LANGFUSE_PRE_COMMIT_SKIP_LINT=1 git commit -m "your commit message"
    

    CI still runs the required checks for pull requests.

  4. Create an env file

     cp .env.dev.example .env
    
  5. Run the entire infrastructure in dev mode. Note: if you have an existing database, this command wipes it. Also, this will fail on the very first run. Please run it again.

    pnpm run dx # first run only (resets db, docker containers, etc...)
    pnpm run dev # any subsequent runs
    

    You will be asked whether you want to reset Postgres and ClickHouse. Confirm both with 'Y' and press enter.

  6. Open the web app in your browser to start using Langfuse:

  7. Log in as a test user:

To get comprehensive example data, you can use the seed command:

pnpm run db:seed:examples

Monorepo quickstart

  • Available packages and their dependencies

    Packages are included in the monorepo according to the pnpm-workspace.yaml file. Each package maintains its own dependencies defined in the package.json. Internal dependencies can be added as well by adding them to the package dependencies: "@langfuse/shared": "workspace:*".

  • Executing commands

    You can run commands in all packages at once. For example, to install all dependencies in all packages, you can execute:

    pnpm install
    pnpm run dev
    pnpm --filter=web run dev # execute command only in one package
    pnpm tc                   # fast typecheck all packages
    pnpm build:check          # Full Next.js build to alternate dir (can run parallel with dev server)
    

    In the root package.json, you can find scripts which are executed with turbo e.g. turbo run dev. These scripts are executed with the help of Turbo. Turbo executes the commands in all packages taking care of the correct order of execution. Task definitions can be found in the turbo.config.js file.

  • Run migrations

    To run migrations, you can execute the following command.

    pnpm run db:migrate -- --name <name of the migration>
    

[!NOTE] If you frequently switch branches, use pnpm run dx instead of pnpm run dev. This command will install dependencies, reset the database (wipe and apply all migrations), and run the database seeder with example data before starting the development server.

[!NOTE] If you find yourself stuck and want to clean the repo, execute pnpm run nuke. It will remove all node_modules and build files.

System behavior

Ingestion API (/public/api/ingestion)

  • the ingestion API takes different event types (creation and updates of traces, generations, spans, events)
  • The API loops through each event and:
    • validates the event
    • stores the event raw in the events table
    • calculates tokens for generations
    • matches models from the models table to model for generations events
    • upserts the event in the traces or observations table
  • returns a 207 HTTP status code with a list of errors if any event failed to be ingested

Commit messages

On the main branch, we adhere to the best practices of conventional commits. All pull requests and branches are squash-merged to maintain a clean and readable history. This approach ensures the addition of a conventional commit message when merging contributions.

Running Unit Tests

All tests run in the CI and must pass before merging. All tests run against a running langfuse instance and write/delete real data from the database.

Test Database Setup

Per default, the tests use the local development database. Therefore, wiping your data in the process. For proper test isolation, create a .env.test file in the root directory:

cp .env.test.example .env.test

Then, a different PostgreSQL and Redis are used for the tests. The .env.test file only overrides the set values and falls back on .env for all undefined values.

  • PostgreSQL: Uses separate langfuse_test database for isolation
  • ClickHouse: Uses shared default database for now
  • Redis: Uses database 1 instead of 0 for isolation (Redis data is not cleaned between tests)

Tests automatically create the PostgreSQL test database if it doesn't exist and clean up data between runs.

Tests in the web package (public API)

We're using Vitest in the web package. There are two types of unit tests:

  • test (server tests)
  • test-client

To run a specific test by name within a file, run:

cd web  # or use `pnpm --filter web test ...` from the repository root
pnpm test prompts.v2.servertest -t "should handle special characters in prompt names"

To run all tests:

pnpm run test

Run interactively in watch mode (not recommended!)

pnpm run test:watch

Tests in the worker package

For the worker package, we're also using Vitest to run unit tests.

pnpm --filter worker run test FILE_YOU_WANT_TO_TEST.ts -t "test name"

CI/CD

We use GitHub Actions for CI/CD, the configuration is in .github/workflows/pipeline.yml

CI on main and pull_request

  • Check Linting
  • E2E test of API using Vitest
  • E2E tests of UI using Playwright

CD on main

  • Publish Docker image to GitHub Packages if CI passes. Done on every push to main branch. Only released versions are tagged with latest.

Staging environment

We run a staging environment at https://staging.langfuse.com that is automatically deployed on every push to main branch.

The same environment is also used for preview deployments of pull requests. Limitations:

  • SSO is not available as dynamic domains are not supported by most SSO providers.
  • When making changes to the database, migrations to the staging database need to be applied manually by a maintainer. If you want to interactively test database changes in the staging environment, please reach out.

You can use the staging environment end-to-end with the Langfuse integrations or SDKs (host: https://staging.langfuse.com). However, please note that the staging environment is not intended for production use and may be reset at any time.

Production environment

We run two separate release/deployment processes:

  1. Langfuse Cloud deployment (frequent):
    • Every commit on the production branch triggers an ECS deployment to Langfuse Cloud.
    • To deploy current main to Langfuse Cloud, promote main to production via promote-main-to-production.yml (instead of force pushing from a local machine).
  2. Open-source release (less frequent):
    • The open-source Docker release process is handled via release.yml for self-hosted production users.

You can trigger the Langfuse Cloud promotion in either way:

  1. Preferred local command:
pnpm run release:cloud

This wraps the GitHub CLI trigger and performs preflight checks (main branch/sync checks and migration diff checks against production) before dispatching the workflow.

  1. GitHub UI: open Actions -> Promote Main to Production -> Run workflow, then set confirm=promote.

Theming

At Langfuse, we utilize CSS variables to manage our theme settings across the platform.

Our approach leverages separate CSS variables for backgrounds (--background) and foregrounds (--foreground), fully adhering to the shadcn/ui color conventions. The background suffix can be omitted if the variable is used for the background color of the component. We recommend using HSL values for these colors to enhance consistency and customization. There is no need to manually handle dark mode styling with "dark:" prefixes, as next-themes automatically manages the theme switching.

Given the following CSS variables:

--primary: 222.2 47.4% 11.2%; // e.g. background-color
--primary-foreground: 210 40% 98%; // e.g. text-color

The background color of the following component will be hsl(var(--primary)) and the foreground color will be hsl(var(--primary-foreground)).

<div class="bg-primary text-primary-foreground">Hello</div>

Color Variables

VariableDescriptionExamples
--backgroundBackground colorDefault background color of body
--foregroundForeground colorDefault text color of body
--mutedMuted background colorTabsList, Skeleton and Switch
--muted-foregroundMuted foreground color
--popoverPopover background colorDropdownMenu, HoverCard, Popover
--popover-foregroundPopover foreground color
--cardCard background colorCard
--card-foregroundCard foreground color
--borderBorder colorDefault border color
--inputInput field border colorInput, Select, Textarea
--primaryPrimary button background colorsButton variant="primary"
--primary-foregroundPrimary button foreground color
--secondarySecondary button background colorButton variant="secondary"
--secondary-foregroundSecondary button foreground color
--accentUsed for accents such as hover effectsDropdownMenuItem, SelectItem
--accent-foregroundUsed for texts on hover effectsDropdownMenuItem, SelectItem
--destructiveDestructive action color for backgroundButton variant="destructive"
--destructive-foregroundDestructive action color for text
--ringFocus ring colorMultiSelect
--primary-accentPrimary accent color used for brandingLayout
--hover-primary-accentPrimary accent color used for hover effects for linksSignIn and AuthCloudRegionSwitch
--muted-greenMuted green for Event labelObservationTree
--muted-magentaMuted magenta for Generation labelObservationTree
--muted-blueMuted blue for Span labelObservationTree
--muted-grayMuted gray for disabled status badgesStatusBadge
--accent-light-greenLight green accent for background of output and assistant messagesIOPreview, Generations, Traces
--accent-dark-greenDark green accent for border of output and assistant messagesCodeJsonViewer and IOPReview
--light-redLight red for error backgroundlevel-color and StatusBadge
--dark-redDark red for error text and error badge dot colorlevel-color and ErrorPage
--light-yellowLight yellow for warning backgroundLevelColor
--dark-yellowDark yellow for warning textLevelColor
--light-greenLight green for success status badge backgroundStatusBadge
--dark-greenDark green for success status badge text and dotStatusBadge
--light-blueLight blue for background of Staging labelLangfuseLogo
--dark-blueDark blue for text and border of Staging labelLangfuseLogo
--accent-light-blueLight blue accent for table link hover effectTableLink
--accent-dark-blueDark blue accent for table link textTableLink
--find-match-selected-backgroundBackground color for selected search matchesCodeMirrorEditor
--find-match-selected-foregroundForeground color for selected search matchesCodeMirrorEditor
--find-match-backgroundBackground color for search matchesCodeMirrorEditor

Adding New Colors

  1. Global Definitions: Add new CSS variable definitions in the global.css file.
  2. Tailwind Configuration: Reflect these new colors in the tailwind.config.js to maintain alignment with Tailwind's utility classes.

By following these guidelines, you can ensure that any contributions to our theme are consistent, maintainable, and aligned with our design system.

Maintainers

Using secrets stored in 1Password

When applying changes to non-local environments, you may need to use secrets stored in 1Password. We use the 1Password CLI for this purpose.

Example:

op run --env-file="./.env" -- pnpm --filter=shared run db:deploy

Editing default models and prices

You can update the default AI models and prices by adding or updating an entry in worker/src/constants/default-model-prices.json.

Please note that

  • prices are in USD
  • the list is ordered by ID, so make sure to keep this order and insert new models at the end of the list
  • the updated_at field must be updated with the current date in ISO 8601 format. Otherwise, the change will be ignored.

Transition period until V3 release

Until the V3 release, both the JSON record must be updated and a migration must be created to continue supporting self-hosted users. Note that the migration must updated both the models as well as the prices table accordingly.

Updating the OpenAPI Specs & fern SDKs

We maintain the API specifications manually to guarantee a high degree of understandability. If you made changes to the API, please update the respective .yml files in fern/apis/....

To export the respective openapi.yml files which power the online API reference, run:

npx fern-api export --api server web/public/generated/api/openapi.yml
npx fern-api export --api client web/public/generated/api-client/openapi.yml
npx fern-api export --api organizations web/public/generated/organizations-api/openapi.yml

To generate the server SDKs, run:

npx fern-api generate --api server

Note: You need a signed in fern account to generate SDKs.

License

Langfuse is MIT licensed, except for ee/ folder. See LICENSE and docs for more details.

When contributing to the Langfuse codebase, you need to agree to the Contributor License Agreement. You only need to do this once and the CLA bot will remind you if you haven't signed it yet.

If the CLA check gets stuck after signing (a known cla-assistant bug), comment /check-cla on your PR to retrigger it.

Prompt Playground

3 Variables

Fill Variables

Preview

![Langfuse GitHub Banner](https://github.com/langfuse/langfuse/assets/121163007/6035f0f3-d691-4963-b5d0-10cf506e9d42)

# Contributing to Langfuse

First off, thanks for taking the time to contribute! ❤️

The best ways to contribute to Langfuse:

- Submit and vote on [Ideas](https://github.com/orgs/langfuse/discussions/categories/ideas)
- Create and comment on [Issues](https://github.com/langfuse/langfuse/issues)
- Open a PR.

We welcome contributions through GitHub pull requests. This document outlines our conventions regarding development workflow, commit message formatting, contact points, and other resources. Our goal is to simplify the process and ensure that your contributions are easily accepted.

We gratefully welcome improvements to documentation ([docs repo](https://github.com/langfuse/langfuse-docs)), the core application (this repo) and the SDKs ([Python](https://github.com/langfuse/langfuse-python), [JS](https://github.com/langfuse/langfuse-js)).

The maintainers are available on [Discord](https://langfuse.com/discord) in case you have any questions.

> And if you like the project, but just don't have time to contribute code, that's fine. There are other easy ways to support the project and show your appreciation, which we would also be very happy about:
>
> - Star the project;
> - Tweet about it;
> - Refer to this project in your project's readme;
> - Submit and vote on [Ideas](https://github.com/orgs/langfuse/discussions/categories/ideas);
> - Create and comment on [Issues](https://github.com/langfuse/langfuse/issues);
> - Mention the project at local meetups and tell your friends/colleagues.

## Making a change

_Before making any significant changes, please [open an issue](https://github.com/langfuse/langfuse/issues)._ Discussing your proposed changes ahead of time will make the contribution process smooth for everyone. Changes that were not discussed in an issue may be rejected.

Once we've discussed your changes and you've got your code ready, make sure that tests are passing and open your pull request.

A good first step is to search for open [issues](https://github.com/langfuse/langfuse/issues). Issues are labeled, and some good issues to start with are labeled: [good first issue](https://github.com/langfuse/langfuse/issues?q=is%3Aissue+is%3Aopen+label%3A%22good+first+issue%22).

## Project Overview

We recommend checking out DeepWiki to familiarize yourself with the project:

[![Ask DeepWiki](https://deepwiki.com/badge.svg)](https://deepwiki.com/langfuse/langfuse)

### Technologies we use

- Application (this repository)
  - NextJS 14, pages router
  - NextAuth.js / Auth.js
  - tRPC: Frontend APIs
  - Prisma ORM
  - Zod v4
  - Tailwind CSS
  - shadcn/ui tailwind components (using Radix and tanstack)
  - Fern: generate OpenAPI spec and Pydantic models
- JS SDK ([langfuse/langfuse-js](https://github.com/langfuse/langfuse-js))
  - openapi-typescript to generated types based on OpenAPI spec
- Python SDK ([langfuse/langfuse-python](https://github.com/langfuse/langfuse-python))
  - Pydantic for input validation, models generated by fern

### Architecture Overview

See this [diagram](https://langfuse.com/self-hosting#architecture) for an overview of the architecture.

### Network Overview

```mermaid
flowchart TB
    User["UI, API, SDKs"]
    subgraph vpc["VPC"]
        Web["Web Server<br/>(langfuse/langfuse)"]
        Worker["Async Worker<br/>(langfuse/worker)"]
        Postgres["Postgres - OLTP<br/>(Transactional Data)"]
        Cache["Redis/Valkey<br/>(Cache, Queue)"]
        Clickhouse["Clickhouse - OLAP<br/>(Observability Data)"]
        S3["S3 / Blob Storage<br/>(Raw events, multi-modal attachments)"]
    end
    LLM["LLM API/Gateway<br/>(optional)"]

    User --> Web
    Web --> S3
    Web --> Postgres
    Web --> Cache
    Web --> Clickhouse
    Web -.->|"optional for playground"| LLM

    Cache --> Worker
    Worker --> Clickhouse
    Worker --> Postgres
    Worker --> S3
    Worker -.->|"optional for evals"| LLM
```

### Database Overview

The diagram below may not show all relationships if the foreign key is not defined in the database schema. For instance, `trace_id` in the `observation` table is not defined as a foreign key to the `trace` table to allow unordered ingestion of these objects, but it is still a foreign key in the application code.

Full database schema: [packages/shared/prisma/schema.prisma](packages/shared/prisma/schema.prisma)

## Repository Structure

We built a monorepo using [pnpm](https://pnpm.io/motivation) and [turbo](https://turbo.build/repo/docs) to manage the dependencies and build process. The monorepo contains the following packages:

- `web`: is the main application package providing Frontend and Backend APIs for Langfuse.
- `worker`: contains an application for asynchronous processing of tasks.
- `packages`:
  - `shared`: contains shared code between the above packages.
  - `config-eslint`: contains eslint configurations which are shared between the above packages.
  - `config-typescript`: contains typescript configurations which are shared between the above packages.
- `ee`: contains all enterprise features. See [EE README](ee/README.md) for more details.

## Development Setup

Requirements

- Node.js 24 as specified in the [.nvmrc](.nvmrc)
- Pnpm v.11.4.0
- Docker to run the database locally
- Clickhouse client

**Note:** You can also simply run Langfuse in a **GitHub Codespace** via the provided devcontainer. To do this, click on the green "Code" button in the top right corner of the repository and select "Open with Codespaces".

### Codex Cloud Setup

You can also attach this repository to an OpenAI Codex cloud environment. The
cloud environment itself is configured in the Codex UI, while the repo-owned
bootstrap is versioned in:

- `scripts/codex/setup.sh`
- `scripts/codex/maintenance.sh`

Recommended Codex UI configuration:

1. Create a new cloud environment for this repository in the Codex UI.
2. Choose a base environment with Node.js 24 support.
3. Set the setup script to:

   ```bash
   bash scripts/codex/setup.sh
   ```

4. Set the maintenance script to:

   ```bash
   bash scripts/codex/maintenance.sh
   ```

5. Keep internet access disabled by default, or only allow the minimum domains
   needed for your task.
6. Add secrets and environment variables in the Codex UI instead of committing
   them to the repository.

Notes:

- This Codex setup is intended for repository tasks such as code changes,
  linting, typechecking, and targeted tests.
- It does **not** start the full Langfuse stack. Local development still uses
  Docker and `pnpm run dx` / `pnpm run dev`.
- Running the full application inside Codex requires external services for
  PostgreSQL, Redis, ClickHouse, and object storage, plus matching environment
  variables in the Codex UI.

### Shared Agent Setup

This repository keeps the shared agent setup in source control so developers
using different tools can work against the same instructions, bootstrap, and
MCP server catalog.

- Canonical shared docs:
  - `.agents/AGENTS.md`
- Root discovery symlinks:
  - `AGENTS.md`
  - `CLAUDE.md`
- Shared agent setup overview: `.agents/README.md`
- Shared skills: `.agents/skills/`
- Shared tool/bootstrap/MCP config: `.agents/config.json`
- Tool-specific MCP configs generated locally from that catalog and not committed:
  - `.mcp.json`
  - `.cursor/mcp.json`
  - `.vscode/mcp.json`
  - `.codex/config.toml`
- Tool-specific runtime shims generated locally from the shared config and not committed:
  - `.claude/settings.json`
  - `.codex/environments/environment.toml`
  - `.cursor/environment.json`
- Tool-specific skill projections generated locally and not committed:
  - `.claude/skills/*`
- Shared bootstrap for agent environments: `bash scripts/codex/setup.sh`

When you change the shared MCP setup:

1. Edit `.agents/config.json`
2. Run `pnpm run agents:sync`
3. Run `pnpm run agents:check`
4. Do not commit the generated MCP config files or runtime shims

**Steps**

1. Install development dependencies:
   - [golang-migrate](https://github.com/golang-migrate/migrate/tree/master/cmd/migrate#migrate-cli) as CLI
   - [clickhouse binary](https://clickhouse.com/docs/install) on macOS with brew: `curl https://clickhouse.com/ | sh`

2. Fork the repository and clone it locally

   ```bash
   git clone https://github.com/langfuse/langfuse.git
   cd langfuse
   ```

3. Install dependencies and set up pre-commit hooks

   ```bash
   pnpm install
   pnpm run prepare  # Sets up Husky pre-commit hooks for code formatting
   ```

   The pre-commit hook runs formatting and lint checks. To skip only the lint
   check for a commit, set `LANGFUSE_PRE_COMMIT_SKIP_LINT`, for example:

   ```bash
   LANGFUSE_PRE_COMMIT_SKIP_LINT=1 git commit -m "your commit message"
   ```

   CI still runs the required checks for pull requests.

4. Create an env file

   ```bash
    cp .env.dev.example .env
   ```

5. Run the entire infrastructure in dev mode. **Note**: if you have an existing database, this command wipes it. Also, this will fail on the very first run. Please run it again.

   ```bash
   pnpm run dx # first run only (resets db, docker containers, etc...)
   pnpm run dev # any subsequent runs
   ```

   You will be asked whether you want to reset Postgres and ClickHouse. Confirm both with 'Y' and press enter.

6. Open the web app in your browser to start using Langfuse:
   - [Sign up page, http://localhost:3000](http://localhost:3000)
   - [Demo project, http://localhost:3000/project/7a88fb47-b4e2-43b8-a06c-a5ce950dc53a](http://localhost:3000/project/7a88fb47-b4e2-43b8-a06c-a5ce950dc53a)

7. Log in as a test user:
   - Username: `[email protected]`
   - Password: `password`

To get comprehensive example data, you can use the `seed` command:

```sh
pnpm run db:seed:examples
```

## Monorepo quickstart

- Available packages and their dependencies

  Packages are included in the monorepo according to the `pnpm-workspace.yaml` file. Each package maintains its own dependencies defined in the `package.json`. Internal dependencies can be added as well by adding them to the package dependencies: `"@langfuse/shared": "workspace:*"`.

- Executing commands

  You can run commands in all packages at once. For example, to install all dependencies in all packages, you can execute:

  ```bash
  pnpm install
  pnpm run dev
  pnpm --filter=web run dev # execute command only in one package
  pnpm tc                   # fast typecheck all packages
  pnpm build:check          # Full Next.js build to alternate dir (can run parallel with dev server)
  ```

  In the root `package.json`, you can find scripts which are executed with turbo e.g. `turbo run dev`. These scripts are executed with the help of Turbo. Turbo executes the commands in all packages taking care of the correct order of execution. Task definitions can be found in the `turbo.config.js` file.

- Run migrations

  To run migrations, you can execute the following command.

  ```bash
  pnpm run db:migrate -- --name <name of the migration>
  ```

> [!NOTE]
> If you frequently switch branches, use `pnpm run dx` instead of `pnpm run dev`. This command will install dependencies, reset the database (wipe and apply all migrations), and run the database seeder with example data before starting the development server.

> [!NOTE]
> If you find yourself stuck and want to clean the repo, execute `pnpm run nuke`. It will remove all node_modules and build files.

## System behavior

### Ingestion API `(/public/api/ingestion)`

- the ingestion API takes different event types (creation and updates of traces, generations, spans, events)
- The API loops through each event and:
  - validates the event
  - stores the event raw in the events table
  - calculates tokens for `generations`
  - matches models from the `models` table to model for `generations` events
  - upserts the event in the `traces` or `observations` table
- returns a `207` HTTP status code with a list of errors if any event failed to be ingested

## Commit messages

On the main branch, we adhere to the best practices of [conventional commits](https://www.conventionalcommits.org/en/v1.0.0/). All pull requests and branches are squash-merged to maintain a clean and readable history. This approach ensures the addition of a conventional commit message when merging contributions.

## Running Unit Tests

All tests run in the CI and must pass before merging.
All tests run against a running langfuse instance and **write/delete real data from the database**.

### Test Database Setup

Per default, the tests use the local development database. Therefore, wiping your data in the process.
For proper test isolation, create a `.env.test` file in the root directory:

```bash
cp .env.test.example .env.test
```

Then, a different PostgreSQL and Redis are used for the tests.
The `.env.test` file only overrides the set values and falls back on `.env` for all undefined values.

- **PostgreSQL**: Uses separate `langfuse_test` database for isolation
- **ClickHouse**: Uses shared `default` database for now
- **Redis**: Uses database 1 instead of 0 for isolation (Redis data is not cleaned between tests)

Tests automatically create the PostgreSQL test database if it doesn't exist and clean up data between runs.

### Tests in the `web` package (public API)

We're using Vitest in the `web` package. There are two types of unit tests:

- `test` (server tests)
- `test-client`

To run a specific test by name within a file, run:

```sh
cd web  # or use `pnpm --filter web test ...` from the repository root
pnpm test prompts.v2.servertest -t "should handle special characters in prompt names"
```

To run all tests:

```sh
pnpm run test
```

Run interactively in watch mode (not recommended!)

```sh
pnpm run test:watch
```

### Tests in the `worker` package

For the `worker` package, we're also using Vitest to run unit tests.

```sh
pnpm --filter worker run test FILE_YOU_WANT_TO_TEST.ts -t "test name"
```

## CI/CD

We use GitHub Actions for CI/CD, the configuration is in [`.github/workflows/pipeline.yml`](.github/workflows/pipeline.yml)

CI on `main` and `pull_request`

- Check Linting
- E2E test of API using Vitest
- E2E tests of UI using Playwright

CD on `main`

- Publish Docker image to GitHub Packages if CI passes. Done on every push to `main` branch. Only released versions are tagged with `latest`.

## Staging environment

We run a staging environment at [https://staging.langfuse.com](https://staging.langfuse.com) that is automatically deployed on every push to `main` branch.

The same environment is also used for preview deployments of pull requests. Limitations:

- SSO is not available as dynamic domains are not supported by most SSO providers.
- When making changes to the database, migrations to the staging database need to be applied manually by a maintainer. If you want to interactively test database changes in the staging environment, please reach out.

You can use the staging environment end-to-end with the Langfuse integrations or SDKs (host: `https://staging.langfuse.com`). However, please note that the staging environment is not intended for production use and may be reset at any time.

## Production environment

We run two separate release/deployment processes:

1. **Langfuse Cloud deployment (frequent):**
   - Every commit on the `production` branch triggers an ECS deployment to Langfuse Cloud.
   - To deploy current `main` to Langfuse Cloud, promote `main` to `production` via [`promote-main-to-production.yml`](.github/workflows/promote-main-to-production.yml) (instead of force pushing from a local machine).
2. **Open-source release (less frequent):**
   - The open-source Docker release process is handled via [`release.yml`](.github/workflows/release.yml) for self-hosted production users.

You can trigger the Langfuse Cloud promotion in either way:

1. Preferred local command:

```bash
pnpm run release:cloud
```

This wraps the GitHub CLI trigger and performs preflight checks (main branch/sync checks and migration diff checks against `production`) before dispatching the workflow.

2. GitHub UI: open **Actions** -> **Promote Main to Production** -> **Run workflow**, then set `confirm=promote`.

## Theming

At Langfuse, we utilize CSS variables to manage our theme settings across the platform.

Our approach leverages separate CSS variables for backgrounds (--background) and foregrounds (--foreground), fully adhering to the [shadcn/ui](https://ui.shadcn.com/docs/theming) color conventions. The background suffix can be omitted if the variable is used for the background color of the component. We recommend using HSL values for these colors to enhance consistency and customization. There is no need to manually handle dark mode styling with "dark:" prefixes, as next-themes automatically manages the theme switching.

Given the following CSS variables:

```
--primary: 222.2 47.4% 11.2%; // e.g. background-color
--primary-foreground: 210 40% 98%; // e.g. text-color
```

The background color of the following component will be `hsl(var(--primary))` and the foreground color will be `hsl(var(--primary-foreground))`.

```
<div class="bg-primary text-primary-foreground">Hello</div>
```

### Color Variables

| Variable                         | Description                                                        | Examples                         |
| -------------------------------- | ------------------------------------------------------------------ | -------------------------------- |
| --background                     | Background color                                                   | Default background color of body |
| --foreground                     | Foreground color                                                   | Default text color of body       |
| --muted                          | Muted background color                                             | TabsList, Skeleton and Switch    |
| --muted-foreground               | Muted foreground color                                             |                                  |
| --popover                        | Popover background color                                           | DropdownMenu, HoverCard, Popover |
| --popover-foreground             | Popover foreground color                                           |                                  |
| --card                           | Card background color                                              | Card                             |
| --card-foreground                | Card foreground color                                              |                                  |
| --border                         | Border color                                                       | Default border color             |
| --input                          | Input field border color                                           | Input, Select, Textarea          |
| --primary                        | Primary button background colors                                   | Button variant="primary"         |
| --primary-foreground             | Primary button foreground color                                    |                                  |
| --secondary                      | Secondary button background color                                  | Button variant="secondary"       |
| --secondary-foreground           | Secondary button foreground color                                  |                                  |
| --accent                         | Used for accents such as hover effects                             | DropdownMenuItem, SelectItem     |
| --accent-foreground              | Used for texts on hover effects                                    | DropdownMenuItem, SelectItem     |
| --destructive                    | Destructive action color for background                            | Button variant="destructive"     |
| --destructive-foreground         | Destructive action color for text                                  |                                  |
| --ring                           | Focus ring color                                                   | MultiSelect                      |
| --primary-accent                 | Primary accent color used for branding                             | Layout                           |
| --hover-primary-accent           | Primary accent color used for hover effects for links              | SignIn and AuthCloudRegionSwitch |
| --muted-green                    | Muted green for Event label                                        | ObservationTree                  |
| --muted-magenta                  | Muted magenta for Generation label                                 | ObservationTree                  |
| --muted-blue                     | Muted blue for Span label                                          | ObservationTree                  |
| --muted-gray                     | Muted gray for disabled status badges                              | StatusBadge                      |
| --accent-light-green             | Light green accent for background of output and assistant messages | IOPreview, Generations, Traces   |
| --accent-dark-green              | Dark green accent for border of output and assistant messages      | CodeJsonViewer and IOPReview     |
| --light-red                      | Light red for error background                                     | level-color and StatusBadge      |
| --dark-red                       | Dark red for error text and error badge dot color                  | level-color and ErrorPage        |
| --light-yellow                   | Light yellow for warning background                                | LevelColor                       |
| --dark-yellow                    | Dark yellow for warning text                                       | LevelColor                       |
| --light-green                    | Light green for success status badge background                    | StatusBadge                      |
| --dark-green                     | Dark green for success status badge text and dot                   | StatusBadge                      |
| --light-blue                     | Light blue for background of Staging label                         | LangfuseLogo                     |
| --dark-blue                      | Dark blue for text and border of Staging label                     | LangfuseLogo                     |
| --accent-light-blue              | Light blue accent for table link hover effect                      | TableLink                        |
| --accent-dark-blue               | Dark blue accent for table link text                               | TableLink                        |
| --find-match-selected-background | Background color for selected search matches                       | CodeMirrorEditor                 |
| --find-match-selected-foreground | Foreground color for selected search matches                       | CodeMirrorEditor                 |
| --find-match-background          | Background color for search matches                                | CodeMirrorEditor                 |

### Adding New Colors

1. Global Definitions: Add new CSS variable definitions in the global.css file.
2. Tailwind Configuration: Reflect these new colors in the tailwind.config.js to maintain alignment with Tailwind's utility classes.

By following these guidelines, you can ensure that any contributions to our theme are consistent, maintainable, and aligned with our design system.

## Maintainers

### Using secrets stored in 1Password

When applying changes to non-local environments, you may need to use secrets stored in 1Password. We use the 1Password CLI for this purpose.

Example:

```bash
op run --env-file="./.env" -- pnpm --filter=shared run db:deploy
```

### Editing default models and prices

You can update the default AI models and prices by adding or updating an entry in `worker/src/constants/default-model-prices.json`.

Please note that

- prices are in USD
- the list is ordered by ID, so make sure to keep this order and insert new models at the end of the list
- the `updated_at` field must be updated with the current date in ISO 8601 format. Otherwise, the change will be ignored.

### Transition period until V3 release

Until the V3 release, both the JSON record must be updated **and** a migration must be created to continue supporting self-hosted users. Note that the migration must updated both the `models` as well as the `prices` table accordingly.

## Updating the OpenAPI Specs & fern SDKs

We maintain the API specifications manually to guarantee a high degree of understandability. If you made changes to the API, please update the respective `.yml` files in `fern/apis/...`.

To export the respective `openapi.yml` files which power the online API reference, run:

```sh
npx fern-api export --api server web/public/generated/api/openapi.yml
npx fern-api export --api client web/public/generated/api-client/openapi.yml
npx fern-api export --api organizations web/public/generated/organizations-api/openapi.yml
```

To generate the server SDKs, run:

```sh
npx fern-api generate --api server
```

**Note:** You need a signed in fern account to generate SDKs.

## License

Langfuse is MIT licensed, except for `ee/` folder. See [LICENSE](LICENSE) and [docs](https://langfuse.com/docs/open-source) for more details.

When contributing to the Langfuse codebase, you need to agree to the [Contributor License Agreement](https://cla-assistant.io/langfuse/langfuse). You only need to do this once and the CLA bot will remind you if you haven't signed it yet.

If the CLA check gets stuck after signing (a [known cla-assistant bug](https://github.com/cla-assistant/cla-assistant/issues/520)), comment `/check-cla` on your PR to retrigger it.
Share: