8000 GitHub - lmnr-ai/lmnr: Laminar - open-source observability platform purpose-built for AI agents. YC S24. · GitHub
[go: up one dir, main page]

Skip to content
lmnr-aiPublic

Latest commit

 

History

2,145 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Static Badge X (formerly Twitter) Follow LinkedIn Static Badge

Laminar banner

Laminar

Laminar is an open-source observability platform purpose-built for AI agents.

  • Tracing. Docs
    • OpenTelemetry-native powerful tracing SDK - 1 line of code to automatically trace Vercel AI SDK, Browser Use, Stagehand, LangChain, OpenAI, Anthropic, Gemini, and more.
  • Signals. Docs
    • Describe any behavior of your agent that you want to track in plain English (e.g. "agent is stuck in a loop")
    • Laminar reads every agent run and pings you in Slack when it happens.
  • Evals. Docs
    • Unopinionated, extensible SDK and CLI for running evals locally or in CI/CD pipeline.
    • UI for visualizing evals and comparing results.
  • MCP / CLI access for your coding agent
    • Query traces, spans, metrics, and events with SQL
    • Let your coding agent investigate and debug issues based on your traces
  • Dashboards. Docs
    • Powerful dashboard builder for traces, metrics, and events with support of custom SQL queries.
  • Data annotation & Datasets. Docs
    • Custom data rendering UI for fast data annotation and dataset creation for evals.
  • Extremely high performance.
    • Written in Rust 🦀
    • 20x trace compression for efficient ingestion and storage. Read more about it here.
    • Custom realtime engine for viewing traces as they happen.
    • Ultra-fast full-text search over span data.
    • gRPC exporter for tracing data.

Traces

Documentation

Check out the full documentation here laminar.sh/docs.

Getting started

The fastest and easiest way to get started is with our managed platform -> laminar.sh

Self-hosting with Docker compose

Laminar is very easy to self-host locally. For a quick start, clone the repo and start the services with docker compose:

git clone https://github.com/lmnr-ai/lmnr
cd lmnr
docker compose up -d

This will spin up a lightweight but full-featured version of the stack. This is good for a quickstart or for lightweight usage. You can access the UI at http://localhost:5667 in your browser.

You will also need to properly configure the SDK, with baseUrl and correct ports. See guide on self-hosting.

For production environment, we recommend using our managed platform or docker compose -f docker-compose-full.yml up -d.

Upgrading existing self-hosted installations

The ClickHouse configuration disables internal telemetry logs that are not useful for a single-node installation and retains query and error logs for three days. After pulling this change, recreate the ClickHouse container to apply the configuration. The examples below use docker-compose.yml; pass -f with the Compose file used by your deployment when it differs:

docker compose up -d --force-recreate clickhouse

Disabling a system log stops new writes but does not clear an existing table. To reclaim its disk space while preserving the table structure, truncate the disabled log tables after ClickHouse restarts:

docker compose exec -T clickhouse clickhouse-client --multiquery <<'SQL'
TRUNCATE TABLE IF EXISTS system.trace_log;
TRUNCATE TABLE IF EXISTS system.text_log;
TRUNCATE TABLE IF EXISTS system.part_log;
TRUNCATE TABLE IF EXISTS system.metric_log;
TRUNCATE TABLE IF EXISTS system.asynchronous_metric_log;
TRUNCATE TABLE IF EXISTS system.background_schedule_pool_log;
TRUNCATE TABLE IF EXISTS system.query_metric_log;
TRUNCATE TABLE IF EXISTS system.query_thread_log;
TRUNCATE TABLE IF EXISTS system.query_views_log;
TRUNCATE TABLE IF EXISTS system.session_log;
TRUNCATE TABLE IF EXISTS system.crash_log;
TRUNCATE TABLE IF EXISTS system.opentelemetry_span_log;
TRUNCATE TABLE IF EXISTS system.zookeeper_log;
TRUNCATE TABLE IF EXISTS system.blob_storage_log;
TRUNCATE TABLE IF EXISTS system.processors_profile_log;
TRUNCATE TABLE IF EXISTS system.asynchronous_insert_log;
SQL

