<h1 align="center">
<a href="https://prompts.chat">
First off, thanks for taking the time to contribute! ❤️
Sign in to like and favorite skills
First off, thanks for taking the time to contribute! ❤️
The best ways to contribute to Langfuse:
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:
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.
We recommend checking out DeepWiki to familiarize yourself with the project:
See this diagram for an overview of the architecture.
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
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
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.Requirements
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".
Steps
Install development dependencies:
brew install --cask clickhouseFork the repository and clone it locally
git clone https://github.com/langfuse/langfuse.git cd langfuse
Install dependencies and set up pre-commit hooks
pnpm install pnpm run prepare # Sets up Husky pre-commit hooks for code formatting
Create an env file
cp .env.dev.example .env
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.
Open the web app in your browser to start using Langfuse:
Log in as a test user:
[email protected]passwordTo get comprehensive example data, you can use the
seed command:
pnpm run db:seed:examples
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
instead ofpnpm run dx. 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.pnpm run dev
[!NOTE] If you find yourself stuck and want to clean the repo, execute
. It will remove all node_modules and build files.pnpm run nuke
(/public/api/ingestion)generationsmodels table to model for generations eventstraces or observations table207 HTTP status code with a list of errors if any event failed to be ingestedOn 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.
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.
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.
langfuse_test database for isolationdefault database for nowTests automatically create the PostgreSQL test database if it doesn't exist and clean up data between runs.
web package (public API)We're using Jest with in the
web package. Therefore, if you want to provide an argument to the test runner, do it directly without an intermittent --.
There are three types of unit tests:
test-synctest (for async folder tests)test-clientTo run a specific test, for example the test:
"should handle special characters in prompt names" in prompts.v2.servertest.ts, run:
cd web # or with --filter=web pnpm test-sync --testPathPatterns="prompts\.v2\.servertest" --testNamePattern="should handle special characters in prompt names" # for async folder tests: pnpm test -- --testPathPatterns="observations-api" --testNamePattern="should fetch all observations"
To run all tests:
pnpm run test
Run interactively in watch mode (not recommended!)
pnpm run test:watch
worker packageFor the
worker package, we're using vitest to run unit tests.
pnpm run test --filter=worker -- FILE_YOU_WANT_TO_TEST.ts -t "test name"
We use GitHub Actions for CI/CD, the configuration is in .github/workflows/pipeline.yml
CI on
main and pull_request
CD on
main
main branch. Only released versions are tagged with latest.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:
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.
When a new release is tagged on the
main branch (excluding prereleases), it triggers a production deployment. The deployment process consists of two steps:
latest tag.main branch to the production branch during every release, using the release.yml GitHub Action.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>
| 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 |
By following these guidelines, you can ensure that any contributions to our theme are consistent, maintainable, and aligned with our design system.
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
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
updated_at field must be updated with the current date in ISO 8601 format. Otherwise, the change will be ignored.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.
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 generate the respective
openapi.yml files which power the online API reference & SDKs, run:
npx fern-api generate --api server # for the server API npx fern-api generate --api client # for the client API npx fern-api generate --api organizations # for the organizations API
Note: You need a signed in fern account to run those commands.
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.