+
+
diff --git a/api-reference/openapi.json b/api-reference/openapi.json
index a96593c..80fe2de 100644
--- a/api-reference/openapi.json
+++ b/api-reference/openapi.json
@@ -22,7 +22,7 @@
"/v1/queues/push": {
"post": {
"tags": [
- "api::queues"
+ "Queues"
],
"operationId": "push_to_queue",
"requestBody": {
@@ -75,7 +75,7 @@
"/v1/evals": {
"post": {
"tags": [
- "api::evaluations"
+ "Evals"
],
"operationId": "init_eval",
"requestBody": {
@@ -127,7 +127,7 @@
"/v1/evals/{eval_id}/datapoints": {
"post": {
"tags": [
- "api::evaluations"
+ "Evals"
],
"operationId": "save_eval_datapoints",
"parameters": [
@@ -204,7 +204,7 @@
"/v1/evals/{eval_id}/datapoints/{datapoint_id}": {
"post": {
"tags": [
- "api::evaluations"
+ "Evals"
],
"operationId": "update_eval_datapoint",
"parameters": [
@@ -291,7 +291,7 @@
"/v1/evaluators/score": {
"post": {
"tags": [
- "api::evaluators"
+ "Evaluators"
],
"operationId": "create_evaluator_score",
"requestBody": {
@@ -339,7 +339,7 @@
"/v1/sql/query": {
"post": {
"tags": [
- "api::sql"
+ "SQL"
],
"operationId": "sql_query",
"requestBody": {
diff --git a/api-reference/payloads/get_payload.mdx b/api-reference/payloads/get_payload.mdx
new file mode 100644
index 0000000..e304054
--- /dev/null
+++ b/api-reference/payloads/get_payload.mdx
@@ -0,0 +1,9 @@
+---
+title: "Get payload"
+method: GET
+path: /v1/payloads/{payloadId}
+openapi: "GET /v1/payloads/{payloadId}"
+description: Stream a stored payload (image or raw).
+---
+
+Retrieve a stored payload. Use `payloadType=image|raw` query param to control content type.
diff --git a/api-reference/projects/eval_score_distribution.mdx b/api-reference/projects/eval_score_distribution.mdx
new file mode 100644
index 0000000..e462b21
--- /dev/null
+++ b/api-reference/projects/eval_score_distribution.mdx
@@ -0,0 +1,9 @@
+---
+title: "Evaluation score distribution"
+method: GET
+path: /api/v1/projects/{project_id}/evaluation-score-distribution
+openapi: "GET /api/v1/projects/{project_id}/evaluation-score-distribution"
+description: Score distribution across buckets.
+---
+
+Get score distribution for one or more evaluations. Query params: `evaluationIds` (comma-separated UUIDs), `scoreName`.
diff --git a/api-reference/projects/eval_score_stats.mdx b/api-reference/projects/eval_score_stats.mdx
new file mode 100644
index 0000000..85dc0a5
--- /dev/null
+++ b/api-reference/projects/eval_score_stats.mdx
@@ -0,0 +1,9 @@
+---
+title: "Evaluation score stats"
+method: GET
+path: /api/v1/projects/{project_id}/evaluation-score-stats
+openapi: "GET /api/v1/projects/{project_id}/evaluation-score-stats"
+description: Average score for an evaluation.
+---
+
+Get average score value for an evaluation. Query params: `evaluationId`, `scoreName`.
diff --git a/api-reference/projects/realtime.mdx b/api-reference/projects/realtime.mdx
new file mode 100644
index 0000000..eed4b4b
--- /dev/null
+++ b/api-reference/projects/realtime.mdx
@@ -0,0 +1,9 @@
+---
+title: "Realtime SSE"
+method: GET
+path: /api/v1/projects/{project_id}/realtime
+openapi: "GET /api/v1/projects/{project_id}/realtime"
+description: Open a project SSE stream.
+---
+
+Open a server-sent events stream. Query param `key=traces|trace_{traceId}` selects the stream.
diff --git a/api-reference/projects/spans.mdx b/api-reference/projects/spans.mdx
new file mode 100644
index 0000000..0492c7c
--- /dev/null
+++ b/api-reference/projects/spans.mdx
@@ -0,0 +1,10 @@
+---
+title: "Project spans"
+method: POST
+path: /api/v1/projects/{project_id}/spans
+openapi: "POST /api/v1/projects/{project_id}/spans"
+description: Enqueue a span for a project.
+---
+
+Body: `{name, spanType?, startTime, endTime, attributes?, traceId?, parentSpanId?}`.
+Returns `{spanId, traceId}`.
diff --git a/api-reference/projects/spans_search.mdx b/api-reference/projects/spans_search.mdx
new file mode 100644
index 0000000..97e5f10
--- /dev/null
+++ b/api-reference/projects/spans_search.mdx
@@ -0,0 +1,10 @@
+---
+title: "Search spans"
+method: POST
+path: /api/v1/projects/{project_id}/spans/search
+openapi: "POST /api/v1/projects/{project_id}/spans/search"
+description: Search spans for a project.
+---
+
+Body: `{traceId?, searchQuery, startTime?, endTime?, searchIn?, limit, offset}`.
+Returns hits `{trace_id, span_id}` from Quickwit.
diff --git a/api-reference/projects/sql_from_json.mdx b/api-reference/projects/sql_from_json.mdx
new file mode 100644
index 0000000..c12f047
--- /dev/null
+++ b/api-reference/projects/sql_from_json.mdx
@@ -0,0 +1,9 @@
+---
+title: "SQL from JSON"
+method: POST
+path: /api/v1/projects/{project_id}/sql/from-json
+openapi: "POST /api/v1/projects/{project_id}/sql/from-json"
+description: Convert a JSON query structure to SQL.
+---
+
+Body: `{queryStructure}`. Returns `{success, sql?, error?}`.
diff --git a/api-reference/projects/sql_query.mdx b/api-reference/projects/sql_query.mdx
new file mode 100644
index 0000000..d680a68
--- /dev/null
+++ b/api-reference/projects/sql_query.mdx
@@ -0,0 +1,9 @@
+---
+title: "Project SQL query"
+method: POST
+path: /api/v1/projects/{project_id}/sql/query
+openapi: "POST /api/v1/projects/{project_id}/sql/query"
+description: Execute a SQL query for a project.
+---
+
+Body: `{query, parameters}`.
diff --git a/api-reference/projects/sql_to_json.mdx b/api-reference/projects/sql_to_json.mdx
new file mode 100644
index 0000000..b9e757d
--- /dev/null
+++ b/api-reference/projects/sql_to_json.mdx
@@ -0,0 +1,9 @@
+---
+title: "SQL to JSON"
+method: POST
+path: /api/v1/projects/{project_id}/sql/to-json
+openapi: "POST /api/v1/projects/{project_id}/sql/to-json"
+description: Convert SQL to JSON structure.
+---
+
+Body: `{sql}`. Returns `{success, queryStructure?, error?}`.
diff --git a/api-reference/projects/sql_validate.mdx b/api-reference/projects/sql_validate.mdx
new file mode 100644
index 0000000..f29a329
--- /dev/null
+++ b/api-reference/projects/sql_validate.mdx
@@ -0,0 +1,9 @@
+---
+title: "Validate SQL"
+method: POST
+path: /api/v1/projects/{project_id}/sql/validate
+openapi: "POST /api/v1/projects/{project_id}/sql/validate"
+description: Validate a SQL query for a project.
+---
+
+Body: `{query}`. Returns `{success, validatedQuery?, error?}`.
diff --git a/api-reference/queues/queues_push.mdx b/api-reference/queues/queues_push.mdx
index 105a403..cf628ba 100644
--- a/api-reference/queues/queues_push.mdx
+++ b/api-reference/queues/queues_push.mdx
@@ -5,6 +5,6 @@ openapi: 'POST /v1/queues/push'
### Description
-You can push data to a labeling queue.
+This endpoint is not available in the current server build. Labeling queues have not been released yet; data cannot be pushed or consumed at this route today.
-It will then be accessible in the UI for further labeling.
+When queues ship, this page will document the request/response shape.
diff --git a/api-reference/sql/sql_query.mdx b/api-reference/sql/sql_query.mdx
index a7a5af7..f33ad85 100644
--- a/api-reference/sql/sql_query.mdx
+++ b/api-reference/sql/sql_query.mdx
@@ -6,7 +6,7 @@ openapi: /api-reference/openapi.json POST /v1/sql/query
## SQL Query
-You can run SQL queries on your data stored in Laminar using the SQL query API. Learn more in the [SQL Editor](/sql-editor/introduction) reference.
+You can run SQL queries on your data stored in Laminar using the SQL query API. Learn more in the [SQL Editor](/sql-editor/overview) reference.
### Example request
diff --git a/api-reference/tag.mdx b/api-reference/tag.mdx
new file mode 100644
index 0000000..0a2c1f7
--- /dev/null
+++ b/api-reference/tag.mdx
@@ -0,0 +1,9 @@
+---
+title: "Tag spans or traces"
+method: POST
+path: /v1/tag
+openapi: "POST /v1/tag"
+description: Add tags to a trace or span (default key).
+---
+
+Body: `{"names":[...], "traceId": UUID}` or `{"names":[...], "spanId": UUID}`. Resolves the top span if only `traceId` is provided. Returns list of `{id, spanId}`.
diff --git a/changelog/index.mdx b/changelog/index.mdx
new file mode 100644
index 0000000..e19e6f5
--- /dev/null
+++ b/changelog/index.mdx
@@ -0,0 +1,95 @@
+---
+title: "Changelog"
+description: "Key releases and updates"
+---
+
+Automatic tracing for Claude Agent SDK with a lightweight Rust proxy.
+Reliability fixes and better ESM support.
+Serverless API for running browser agents in production with full observability.
+Visualize LangGraph execution flows and trace Skyvern automation with session recordings.
+Query all Laminar data with SQL and build dashboards. Backed by ClickHouse for sub-second reads.
+Session recordings synced with traces; real-time spans and live cost tracking.
+Automatic tracing for generateText/streamText with Next.js support.
+Trace Stagehand runs with session recordings and per-step cost.
+Support for Gemini, Mistral, Bedrock, Groq, Cohere, CrewAI, LiteLLM, plus improved OpenAI/Anthropic instrumentation.
+Self-hosted container to manage browser agent infrastructure.
+Flow (dynamic task engine), Evaluations SDK, Semantic Search API, Labeling Queues, Online Evaluations.
+Automatic tracing (OpenAI, Anthropic, LangChain), @observe decorator, datasets, playground, self-hosting.
+
-
-
-## What You Can Track
-
-Custom Dashboards are built on top of our powerful query engine, working across all your observability data - `traces`, `spans`, `events`, and `tags`. You can track any metric that matters to your application.
-
-For detailed information on available entities, fields, and how to select the right data for your charts, see the [SQL Editor Reference](/sql-editor/reference).
-## How to Build Charts
-
-To create a chart, navigate to **dashboard** menu and click the **`+ Chart`** button in the upper right corner.
-
-
-
-
+Build dashboards on top of your traces, spans, events, and eval scores without extra infrastructure. Track costs, latency, accuracy, or any custom metric in a few clicks.
-**The process:**
+## What you'll build
-1. **Pick visualization**:
- - **Line Chart**: For time series visualization. We automatically prefill and group data by time range, perfect for tracking trends over time.
- - **Bar Chart**: Another alternative to visualize time series data, useful when you want to emphasize individual time periods.
- - **Horizontal Bar Chart**: For visualizations that need to be ranked, similar to a sortable table. Use this to compare and rank items (e.g., top users, models by cost).
+- A dashboard tile showing the metrics that matter (cost, tokens, latency, accuracy).
+- Filters and group-bys to break down by model, route, user, team, or tag.
+- Shareable dashboards for eng/support/research.
-2. **Select data source**: Traces, Spans, Events, and Tags
+## Copy/paste workflow
-3. **Define metrics**: What to measure (count, sum, avg, min, max, p90, p95, p99)
+1. Open **Dashboard** → click **`+ Chart`**.
+2. Choose a visualization (line, bar, horizontal bar for rankings).
+3. Select a source (`spans`, `traces`, `events`, `evaluation_scores`).
+4. Define metrics (count/sum/avg/p90/p95/p99) and group by model/route/tag.
+5. Save to a dashboard and resize/arrange tiles.
-4. **Add context**: Filters to narrow scope, grouping to break down by dimensions, order by fields, limits for top N results
+
+
-**How to build:**
-- Chart type: Line Chart
-- Table: `spans`
-- Metric: `total_tokens` with `sum` aggregation
-- Group by: `model`
-- Filter: `span_type` = `LLM` (to include only LLM calls)
-
-### p90 Cost by Provider
-
-Track cost trends across different LLM providers over time. The p90 metric shows what most of your expensive requests cost, helping you compare provider pricing and spot cost increases.
-
-
-
-
+## Build this next
-**How to build:**
-- Chart type: Line Chart
-- Table: `spans`
-- Metric: `cost` with `p90` aggregation
-- Group by: `provider`
+- Write custom queries → [SQL editor](/sql-editor/overview)
+- Export query results to datasets → [Export & datasets](/sql-editor/overview)
+- Pipe eval scores into dashboards → [Evaluations](/evaluations/quickstart)
diff --git a/datasets/adding-data.mdx b/datasets/adding-data.mdx
index 2af400f..1f0eb40 100644
--- a/datasets/adding-data.mdx
+++ b/datasets/adding-data.mdx
@@ -53,3 +53,7 @@ You can create new datapoints by editing existing ones or copying span data usin
Humans can then edit the datapoints in the queue and save them to new datapoints either
in the same dataset or in a new one. [Learn more about queues](/queues/quickstart).
+
+
+
-## Format
+## What you'll do with datasets
-Every datapoint has two fixed JSON objects: `data` and `target`, each with arbitrary keys.
-`target` is only used in evaluations.
+- Store inputs + expected outputs for evaluations.
+- Turn traces into labeled examples, then iterate in labeling queues.
+- Export query results (cost outliers, bad traces) straight into a dataset.
-- `data` – the actual datapoint data,
-- `target` – data additionally sent to the evaluator function.
-- `metadata` – arbitrary key-value metadata about the datapoint.
-
-For every key inside `data` and `target`, the value can be any JSON value.
-
-### Example
-
-This is an example of a valid datapoint.
+## Datapoint shape
```json
{
- "data": {
- "color": "red",
- "size": "large",
- "messages": [
- {
- "role": "user",
- "content": "Hello, can you help me choose a T-shirt?"
- },
- {
- "role": "assistant",
- "content": "I'm afraid, we don't sell T-shirts"
- }
- ]
- },
- "target": {
- "expected_output": "Of course! What size and color are you looking for?"
- }
+ "data": { "question": "What is the capital of France?" },
+ "target": { "answer": "Paris" },
+ "metadata": { "category": "geography" }
}
```
-## Use case: Evaluations
-
-Datasets can be used for evaluations to specify inputs and expected outputs.
-
-You will need to make sure the dataset keys match the input and output node names of the pipelines.
-See more in the [Evaluations](/evaluations/introduction) page.
-
-## Editing
-
-Datasets are editable. You can edit the datapoints by clicking on the datapoint and
-editing the data in JSON. Changes are saved as a new datapoint version.
+- `data`: the input to your executor.
+- `target`: optional reference passed to evaluators.
+- `metadata`: tags for filtering and grouping.
-### Versioning
+## Storage model (append-only)
-Each datapoint has a unique id and a `created_at` timestamp. Every time you
-edit a datapoint, under the hood,
-a new datapoint version is created with the same id,
-but the `created_at` timestamp is updated.
+- Each datapoint you add is stored as a row with the provided `id` (or a generated UUIDv7).
+- Updates today are append-only; there is no built-in version history or delete/rollback.
+- To change a datapoint, write a new row with the desired values; consumers should pick the record they need.
-The version stack is push-only. That is, when you revert to a previous version,
-a copy of that version is created and added as a current version.
+## Common workflows
-Example:
+- **Feed evals**: wire a dataset into [evaluate](/evaluations/quickstart) to score prompts/agents.
+- **Label from traces**: push spans into a queue, label targets, and write back to the dataset.
+- **Export from SQL**: query outliers in the [SQL editor](/sql-editor/overview) and export to a dataset.
-- Initial version (v1):
-```json
-{
- "id": "019a3122-ca78-7d75-91a7-a860526895b2",
- "created_at": "2025-01-01T00:00:00.000Z",
- "data": { "key": "initial value" }
-}
-```
-- Version 2 (v2):
-```json
-{
- "id": "019a3122-ca78-7d75-91a7-a860526895b2",
- "created_at": "2025-01-05T00:00:05.000Z",
- "data": { "key": "value at v2" }
-}
-```
-- Version 3 (v3):
-```json
-{
- "id": "019a3122-ca78-7d75-91a7-a860526895b2",
- "created_at": "2025-01-10T00:00:10.000Z",
- "data": { "key": "value at v3" }
-}
-```
-
-After this, you want to update to version 1 (initial version). This will create a new version (v4) with the same id, but the `created_at` timestamp is updated.
-
-- Version 4 (v4):
-```json
-{
- "id": "019a3122-ca78-7d75-91a7-a860526895b2",
- "created_at": "2025-01-15T00:00:15.000Z",
- "data": { "key": "initial value" }
-}
-```
+
+
+
-### Datapoint id
+## Build this next
-When you push a new datapoint to a dataset, a UUIDv7 is generated for it.
-This allows to sort datapoints by their creation order and preserve the order of insertion.
+- Create/load datasets programmatically → [Datasets CLI](/datasets/cli)
+- Label quickly → [Labeling queues](/queues/quickstart) (coming soon in the current server build)
+- Run evals on your dataset → [Evaluations quickstart](/evaluations/quickstart)
diff --git a/docs.json b/docs.json
index 0c35654..ada60dc 100644
--- a/docs.json
+++ b/docs.json
@@ -1,6 +1,6 @@
{
"$schema": "https://mintlify.com/docs.json",
- "theme": "mint",
+ "theme": "maple",
"name": "Laminar documentation",
"colors": {
"primary": "#ED6E40",
@@ -12,39 +12,45 @@
"tabs": [
{
"tab": "Documentation",
+ "icon": "book-open",
"groups": [
{
- "group": "Overview",
+ "group": "Laminar",
+ "icon": "info",
"pages": [
"overview",
- "installation",
- {
- "group": "Self-hosting",
- "pages": [
- "self-hosting/setup",
- "self-hosting/access-control-setup"
- ]
- },
- "cursor"
+ "tracing/quickstart"
]
},
{
- "group": "Tracing",
+ "group": "Stage 1: Build locally",
+ "icon": "rocket",
"pages": [
- "tracing/introduction",
- "tracing/quickstart",
+ "installation",
"tracing/automatic-instrumentation",
+ {
+ "group": "Trace structure",
+ "pages": [
+ "tracing/structure/observe",
+ "tracing/structure/manual-span-creation",
+ "tracing/structure/session",
+ "tracing/structure/user-id",
+ "tracing/structure/metadata",
+ "tracing/structure/tags",
+ "tracing/structure/image"
+ ]
+ },
{
"group": "Integrations",
"pages": [
"tracing/integrations/openai",
"tracing/integrations/anthropic",
"tracing/integrations/gemini",
- "tracing/integrations/langchain",
"tracing/integrations/cohere",
+ "tracing/integrations/litellm",
"tracing/integrations/vercel-ai-sdk",
"tracing/integrations/nextjs",
- "tracing/integrations/litellm",
+ "tracing/integrations/langchain",
"tracing/integrations/kernel",
"tracing/integrations/claude-agent-sdk",
"tracing/integrations/browser-use",
@@ -53,28 +59,37 @@
"tracing/integrations/playwright",
"tracing/integrations/puppeteer"
]
- },
+ }
+ ]
+ },
+ {
+ "group": "Stage 2: Ship to production",
+ "icon": "cloud-upload",
+ "pages": [
+ "how-it-works",
{
- "group": "Tracing Structure",
+ "group": "Self-hosting",
"pages": [
- "tracing/structure/overview",
- "tracing/structure/observe",
- "tracing/structure/manual-span-creation",
- "tracing/structure/session",
- "tracing/structure/user-id",
- "tracing/structure/metadata",
- "tracing/structure/tags",
- "tracing/structure/image",
- "tracing/structure/continuing-traces",
- "tracing/structure/providers",
- "tracing/structure/flushing"
+ "self-hosting/setup",
+ "self-hosting/access-control-setup"
]
},
+ "tracing/otel"
+ ]
+ },
+ {
+ "group": "Stage 3: Monitor in production",
+ "icon": "bar-chart",
+ "pages": [
"tracing/realtime",
- "tracing/browser-agent-observability",
- "tracing/langgraph-visualization",
"tracing/events",
- "tracing/otel",
+ {
+ "group": "Browser agents",
+ "pages": [
+ "tracing/browser-agent-observability",
+ "tracing/langgraph-visualization"
+ ]
+ },
{
"group": "Troubleshooting",
"pages": [
@@ -85,14 +100,28 @@
]
},
{
- "group": "Evaluations",
+ "group": "Stage 4: Analyze & discover errors",
+ "icon": "search",
+ "pages": [
+ "playground/introduction",
+ "playground/playground-from-span",
+ "playground/tools",
+ "playground/history",
+ "sql-editor/overview",
+ "sql-editor/reference",
+ "custom-dashboards/overview"
+ ]
+ },
+ {
+ "group": "Stage 5: Create evaluation datasets",
+ "icon": "database",
"pages": [
- "evaluations/introduction",
"evaluations/quickstart",
"evaluations/using-dataset",
- "evaluations/human-evaluators",
"evaluations/configuration",
"evaluations/reference",
+ "evaluations/human-evaluators",
+ "evaluations/reference",
"evaluations/manual-evaluation",
"evaluations/cookbook",
{
@@ -102,78 +131,90 @@
"evaluations/online-evaluators/scoring-with-hosted-evaluators",
"evaluations/online-evaluators/scoring-with-sdk"
]
- }
+ },
+ {
+ "group": "Datasets",
+ "pages": [
+ "datasets/adding-data",
+ "datasets/cli"
+ ]
+ },
+ "queues/quickstart"
]
- },
+ }
+ ]
+ },
+ {
+ "tab": "Recipes",
+ "icon": "map",
+ "pages": [
+ "guides/fastapi",
+ "guides/nextjs",
+ "guides/nextjs-aisdk",
+ "guides/evaluating-tool-calls"
+ ]
+ },
+ {
+ "tab": "Changelog",
+ "icon": "megaphone",
+ "pages": [
+ "changelog/index"
+ ]
+ },
+ {
+ "tab": "API Reference",
+ "icon": "code",
+ "pages": [
+ "api-reference/introduction",
{
- "group": "SQL Editor",
+ "group": "Ingestion",
"pages": [
- "sql-editor/introduction",
- "sql-editor/overview",
- "sql-editor/reference"
+ "api-reference/ingestion/traces",
+ "api-reference/ingestion/browser-sessions",
+ "api-reference/ingestion/metrics",
+ "api-reference/ingestion/otlp-grpc"
]
},
{
- "group": "Custom Dashboards",
+ "group": "Tracing & datasets",
"pages": [
- "custom-dashboards/overview"
+ "api-reference/tag",
+ "api-reference/datasets/list",
+ "api-reference/datasets/datapoints",
+ "api-reference/datasets/upsert",
+ "api-reference/datasets/parquet",
+ "api-reference/payloads/get_payload",
+ "api-reference/sql/sql_query"
]
},
{
- "group": "Datasets",
+ "group": "Evaluations",
"pages": [
- "datasets/introduction",
- "datasets/adding-data",
- "datasets/cli"
+ "api-reference/evals/init_eval",
+ "api-reference/evals/save_eval_datapoints",
+ "api-reference/evals/update_eval_datapoint",
+ "api-reference/evaluators/score"
]
},
{
- "group": "Labeling Queues",
+ "group": "Project (scoped)",
"pages": [
- "queues/quickstart"
+ "api-reference/projects/spans",
+ "api-reference/projects/spans_search",
+ "api-reference/projects/sql_query",
+ "api-reference/projects/sql_validate",
+ "api-reference/projects/sql_to_json",
+ "api-reference/projects/sql_from_json",
+ "api-reference/projects/eval_score_stats",
+ "api-reference/projects/eval_score_distribution",
+ "api-reference/projects/realtime"
]
},
{
- "group": "Playground",
+ "group": "Health",
"pages": [
- "playground/introduction",
- "playground/playground-from-span",
- "playground/tools",
- "playground/history"
- ]
- }
- ]
- },
- {
- "tab": "Guides",
- "pages": [
- "guides/fastapi",
- "guides/nextjs-aisdk",
- "guides/nextjs",
- "guides/evaluating-tool-calls"
- ]
- },
- {
- "tab": "API Reference",
- "groups": [
- {
- "group": "API Documentation",
- "pages": [
- "api-reference/introduction",
- {
- "group": "Evaluations",
- "pages": [
- "api-reference/evals/init_eval",
- "api-reference/evals/save_eval_datapoints",
- "api-reference/evals/update_eval_datapoint"
- ]
- },
- {
- "group": "SQL",
- "pages": [
- "api-reference/sql/sql_query"
- ]
- }
+ "api-reference/health/health",
+ "api-reference/health/ready"
]
}
]
@@ -194,6 +235,12 @@
}
},
"logo": "/logo/logo.png",
+ "styling": {
+ "breadcrumbs": true,
+ "css": [
+ "/style.css"
+ ]
+ },
"api": {
"openapi": [
"api-reference/openapi.json"
@@ -238,4 +285,4 @@
"apiHost": "https://p.lmnr.ai"
}
}
-}
\ No newline at end of file
+}
diff --git a/evaluations/configuration.mdx b/evaluations/configuration.mdx
index 0778ab1..b992b7e 100644
--- a/evaluations/configuration.mdx
+++ b/evaluations/configuration.mdx
@@ -1,7 +1,7 @@
---
sidebarTitle: Configuration
title: Configuring Laminar evaluations
-description: This page describes how to configure evaluations in Laminar and showcases some common use cases.
+description: Configure evaluations in Laminar and see common use cases.
---
## Configuring evaluations to report results to locally self-hosted Laminar
diff --git a/evaluations/human-evaluators.mdx b/evaluations/human-evaluators.mdx
index 170b3b2..dde4eb9 100644
--- a/evaluations/human-evaluators.mdx
+++ b/evaluations/human-evaluators.mdx
@@ -200,7 +200,7 @@ Let's explore how to collect human evaluator data into datasets and use it to va
### Collecting human evaluator data into datasets
-The [SQL Editor](/sql-editor/introduction) is a powerful tool for analyzing your human evaluator results and creating datasets for training or validating LLM-as-a-judge evaluators. Here's how to leverage it:
+The [SQL Editor](/sql-editor/overview) is a powerful tool for analyzing your human evaluator results and creating datasets for training or validating LLM-as-a-judge evaluators. Here's how to leverage it:
### Finding human evaluator spans
@@ -223,4 +223,4 @@ After running this query, click **"Export to Dataset"** to:
2. Map the `input` to dataset `data` field
3. Map the `output` to dataset `target` field
-Then you can use this dataset to run evaluation of your LLM-as-a-judge evaluator and use human score in `target` field as an expected score for the LLM-as-a-judge evaluator.
\ No newline at end of file
+Then you can use this dataset to run evaluation of your LLM-as-a-judge evaluator and use human score in `target` field as an expected score for the LLM-as-a-judge evaluator.
diff --git a/evaluations/introduction.mdx b/evaluations/introduction.mdx
deleted file mode 100644
index c76666f..0000000
--- a/evaluations/introduction.mdx
+++ /dev/null
@@ -1,48 +0,0 @@
----
-sidebarTitle: Introduction
-title: Introduction to Laminar evaluations
----
-import GetProjectApiKey from '/snippets/get-project-api-key.mdx';
-
-Evaluation is the process of validating and testing the outputs that your AI applications are producing. Having strong evaluations ("evals") means a more stable, reliable application that is resilient to code and model changes. An eval is a task used to measure the quality of the output of an LLM or LLM system.
-
-
-
+
-### Create an evaluation file
+## Why evaluations?
-
+
-## Tracking evaluation progress
+Click any row to see the full trace for that datapoint.
-To track the score progression over time or compare evaluations side-by-side, you need to group them together. This can be achieved by passing the `groupName` parameter to the `evaluate` function.
+## How it works
-
-
\ No newline at end of file
+
+
+
+## Where data lives
+
+- **Laminar Cloud**: managed storage and dashboard.
+- **Self-hosted**: data stays in your ClickHouse; switch by changing the endpoint.
+
+## Security
+
+- Project-scoped API keys
+- TLS in transit; encryption at rest
+- Self-hosting keeps data in your network
+
+Next: get a trace in minutes → [Quickstart](/tracing/quickstart)
diff --git a/installation.mdx b/installation.mdx
index c47d8fd..8d307da 100644
--- a/installation.mdx
+++ b/installation.mdx
@@ -1,112 +1,67 @@
---
title: Installation
-description: Laminar installation guide
+description: Copy/paste installs to get your first Laminar trace fast.
---
-## Install the package
+Laminar is designed to get you to a live trace in minutes. Pick your language, run the three commands, and your LLM calls will show up with inputs, outputs, tokens, and costs.
-
+
-
+
-The Playground serves as a sandbox environment where you can:
+## What you'll do here
-- **Prompt experimentation**: Test different prompt variations and input configurations with instant results.
-- [**Playground from span**](/playground/playground-from-span): Reproduce and experiment with exact configurations from any span by opening it directly in playground.
-- [**Tool integration**](/playground/tools): Configure and test custom tools that models can call during conversations.
-- [**Session history**](/playground/history): Access complete traces of all previous runs with full context and configurations.
+- Reproduce any trace span (LLM/tool call) in one click.
+- Edit prompts, models, and tool configs with instant feedback.
+- Save variants and compare outputs; keep session history for later.
-
-
+
-
-
-
-
-
-### Using SQL Query API
-
-You can also run queries directly from the API. It is available at the `/v1/sql/query` endpoint.
-
-Querying via API is identical to using the SQL Editor, you simply pass the `query` as a parameter.
-
-Read the [API reference](/api-reference/sql/sql_query) page for more information.
-
-### Example query
-
-```sql
-SELECT
- input,
- output,
- start_time
-FROM spans
-WHERE start_time BETWEEN now() - INTERVAL '3 days' AND now()
-```
-
-This query will return the input and output of the spans in the last 3 days.
-
-### Viewing results
-
-Results are displayed in a table or raw JSON view.
-
-
-
-### Exporting results
-
-Once you have selected the results you want to export, click the "Export to Dataset" button.
-
-Choose the dataset you want to export to and map the columns to the dataset `data`, `metadata`, and `target` fields.
-
-
-
-[Learn more about datasets](/datasets/introduction)
-
-## Next steps
-
-- [Overview](/sql-editor/overview) – Overview of the Laminar SQL Editor and how to use it
-- [Reference](/sql-editor/reference) – Reference of the table schemas and best practices
diff --git a/sql-editor/overview.mdx b/sql-editor/overview.mdx
index 16acacb..7839196 100644
--- a/sql-editor/overview.mdx
+++ b/sql-editor/overview.mdx
@@ -3,250 +3,50 @@ title: Overview of Laminar SQL syntax and approach
sidebarTitle: Overview
---
-## Introduction
+Query your Laminar data with ClickHouse SQL to drive dashboards, exports, and debugging. Use these patterns to keep queries fast and reliable.
-Laminar stores all queryable data in Clickhouse. Clickhouse is an analytical columnar database which provides a SQL-like query language.
+## What you'll do
-This guide will explain the SQL syntax used in Laminar SQL Editor. For the full Clickhouse SQL reference, see the [Clickhouse documentation](https://clickhouse.com/docs/sql-reference).
+- Write SELECT-only queries against spans, traces, events, and eval tables.
+- Filter efficiently (start_time) and avoid heavy joins.
+- Extract JSON fields and truncate time for grouping.
-## Basic query structure
+## Fast patterns
-We only allow `SELECT` queries, so we will focus on the syntax for this.
+- Always filter by `start_time` when possible for performance.
+- Prefer application-side joins; fetch ids in one query, hydrate in another.
+- Use `simpleJSONExtract*` when you know the type.
-Here's the basic syntax of a Clickhouse `SELECT` query. Some parts that we don't recommend or don't support are omitted.
-
-```
-[WITH expr_list(subquery)]
-SELECT [DISTINCT [ON (column1, column2, ...)]] expr_list
-[FROM [db.]table | (subquery) | table_function] [FINAL]
-[SAMPLE sample_coeff]
-[ARRAY JOIN ...]
-[PREWHERE expr]
-[WHERE expr]
-[GROUP BY expr_list] [WITH ROLLUP|WITH CUBE] [WITH TOTALS]
-[HAVING expr]
-[WINDOW window_expr_list]
-[QUALIFY expr]
-[ORDER BY expr_list]
- [WITH FILL] [FROM expr] [TO expr] [STEP expr] [INTERPOLATE [(expr_list)]]
-[LIMIT [offset_value, ]n BY columns]
-[LIMIT [n, ]m] [WITH TIES]
-```
-
-The very basics are similar to standard SQL. That is, you can perform any
-`SELECT FROM WHERE GROUP BY HAVING ORDER BY LIMIT` query.
-
-## Data types
-
-Clickhouse has numerous data types. Here are some of the most important and relevant ones.
-
-- `DateTime64` - represents a date and time with a precision of nanoseconds. All datetimes in Laminar are in UTC.
-- `String` - represents a string.
-- `UUID` - represents a universally unique identifier. This is used for most identifier columns.
-- `Float64` - represents a floating point number.
-- `UInt64` - represents a 64-bit unsigned integer.
-- `UInt8` - unsigned 8-bit integer. Used for enum values.
-
-Should you need to cast anything in your query, you can use the postgres-like `'value'::type` syntax, for example:
-
-```sql
-SELECT name, input, output
-FROM spans
-WHERE start_time > '2025-01-01'::DateTime
-AND start_time < '2025-01-02'::DateTime
-```
-
-### JSON
-
-Laminar stores JSONs as strings in Clickhouse.
-See [Working with JSONs](#working-with-jsons) for more details.
-
-## Filtering by start_time
-
-Spans inside each project are ordered by `start_time`, so adding a `start_time` filter will speed up the query and prevent it from failing because of running out of memory.
-
-This is relevant to both the `spans` table and the `traces` aggregation view on it.
-
-### Example
-
-Suppose you want to have a look at all the tool call spans in a single trace. You know the trace ID.
+### Example: spans per day (last month)
```sql
-SELECT name, input, output, start_time, end_time
-FROM spans
-WHERE trace_id = {traceId: UUID} AND span_type = 6 -- tool call
-```
-
-You can speed up the query significantly by adding a `start_time` filter.
-
-```sql
-SELECT name, input, output, start_time, end_time
-FROM spans
-WHERE trace_id = {traceId: UUID} AND span_type = 6 -- tool call
-AND start_time >= (
- SELECT start_time FROM traces WHERE trace_id = {traceId: UUID} LIMIT 1
-)
-```
-
-## Avoiding joins
-
-Clickhouse is a columnar database, so it's not optimized for joins. If you need to join data, you can do it in the application layer.
-
-### Example
-
-Let's say you want to have a look at LLM spans that took abnormally long time to complete and see the effect of this on their corresponding traces.
-
-In regular SQL, you would join the `spans` table with the `traces` table to get the trace duration.
-
-```sql
-SELECT t.duration, s.name, s.input, s.output, s.start_time, s.end_time
-FROM spans s
-JOIN traces t ON s.trace_id = t.trace_id
-WHERE s.start_time > now() - INTERVAL '1 day'
-AND s.span_type = 1 -- LLM
-AND s.end_time - s.start_time > 90 -- 90 seconds
-```
-
-In Clickhouse, you would need to collect trace_ids in the application, and then query the `traces` table for each trace_id.
-
-```sql
--- 1. First query
-SELECT duration, name, input, output, start_time, end_time, trace_id
-FROM spans
-WHERE span_type = 1 -- LLM
-AND start_time > now() - INTERVAL '1 day'
-AND end_time - start_time > 90 -- 90 seconds
-
--- 2. Collect trace_ids from the first query and use them in the second query
-SELECT duration
-FROM traces
-WHERE trace_id IN ({traceIds: Array(UUID)})
-```
-
-This may seem counter-intuitive at first, but this is the fastest and the most efficient way to do it.
-
-## Working with dates
-
-See the full reference in [Clickhouse documentation](https://clickhouse.com/docs/sql-reference/functions/date-time-functions).
-
-### Truncating datetimes
-
-Clickhouse has a special syntax for truncating datetimes. The most general function for truncation is `toStartOfInterval(value, interval_specifier)`.
-
-```sql Spans per day in the last month
SELECT
- toStartOfInterval(start_time, INTERVAL 1 DAY) AS day,
- count(*) AS spans_count
+ toStartOfInterval(start_time, INTERVAL 1 DAY) AS day,
+ count(*) AS spans_count
FROM spans
WHERE start_time > now() - INTERVAL 1 MONTH
GROUP BY day
-ORDER BY day ASC
+ORDER BY day
```
-If you are familiar with Postgres `date_trunc` function, notice how this is similar.
-
-`toStartOfInterval` is more flexible, because you can specify any interval, not just the ones supported by `date_trunc`.
-
-For example, you can group spans within last day to 15-minute intervals.
+### Example: extract JSON
```sql
SELECT
- toStartOfInterval(start_time, INTERVAL 15 MINUTE) AS interval,
- count(*) AS spans_count
+ simpleJSONExtractInt(attributes, 'gen_ai.usage.input_tokens') AS input_tokens
FROM spans
-WHERE start_time > now() - INTERVAL 1 DAY
-GROUP BY interval
-ORDER BY interval ASC
+WHERE span_type = 'LLM'
+ AND start_time > now() - INTERVAL 1 DAY
```
-There are also convenience functions for common intervals:
-
-- `toStartOfSecond(value)` - truncates to the start of the second
-- `toStartOfMinute(value)` - truncates to the start of the minute
-- `toStartOfTenMinutes(value)` - truncates to the start of the minute
-- `toStartOfHour(value)` - truncates to the start of the hour
-- `toStartOfDay(value)` - truncates to the start of the day
-- `toStartOfWeek(value)` - truncates to the start of the week
-- `toStartOfMonth(value)` - truncates to the start of the month
-- `toStartOfQuarter(value)` - truncates to the start of the month
-- `toStartOfYear(value)` - truncates to the start of the year
-
-## Working with JSON
-
-Many columns, such as `attributes` on `spans` table, contain JSON values stored as strings.
-Clickhouse provides a wide variety of functions to work with JSONs. See the full reference in [Clickhouse documentation](https://clickhouse.com/docs/sql-reference/functions/json-functions).
-
-Generally, there are two families of functions to work with JSONs:
-- `JSON*` functions - comprehensive set of functions that work on the JSON strings
-- `simpleJSON*` functions - simpler subset that works much faster, and under a few assumptions.
-
-
+
-That is, by default, you will get both the LLM calls traces and a session recording of
-the browser sessions.
+## Why this matters
-## How does it work?
+Logs alone can’t tell you what the agent saw. Laminar records browser sessions alongside traces, so every agent step lines up with the exact frame in the recording.
-Laminar traces the LLM calls using automatic instrumentations provided by OpenLLMetry.
+## One-line setup
-In addition, Laminar instruments popular browser automation frameworks (integrations with [Puppeteer](/tracing/integrations/puppeteer), [Playwright](/tracing/integrations/playwright), [Stagehand](/tracing/integrations/stagehand), [BrowserUse](/tracing/integrations/browser-use), and [Skyvern](/tracing/integrations/skyvern)) and records the browser sessions.
-
-## Example
-
-Here is an example of a simple browser agent that uses Playwright to navigate to a website and extract the title.
-
-
+
- await page.locator('input[name="q"]').press('Enter');
- const textSelector = await page.locator('a', { hasText: 'www.lmnr.ai' }).first();
- await textSelector?.waitFor();
- await textSelector?.click();
- const title = await page.title();
- await page.waitForLoadState('load');
- console.log('Title of this page is', title);
- await browser.close();
-}
+- Timeline scrubbing: click any span, see that frame in the recording
+- Action overlay: clicks, scrolls, and inputs highlighted
+- Cost and tokens per step
-main().then(() => Laminar.shutdown().then(() => {
- console.log('Done!');
-}));
-```
-
+Open your dashboard. Every agent step is a span; each span is synced to the recording.
-A more complex trace may look like this. This was recorded by running Laminar Index browser agent.
+## Next steps
-
+
+
-## Getting Started
+## What you'll build
-
+
-
+
-Otherwise, you can achieve this by using `observe` function wrapper, e.g. something like
+## What to look for
-```javascript app/api/chat/route.ts {1-2}
-import { NextRequest } from 'next/server';
-import { getTracer, observe } from '@lmnr-ai/lmnr';
+- Route span (`POST /api/chat`) with duration.
+- Child span for AI SDK with prompt/response, tokens, and cost.
+- Playground button on the LLM span to iterate on the prompt.
-export const GET = observe(async (req: NextRequest) => {
- const tracer = getTracer();
+## Why this matters
- const { firstText, secondText } = await observe(
- {
- name: 'GET /api/chat',
- },
- async () => {
- const { firstText } = await generateText({
- model: openai('gpt-4.1-nano'),
- prompt: 'What is Laminar flow?',
- experimental_telemetry: {
- isEnabled: true,
- tracer,
- },
- });
-
- const { secondText } = await generateText({
- model: openai('gpt-4.1-nano'),
- prompt: 'What is Laminar flow?',
- experimental_telemetry: {
- isEnabled: true,
- tracer,
- },
- });
+- Fast visibility into routes and model calls without custom logging.
+- Cost and latency by route help you prioritize optimizations.
+- Shareable trace links make it easy to collaborate on prompt changes.
- return { firstText, secondText };
- }
- );
-
- return NextResponse.json({ firstText, secondText });
-});
-```
+## Build this next
-Learn more about `observe` function wrapper [here](/tracing/structure/observe).
+- Use the AI SDK tracer directly → [Vercel AI SDK integration](/tracing/integrations/vercel-ai-sdk)
+- Compare providers in one app → [OpenAI](/tracing/integrations/openai), [Anthropic](/tracing/integrations/anthropic)
+- Add tags to break down costs by feature/team → [Tags and metadata](/tracing/structure/tags)
diff --git a/tracing/integrations/openai.mdx b/tracing/integrations/openai.mdx
index 974bc9e..2bf7a7c 100644
--- a/tracing/integrations/openai.mdx
+++ b/tracing/integrations/openai.mdx
@@ -1,154 +1,112 @@
---
-title: LLM Observability for OpenAI SDK in JS and Python
+title: Trace OpenAI in JS and Python (3 steps)
sidebarTitle: OpenAI
-description: Instrument your OpenAI API calls with Laminar
+description: Copy/paste setup to see OpenAI calls with inputs, outputs, tokens, and cost.
---
-## Overview
+Instrument OpenAI with a single import. You will get per-call traces with prompts, responses, latency, tokens, and cost, plus links to Playgrounds for fast iteration.
-Laminar automatically instruments the official OpenAI package with a single line of code, allowing you to trace and monitor all your OpenAI API calls without modifying your existing code. This provides complete visibility into your AI application's performance, costs, and behavior.
+
+
+
-## Getting Started
+## What you'll build
-
+
+
+## What you'll build
+
+- Traces for AI SDK calls with inputs/outputs and cost.
+- Works in Next.js and Node.js with minimal config.
+- Shareable trace links + Playground for prompt iteration.
+
+## Copy/paste/run
-
-
+
-### 3. That's it! Your LLM API Calls Are Now Traced
+You’ll see:
+- Full input prompt and complete response
+- Token count and cost
+- Latency breakdown
-Once initialized, Laminar automatically traces LLM API calls. For example, after initialization, this standard OpenAI call:
+## What just happened?
-
-
+
+
+## Next steps
+
+