When ClickHouse applies the new TTL to an existing query_log or error_log, it may rename the previous table with a numeric suffix such as query_log_0. Inspect any suffixed tables before dropping them if you also want to reclaim their historical data.

Configuring LLM provider (optional)

Frontend AI features (chat-with-trace, SQL-with-AI) and server-side AI workers require an LLM provider. Configure one in your .env file at the repo root.

Pick one of the following provider setups. LLM_MODEL_SMALL|MEDIUM|LARGE are optional — per-provider defaults apply when unset. LLM_DEFAULT_HEADERS_JSON is optional for any provider or gateway that requires static headers.

# Optional for any provider/gateway that requires static headers
# LLM_DEFAULT_HEADERS_JSON='{"X-Gateway-Tenant":"tenant"}'

# Option A: Gemini
LLM_PROVIDER=gemini
LLM_API_KEY=your_gemini_key

# Option B: OpenAI (or any OpenAI-compatible gateway such as LiteLLM, OpenRouter, vLLM)
LLM_PROVIDER=openai
# LLM_BASE_URL=http://localhost:4000   # optional, for OpenAI-compatible gateways
LLM_API_KEY=your_openai_key

# Option C: AWS Bedrock (Anthropic Claude). Uses AWS credentials instead of LLM_API_KEY.
LLM_PROVIDER=bedrock
AWS_ACCESS_KEY_ID=...
AWS_SECRET_ACCESS_KEY=...
AWS_REGION=us-east-1

# Option D: Azure AI Foundry. One resource serves three API shapes; pick the one
# matching your deployment. Model ids are deployment names, so set LLM_MODEL_*
# unless your deployments are named after the models.
#   azure_chat_completions -> <resource>.services.ai.azure.com/openai/v1/chat/completions
#   azure_responses        -> <resource>.services.ai.azure.com/openai/v1/responses
#   azure_anthropic        -> <resource>.services.ai.azure.com/anthropic/v1/messages (Claude)
LLM_PROVIDER=azure_chat_completions
LLM_API_KEY=your_azure_key
AZURE_RESOURCE_ID=your_resource_name   # or AZURE_BASE_URL for private endpoints
# AZURE_API_VERSION=preview            # optional; only for resources that require it

Custom Postgres schema (optional)

By default Laminar uses the public schema. To target a different schema (e.g. when deploying alongside other services in a shared Postgres instance), set the same value for both the frontend and the app-server:

POSTGRES_SCHEMA=laminar
# Set to false if the schema is pre-provisioned or the DB role lacks CREATE.
# POSTGRES_CREATE_SCHEMA=true

The schema is applied as the connection search_path, so all tables, foreign keys, and migrations target it. When a non-public schema is set, the frontend also tracks migrations inside that schema (<schema>.__drizzle_migrations) rather than the shared drizzle schema. Note that running Laminar alongside another Drizzle-managed service in the same database may still require manual intervention, since Drizzle's migration journal is versioned per-schema.

SQL access to Postgres entities (optional)

Some SQL tables are read live from Postgres: signals, evaluations and datasets, plus the signal, evaluation and dataset names on signal_events, evaluation_datapoints and dataset_datapoints. ClickHouse reads them through a dedicated read-only role, which is off until you set both of these in your .env:

CLICKHOUSE_POSTGRES_USER=clickhouse_ro
CLICKHOUSE_POSTGRES_PASSWORD=<choose a strong password>

On boot the frontend creates that role if absent and grants SELECT on only the columns it needs. Use a dedicated role: the frontend refuses to use the app's own Postgres user or a privileged role. Without them, those tables and columns return an error. If Postgres is not the compose postgres service, also set CLICKHOUSE_POSTGRES_HOST / CLICKHOUSE_POSTGRES_PORT as the ClickHouse server reaches it.

Externally managed Postgres role

The frontend checks whether CLICKHOUSE_POSTGRES_USER already exists. For an existing dedicated role, it skips role creation, password changes, role settings, and database CONNECT grants. It still applies schema USAGE, column-level SELECT grants, and SELECT RLS policies after migrations, and configures ClickHouse's pg database with the supplied credentials. No additional environment variable is needed. If the role is absent, the frontend provisions it as before.

To manage the login through Terraform or a database administrator, provision it before starting the frontend, with LOGIN, CONNECT on the target database, statement_timeout (normally 15s), and CONNECTION LIMIT (normally 10). The frontend does not apply CLICKHOUSE_POSTGRES_STATEMENT_TIMEOUT or CLICKHOUSE_POSTGRES_CONNECTION_LIMIT to existing roles. Do not reuse the app's login or a role with SUPERUSER, BYPASSRLS, or CREATEROLE. The frontend's migration login must own, or inherit ownership of, the schema and exposed tables to apply the schema/table grants and RLS policies.

Verify the credentials, required reads, and connectivity from the ClickHouse server before enabling the feature, and test a ClickHouse restart in staging. The PostgreSQL database engine remains a startup dependency of ClickHouse. After rotating the external password, update CLICKHOUSE_POSTGRES_PASSWORD and restart the frontend so it recreates pg with the new credentials. For existing roles, changing this variable does not change the password in Postgres; rotate it through the system that owns the login. Unset credentials still drop pg.

Anonymous usage telemetry

Self-hosted deployments collect anonymized usage telemetry. To opt out, set LAMINAR_TELEMETRY_DISABLED=true in your .env.

For shipped Docker Compose stacks, set the variable in the repository-root .env and recreate the frontend container. For a manually run frontend or Helm deployment, set it in the frontend process or pod environment.

Contributing

For running and building Laminar locally, or to learn more about docker compose files, follow the guide in Contributing.

TS quickstart

First, create a project and generate a project API key. Then,

npm add @lmnr-ai/lmnr

It will install Laminar TS SDK and all instrumentation packages (OpenAI, Anthropic, LangChain ...)

To start tracing LLM calls just add

import { Laminar } from '@lmnr-ai/lmnr';
Laminar.initialize({ projectApiKey: process.env.LMNR_PROJECT_API_KEY });

To trace inputs / outputs of functions use observe wrapper.

import { OpenAI } from 'openai';
import { observe } from '@lmnr-ai/lmnr';

const client = new OpenAI({ apiKey: process.env.OPENAI_API_KEY });

const poemWriter = observe({name: 'poemWriter'}, async (topic) => {
  const response = await client.chat.completions.create({
    model: "gpt-4o-mini",
    messages: [{ role: "user", content: `write a poem about ${topic}` }],
  });
  return response.choices[0].message.content;
});

await poemWriter();

Python quickstart

First, create a project and generate a project API key. Then,

pip install --upgrade 'lmnr[all]'

It will install Laminar Python SDK and all instrumentation packages. See list of all instruments here

To start tracing LLM calls just add

from lmnr import Laminar
Laminar.initialize(project_api_key="<LMNR_PROJECT_API_KEY>")

To trace inputs / outputs of functions use @observe() decorator.

import os
from openai import OpenAI

from lmnr import observe, Laminar
Laminar.initialize(project_api_key="<LMNR_PROJECT_API_KEY>")

client = OpenAI(api_key=os.environ["OPENAI_API_KEY"])

@observe()  # annotate all functions you want to trace
def poem_writer(topic):
    response = client.chat.completions.create(
        model="gpt-4o",
        messages=[
            {"role": "user", "content": f"write a poem about {topic}"},
        ],
    )
    poem = response.choices[0].message.content
    return poem

if __name__ == "__main__":
    print(poem_writer(topic="laminar flow"))

Client libraries

To learn more about instrumenting your code, check out our client libraries:

NPM Version PyPI - Version

Releases

Packages

Used by

Contributors

Languages

0