diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index a766454..695ae92 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -36,7 +36,16 @@ jobs: bun-version: 1.3.14 - name: Install dependencies - run: bun install + run: bun ci + + - name: Typecheck + run: bun run typecheck - name: Run tests run: bun test + + - name: Fake-call every endpoint in every API snapshot + run: bun run conformance:all + + - name: Smoke-test built CLI + run: bun bin/langfuse.mjs --help diff --git a/.gitignore b/.gitignore index e7a6b96..065923d 100644 --- a/.gitignore +++ b/.gitignore @@ -1,7 +1,6 @@ node_modules dist *.tgz -bun.lock bun.lockb .env .env.local diff --git a/bin/langfuse.mjs b/bin/langfuse.mjs index 4869439..709d0eb 100755 --- a/bin/langfuse.mjs +++ b/bin/langfuse.mjs @@ -1,3 +1,3 @@ -#!/usr/bin/env node +#!/usr/bin/env bun import { run } from "../dist/cli.js"; -run(process.argv); +await run(process.argv); diff --git a/bun.lock b/bun.lock new file mode 100644 index 0000000..c0a2d1a --- /dev/null +++ b/bun.lock @@ -0,0 +1,102 @@ +{ + "lockfileVersion": 1, + "configVersion": 1, + "workspaces": { + "": { + "name": "langfuse-cli", + "devDependencies": { + "@apidevtools/swagger-parser": "^12.1.0", + "@types/bun": "^1.3.14", + "ajv": "^8.17.1", + "ajv-formats": "^3.0.1", + "typescript": "^7.0.2", + "yaml": "^2.8.2", + }, + }, + }, + "packages": { + "@apidevtools/json-schema-ref-parser": ["@apidevtools/json-schema-ref-parser@14.0.1", "", { "dependencies": { "@types/json-schema": "^7.0.15", "js-yaml": "^4.1.0" } }, "sha512-Oc96zvmxx1fqoSEdUmfmvvb59/KDOnUoJ7s2t7bISyAn0XEz57LCCw8k2Y4Pf3mwKaZLMciESALORLgfe2frCw=="], + + "@apidevtools/openapi-schemas": ["@apidevtools/openapi-schemas@2.1.0", "", {}, "sha512-Zc1AlqrJlX3SlpupFGpiLi2EbteyP7fXmUOGup6/DnkRgjP9bgMM/ag+n91rsv0U1Gpz0H3VILA/o3bW7Ua6BQ=="], + + "@apidevtools/swagger-methods": ["@apidevtools/swagger-methods@3.0.2", "", {}, "sha512-QAkD5kK2b1WfjDS/UQn/qQkbwF31uqRjPTrsCs5ZG9BQGAkjwvqGFjjPqAuzac/IYzpPtRzjCP1WrTuAIjMrXg=="], + + "@apidevtools/swagger-parser": ["@apidevtools/swagger-parser@12.1.0", "", { "dependencies": { "@apidevtools/json-schema-ref-parser": "14.0.1", "@apidevtools/openapi-schemas": "^2.1.0", "@apidevtools/swagger-methods": "^3.0.2", "ajv": "^8.17.1", "ajv-draft-04": "^1.0.0", "call-me-maybe": "^1.0.2" }, "peerDependencies": { "openapi-types": ">=7" } }, "sha512-e5mJoswsnAX0jG+J09xHFYQXb/bUc5S3pLpMxUuRUA2H8T2kni3yEoyz2R3Dltw5f4A6j6rPNMpWTK+iVDFlng=="], + + "@types/bun": ["@types/bun@1.3.14", "", { "dependencies": { "bun-types": "1.3.14" } }, "sha512-h1hFqFVcvAvD9j9K7ZW7vd82aSA+rTdznZa+5bwvCwqSB1jmmfLcbIWhOLx1/+boy/xmjgCs/OMUL8hRJSmnPw=="], + + "@types/json-schema": ["@types/json-schema@7.0.15", "", {}, "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA=="], + + "@types/node": ["@types/node@26.1.1", "", { "dependencies": { "undici-types": "~8.3.0" } }, "sha512-nxAkRSVkN1Y0JC1W8ky/fTfkGsMmcrRsbx+3XoZE+rMOX71kLYTV7fLXpqud1GpbpP5TuffXFqfX7fH2GgZREw=="], + + "@typescript/typescript-aix-ppc64": ["@typescript/typescript-aix-ppc64@7.0.2", "", { "os": "aix", "cpu": "ppc64" }, "sha512-MTKKkWB7p/0E9xi1d1tHtZ5PiLkGEMIq88pK2CubZjOsLtYTLqhgIgi6zepFa+9GHZ6h05NMCkQxGKiPXMxXtQ=="], + + "@typescript/typescript-darwin-arm64": ["@typescript/typescript-darwin-arm64@7.0.2", "", { "os": "darwin", "cpu": "arm64" }, "sha512-gowzar9MwS/aRWp6f3a4KUqzRjAZjOsmGNCM6LcTgXum+dBfgsBVMN+AgvOCCbguXyick6LJhpBszxMebJ8syA=="], + + "@typescript/typescript-darwin-x64": ["@typescript/typescript-darwin-x64@7.0.2", "", { "os": "darwin", "cpu": "x64" }, "sha512-SZ9xZInqApNlNGc9s0W1VSsktYSOe9cFqNOIqmN1Gs8SmkjKZYFt017G4VwPxASInODuAdbTW7sXiFUf893RgA=="], + + "@typescript/typescript-freebsd-arm64": ["@typescript/typescript-freebsd-arm64@7.0.2", "", { "os": "freebsd", "cpu": "arm64" }, "sha512-W5NH4y/J0plIIS5b2xvTEkU7JFxyqdMAOgf+Ilhl0vHQXKO5dZoxd+C/jEtq56c4F3wk71RB4BMRQ2XdI+bwYQ=="], + + "@typescript/typescript-freebsd-x64": ["@typescript/typescript-freebsd-x64@7.0.2", "", { "os": "freebsd", "cpu": "x64" }, "sha512-UMGDx5sTpzNw3WiPebH7l90IWfJggEd+egHt/q6p7/Cm3zqoV7VxkGXt+3DxPIw8CcmvAB0j3sVVfbhX+M4Tpw=="], + + "@typescript/typescript-linux-arm": ["@typescript/typescript-linux-arm@7.0.2", "", { "os": "linux", "cpu": "arm" }, "sha512-gffT3xPz9sR7j/YJExkyPntrI0P2EP9XbOyWzth2/Gs0RstK+90RBcO0ncXoXy/beYll1SXw846Nf2zdnEz0QQ=="], + + "@typescript/typescript-linux-arm64": ["@typescript/typescript-linux-arm64@7.0.2", "", { "os": "linux", "cpu": "arm64" }, "sha512-Qh4eU4/y3yDjnfjjyPYihMj5/ODIlmt+Bzu17OI+fiSRDW57QmU5SiN63exPRNJPKUzcc1INa1NXdrJ+MqHjUQ=="], + + "@typescript/typescript-linux-loong64": ["@typescript/typescript-linux-loong64@7.0.2", "", { "os": "linux", "cpu": "none" }, "sha512-uEHck9i8hoAzXPiYRib1O7miOnz23SxIeVl6F4LXox+qov1K35jHcEW6VHKvZI+pyvl7fZEP4MCU5LYvIq1GuQ=="], + + "@typescript/typescript-linux-mips64el": ["@typescript/typescript-linux-mips64el@7.0.2", "", { "os": "linux", "cpu": "none" }, "sha512-R4KvAMnE43W5Qeqb0Ly56O3mWMWIAgsMyz36DCaycd5nbg/9kzm0liw3JocfRqyJY0KPmzFjbswozXyW0DnIYA=="], + + "@typescript/typescript-linux-ppc64": ["@typescript/typescript-linux-ppc64@7.0.2", "", { "os": "linux", "cpu": "ppc64" }, "sha512-DORx5b3sd/4S7eayxm4FQv+A7CrkUIGRaHiwI8oiHTAI1fAPWhF4J0vAlkC8biAlHSVVwxMQ3tjZ2/DVbnQiiA=="], + + "@typescript/typescript-linux-riscv64": ["@typescript/typescript-linux-riscv64@7.0.2", "", { "os": "linux", "cpu": "none" }, "sha512-wf0jqEDOjrPRnKwYRyyJDRo11KMbvMFrU+q4zqKyChODBzvlkbhNQfKvLxQCcwTpdDaXSHZTVuh0JoCrKCUMHQ=="], + + "@typescript/typescript-linux-s390x": ["@typescript/typescript-linux-s390x@7.0.2", "", { "os": "linux", "cpu": "s390x" }, "sha512-IkwJc3L7yhytWd/ewjyxNDfOmswCm9GWMJT/ue/dU4aZNbwZeYAetq42VyLmsmSjvoX7z74X6ZaYCtzAr0EuGw=="], + + "@typescript/typescript-linux-x64": ["@typescript/typescript-linux-x64@7.0.2", "", { "os": "linux", "cpu": "x64" }, "sha512-EYdf2cNg7rgCWJnxCdJ+F3V39O8ihb37eHAu1LK8oAFizgTQbPOK7zHHXbPt8rX24COqODXeI3sIf0fCXG7H/A=="], + + "@typescript/typescript-netbsd-arm64": ["@typescript/typescript-netbsd-arm64@7.0.2", "", { "os": "none", "cpu": "arm64" }, "sha512-+polYF4MF04aPpO5FTkHran9yUQDSXqy5GiSDKpsll5jy3l3+g9QLhpf39T+ePtefhXLOGrLl0QIjkQP6VnelA=="], + + "@typescript/typescript-netbsd-x64": ["@typescript/typescript-netbsd-x64@7.0.2", "", { "os": "none", "cpu": "x64" }, "sha512-8YIT0EHM/3dq10ZOVF/A7pc/YSMtbcecct4rWtexrnSCHOPcpC2KTLXfTCR6vDpnSiY12heNb1GiN/wu+T/FyA=="], + + "@typescript/typescript-openbsd-arm64": ["@typescript/typescript-openbsd-arm64@7.0.2", "", { "os": "openbsd", "cpu": "arm64" }, "sha512-APT8+ClYnuYm1u9+kgGXoMj2VzWzcymwh2gNSQVySHfkRDGOTVkoWLjCmOQSaO+PoqQ57B0flRp9SA+7GnnkzQ=="], + + "@typescript/typescript-openbsd-x64": ["@typescript/typescript-openbsd-x64@7.0.2", "", { "os": "openbsd", "cpu": "x64" }, "sha512-yX7s+Q0Dln0Dt9tEzZsAjXXR/+ytBM7AlglaqyeMPxQszJ1JhlJdZ6jLA+IzldHtflX81em7lDao1xXu+aRRkg=="], + + "@typescript/typescript-sunos-x64": ["@typescript/typescript-sunos-x64@7.0.2", "", { "os": "sunos", "cpu": "x64" }, "sha512-dLJDGaLZ1D4HPQn62u1n8mBDkJREwMsAkCdkwd4Ieqw+x3TUyTsqY0YiBCtE6H6OzzgGk3iuZ3vFWRS+E8/d1g=="], + + "@typescript/typescript-win32-arm64": ["@typescript/typescript-win32-arm64@7.0.2", "", { "os": "win32", "cpu": "arm64" }, "sha512-Gyl1Vy6OsWesLzmq+EP0Fb7b4Nid5232AvcA2SFcdYreldpNtYFFofPjnt62y9hQy7VTaZp65ICJjuAQRaVcIQ=="], + + "@typescript/typescript-win32-x64": ["@typescript/typescript-win32-x64@7.0.2", "", { "os": "win32", "cpu": "x64" }, "sha512-0BQ3HkAHHlKLSp1qRvf3SUhGpGsDuhB/jgFw75guyqbxJqEaS0Cw/VFO8i2nHglJUzQCRtMMR/IBAKE3ETMC4g=="], + + "ajv": ["ajv@8.17.1", "", { "dependencies": { "fast-deep-equal": "^3.1.3", "fast-uri": "^3.0.1", "json-schema-traverse": "^1.0.0", "require-from-string": "^2.0.2" } }, "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g=="], + + "ajv-draft-04": ["ajv-draft-04@1.0.0", "", { "peerDependencies": { "ajv": "^8.5.0" }, "optionalPeers": ["ajv"] }, "sha512-mv00Te6nmYbRp5DCwclxtt7yV/joXJPGS7nM+97GdxvuttCOfgI3K4U25zboyeX0O+myI8ERluxQe5wljMmVIw=="], + + "ajv-formats": ["ajv-formats@3.0.1", "", { "dependencies": { "ajv": "^8.0.0" } }, "sha512-8iUql50EUR+uUcdRQ3HDqa6EVyo3docL8g5WJ3FNcWmu62IbkGUue/pEyLBW8VGKKucTPgqeks4fIU1DA4yowQ=="], + + "argparse": ["argparse@2.0.1", "", {}, "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q=="], + + "bun-types": ["bun-types@1.3.14", "", { "dependencies": { "@types/node": "*" } }, "sha512-4N0ig0fEomHt5R0KCFWjovxow98rIoRwKolrYdCcknNwMekCXRnWEUvgu5soYV8QXtVsrUD8B95MBOZGPvr6KQ=="], + + "call-me-maybe": ["call-me-maybe@1.0.2", "", {}, "sha512-HpX65o1Hnr9HH25ojC1YGs7HCQLq0GCOibSaWER0eNpgJ/Z1MZv2mTc7+xh6WOPxbRVcmgbv4hGU+uSQ/2xFZQ=="], + + "fast-deep-equal": ["fast-deep-equal@3.1.3", "", {}, "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q=="], + + "fast-uri": ["fast-uri@3.1.0", "", {}, "sha512-iPeeDKJSWf4IEOasVVrknXpaBV0IApz/gp7S2bb7Z4Lljbl2MGJRqInZiUrQwV16cpzw/D3S5j5Julj/gT52AA=="], + + "js-yaml": ["js-yaml@4.1.1", "", { "dependencies": { "argparse": "^2.0.1" }, "bin": { "js-yaml": "bin/js-yaml.js" } }, "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA=="], + + "json-schema-traverse": ["json-schema-traverse@1.0.0", "", {}, "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug=="], + + "openapi-types": ["openapi-types@12.1.3", "", {}, "sha512-N4YtSYJqghVu4iek2ZUvcN/0aqH1kRDuNqzcycDxhOUpg7GdvLa2F3DgS6yBNhInhv2r/6I0Flkn7CqL8+nIcw=="], + + "require-from-string": ["require-from-string@2.0.2", "", {}, "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw=="], + + "typescript": ["typescript@7.0.2", "", { "optionalDependencies": { "@typescript/typescript-aix-ppc64": "7.0.2", "@typescript/typescript-darwin-arm64": "7.0.2", "@typescript/typescript-darwin-x64": "7.0.2", "@typescript/typescript-freebsd-arm64": "7.0.2", "@typescript/typescript-freebsd-x64": "7.0.2", "@typescript/typescript-linux-arm": "7.0.2", "@typescript/typescript-linux-arm64": "7.0.2", "@typescript/typescript-linux-loong64": "7.0.2", "@typescript/typescript-linux-mips64el": "7.0.2", "@typescript/typescript-linux-ppc64": "7.0.2", "@typescript/typescript-linux-riscv64": "7.0.2", "@typescript/typescript-linux-s390x": "7.0.2", "@typescript/typescript-linux-x64": "7.0.2", "@typescript/typescript-netbsd-arm64": "7.0.2", "@typescript/typescript-netbsd-x64": "7.0.2", "@typescript/typescript-openbsd-arm64": "7.0.2", "@typescript/typescript-openbsd-x64": "7.0.2", "@typescript/typescript-sunos-x64": "7.0.2", "@typescript/typescript-win32-arm64": "7.0.2", "@typescript/typescript-win32-x64": "7.0.2" }, "bin": { "tsc": "bin/tsc" } }, "sha512-8FYau96o3NKOhbjKi/qNvG/W5jhzxkbdm5sj9AbZ/5T5sWqn3hJgLfGx27sRKZWTvyzCP8dLRBTf5tBTSRVUNA=="], + + "undici-types": ["undici-types@8.3.0", "", {}, "sha512-j375ScV60dom+YkPFIfTLcOiPxkN/buHz5GobjLhixFuANaNs3C9l4GmrWqejgXWJ7BbJcFYpTEUkS1Ge8bpZQ=="], + + "yaml": ["yaml@2.8.2", "", { "bin": { "yaml": "bin.mjs" } }, "sha512-mplynKqc1C2hTVYxd0PU2xQAc22TI1vShAYGksCCfxbn/dFwnHTNi1bvYsBTkhdUNtGIf5xNOg938rrSSYvS9A=="], + } +} diff --git a/conformance/README.md b/conformance/README.md new file mode 100644 index 0000000..e0e524b --- /dev/null +++ b/conformance/README.md @@ -0,0 +1,113 @@ +# Langfuse CLI OpenAPI conformance suite + +Language-neutral, version-pinned acceptance tests for the native TypeScript CLI. The oracle does not import the runtime request builder. + +## What is tested + +The primary test fake-calls every operation in every cataloged spec through the real CLI. For each operation it: + +- generates one minimally valid invocation from the untouched OpenAPI source +- starts a local mock HTTP server with a response generated from that operation +- runs the CLI as a subprocess against the mock host +- compares the received method, path, query, headers, authentication, and JSON body +- compares the CLI's response status, body, and exit status + +The black-box oracle does not share request-building code with the CLI. `bun test` currently attempts all 678 operations across all eight v3 snapshots using the historical field-flag adapter. The 18 operations that require lossless JSON bodies remain an explicit compatibility baseline; any additional failure fails the test. The native `contract-v1` adapter passes all 678 operations through `--body-json`. + +Supporting unit tests verify immutable spec hashes, valid sampling, serialization, naming, adapters, and the capture runner itself. + +OpenAPI cannot describe database setup, generated IDs, cross-request bindings, cleanup, licenses, or feature flags. Those stateful live workflows require a small reviewed scenario overlay; they are not silently invented by this generator. + +## Pinned specs + +| Langfuse | Paths | Operations | +|---|---:|---:| +| 3.0.0 | 29 | 39 | +| 3.50.0 | 44 | 68 | +| 3.100.0 | 49 | 77 | +| 3.150.0 | 55 | 86 | +| 3.176.0 | 60 | 96 | +| 3.200.0 | 61 | 98 | +| 3.212.0 | 64 | 101 | +| 3.216.0 | 69 | 113 | + +Each source file is downloaded by immutable commit and verified against the SHA-256 in `catalog.json`. Tests are network-free after sync. + +Langfuse 3.200.0, 3.212.0, and 3.216.0 use the JSON Schema `const` keyword while declaring OpenAPI 3.0.1. `swagger-parser` correctly reports those sources as invalid OAS 3.0 documents. The catalog records this as `oas3.0-const-keyword`; request sampling still preserves and tests the constraint with the independent JSON Schema validator. + +## Files + +```text +catalog.json immutable Git refs, commits, hashes, known source issues +policy.json implementation adapters; not API truth +specs//openapi.yml untouched upstream snapshots +src/ compiler, serializers, adapters, capture runner +tests/ compiler, validator, and runner tests +``` + +## Commands + +```sh +# Generator, schema, serializer, capture, and compatibility tests +bun test + +# Build and fake-call every endpoint through the lossless native CLI +bun run conformance:all + +# Re-download pinned bytes and verify their hashes +bun run conformance:sync +``` + +## CI gate + +GitHub Actions runs both commands for every pull request, merge queue entry, and push to `main`: + +```sh +bun test +bun run conformance:all +``` + +The required check name is **Test and verify OpenAPI conformance**. The interactive release script repeats the checks before building or publishing. + +## Run a focused current-CLI check + +```sh +bun run conformance:run -- \ + --version 3.212.0 \ + --adapter specli-v0 \ + --current-cli +``` + +The adapter name is retained because it describes the old field-flag grammar. The runner builds the native current source and compiles the selected untouched spec into a temporary runtime contract. + +Useful filters: + +```sh +--operation prompts_create +--max 20 +--fail-fast +``` + +Failures identify current limitations inline: raw union bodies, complex body flags, response exit codes, naming mismatches, or missing version selection. + +## Run the lossless native contract + +The native adapter uses lossless JSON body input via `--body-json`: + +```sh +bun run conformance:run -- \ + --version 3.216.0 \ + --adapter contract-v1 \ + -- bun bin/langfuse.mjs --api-version 3.216.0 +``` + +The command after the second `--` is treated as an external black-box executable. + +## Add a version + +1. Resolve the release tag to its immutable commit. +2. Add the tag, commit, and SHA-256 to `catalog.json`. +3. Run `bun run conformance:sync -- --version `. +4. Run `bun test` and `bun run conformance:all`. + +Never point a committed catalog entry at mutable `main` or `latest`. diff --git a/conformance/src/all.ts b/conformance/src/all.ts new file mode 100644 index 0000000..111335b --- /dev/null +++ b/conformance/src/all.ts @@ -0,0 +1,43 @@ +import { loadCatalog } from "./catalog"; +import { generateCorpus } from "./generator"; +import { runConformance } from "./runner"; + +const build = Bun.spawn(["bun", "run", "build"], { + cwd: import.meta.dir + "/../..", + stdout: "inherit", + stderr: "inherit", +}); +if ((await build.exited) !== 0) process.exit(1); + +const catalog = await loadCatalog(); +let passed = 0; +let total = 0; +let failed = false; + +for (const entry of catalog.versions) { + const corpus = await generateCorpus(entry); + const results = await runConformance({ + entry, + manifest: corpus.compiled.manifest, + vectors: corpus.vectors, + adapter: "contract-v1", + command: ["bun", "bin/langfuse.mjs", "--api-version", entry.version], + quiet: true, + }); + const versionPassed = results.filter((result) => result.passed).length; + passed += versionPassed; + total += results.length; + process.stdout.write(`${entry.version}: ${versionPassed}/${results.length}\n`); + + for (const result of results.filter((candidate) => !candidate.passed)) { + failed = true; + process.stderr.write(`FAIL ${result.id}\n`); + for (const failure of result.failures) { + process.stderr.write(` ${failure}\n`); + } + if (result.process.stderr) process.stderr.write(result.process.stderr); + } +} + +process.stdout.write(`Total: ${passed}/${total}\n`); +if (failed) process.exit(1); diff --git a/conformance/src/runner.ts b/conformance/src/runner.ts index 597ab9a..b228f49 100644 --- a/conformance/src/runner.ts +++ b/conformance/src/runner.ts @@ -1,4 +1,4 @@ -import { mkdir, mkdtemp, rm, symlink } from "node:fs/promises"; +import { mkdir, mkdtemp, rm } from "node:fs/promises"; import { tmpdir } from "node:os"; import { join, resolve } from "node:path"; @@ -9,6 +9,7 @@ import { } from "./adapters"; import { CaptureServer, requestDiff, sameJson } from "./capture"; import { POLICY_PATH, REPOSITORY_ROOT, readVerifiedSpec } from "./catalog"; +import { compileApiContract } from "../../src/contracts/compiler"; import type { CatalogEntry, ConformanceVector, @@ -95,11 +96,6 @@ async function currentCliCommand(entry: CatalogEntry): Promise<{ const bin = resolve(directory, "bin"); await mkdir(dist, { recursive: true }); await mkdir(bin, { recursive: true }); - await symlink( - resolve(REPOSITORY_ROOT, "node_modules"), - resolve(directory, "node_modules"), - "dir", - ); const build = Bun.spawn( [ "bun", @@ -108,7 +104,7 @@ async function currentCliCommand(entry: CatalogEntry): Promise<{ "--outfile", resolve(dist, "cli.js"), "--target", - "node", + "bun", "--format", "esm", ], @@ -125,7 +121,21 @@ async function currentCliCommand(entry: CatalogEntry): Promise<{ await rm(directory, { recursive: true, force: true }); throw new Error(`Current CLI build failed:\n${stdout}${stderr}`); } - await Bun.write(resolve(directory, "openapi.yml"), await readVerifiedSpec(entry)); + const raw = await readVerifiedSpec(entry); + const contracts = resolve(dist, "contracts"); + await mkdir(contracts, { recursive: true }); + await Bun.write( + resolve(contracts, `${entry.version}.json`), + `${JSON.stringify(compileApiContract(entry, raw))}\n`, + ); + await Bun.write( + resolve(contracts, "catalog.json"), + `${JSON.stringify({ + schemaVersion: 1, + latest: entry.version, + versions: [{ version: entry.version, sourceSha256: entry.sha256 }], + })}\n`, + ); await Bun.write( resolve(bin, "langfuse.mjs"), await Bun.file(resolve(REPOSITORY_ROOT, "bin/langfuse.mjs")).text(), diff --git a/openapi.yml b/openapi.yml deleted file mode 100644 index 8bd6144..0000000 --- a/openapi.yml +++ /dev/null @@ -1,14629 +0,0 @@ -openapi: 3.0.1 -info: - title: server - version: '' - description: >- - ## Authentication - - - Authenticate with the API using [Basic - Auth](https://en.wikipedia.org/wiki/Basic_access_authentication), get API - keys in the project settings: - - - - username: Langfuse Public Key - - - password: Langfuse Secret Key - - - ## Exports - - - - OpenAPI spec: https://cloud.langfuse.com/generated/api/openapi.yml -paths: - /api/public/annotation-queues: - get: - description: Get all annotation queues - operationId: annotationQueues_listQueues - tags: - - AnnotationQueues - parameters: - - name: page - in: query - description: page number, starts at 1 - required: false - schema: - type: integer - nullable: true - - name: limit - in: query - description: limit of items per page - required: false - schema: - type: integer - nullable: true - responses: - '200': - description: '' - content: - application/json: - schema: - $ref: '#/components/schemas/PaginatedAnnotationQueues' - '400': - description: '' - content: - application/json: - schema: {} - '401': - description: '' - content: - application/json: - schema: {} - '403': - description: '' - content: - application/json: - schema: {} - '404': - description: '' - content: - application/json: - schema: {} - '405': - description: '' - content: - application/json: - schema: {} - security: &ref_0 - - BasicAuth: [] - post: - description: Create an annotation queue - operationId: annotationQueues_createQueue - tags: - - AnnotationQueues - parameters: [] - responses: - '200': - description: '' - content: - application/json: - schema: - $ref: '#/components/schemas/AnnotationQueue' - '400': - description: '' - content: - application/json: - schema: {} - '401': - description: '' - content: - application/json: - schema: {} - '403': - description: '' - content: - application/json: - schema: {} - '404': - description: '' - content: - application/json: - schema: {} - '405': - description: '' - content: - application/json: - schema: {} - security: *ref_0 - requestBody: - required: true - content: - application/json: - schema: - $ref: '#/components/schemas/CreateAnnotationQueueRequest' - /api/public/annotation-queues/{queueId}: - get: - description: Get an annotation queue by ID - operationId: annotationQueues_getQueue - tags: - - AnnotationQueues - parameters: - - name: queueId - in: path - description: The unique identifier of the annotation queue - required: true - schema: - type: string - responses: - '200': - description: '' - content: - application/json: - schema: - $ref: '#/components/schemas/AnnotationQueue' - '400': - description: '' - content: - application/json: - schema: {} - '401': - description: '' - content: - application/json: - schema: {} - '403': - description: '' - content: - application/json: - schema: {} - '404': - description: '' - content: - application/json: - schema: {} - '405': - description: '' - content: - application/json: - schema: {} - security: *ref_0 - /api/public/annotation-queues/{queueId}/items: - get: - description: Get items for a specific annotation queue - operationId: annotationQueues_listQueueItems - tags: - - AnnotationQueues - parameters: - - name: queueId - in: path - description: The unique identifier of the annotation queue - required: true - schema: - type: string - - name: status - in: query - description: Filter by status - required: false - schema: - $ref: '#/components/schemas/AnnotationQueueStatus' - nullable: true - - name: page - in: query - description: page number, starts at 1 - required: false - schema: - type: integer - nullable: true - - name: limit - in: query - description: limit of items per page - required: false - schema: - type: integer - nullable: true - responses: - '200': - description: '' - content: - application/json: - schema: - $ref: '#/components/schemas/PaginatedAnnotationQueueItems' - '400': - description: '' - content: - application/json: - schema: {} - '401': - description: '' - content: - application/json: - schema: {} - '403': - description: '' - content: - application/json: - schema: {} - '404': - description: '' - content: - application/json: - schema: {} - '405': - description: '' - content: - application/json: - schema: {} - security: *ref_0 - post: - description: Add an item to an annotation queue - operationId: annotationQueues_createQueueItem - tags: - - AnnotationQueues - parameters: - - name: queueId - in: path - description: The unique identifier of the annotation queue - required: true - schema: - type: string - responses: - '200': - description: '' - content: - application/json: - schema: - $ref: '#/components/schemas/AnnotationQueueItem' - '400': - description: '' - content: - application/json: - schema: {} - '401': - description: '' - content: - application/json: - schema: {} - '403': - description: '' - content: - application/json: - schema: {} - '404': - description: '' - content: - application/json: - schema: {} - '405': - description: '' - content: - application/json: - schema: {} - security: *ref_0 - requestBody: - required: true - content: - application/json: - schema: - $ref: '#/components/schemas/CreateAnnotationQueueItemRequest' - /api/public/annotation-queues/{queueId}/items/{itemId}: - get: - description: Get a specific item from an annotation queue - operationId: annotationQueues_getQueueItem - tags: - - AnnotationQueues - parameters: - - name: queueId - in: path - description: The unique identifier of the annotation queue - required: true - schema: - type: string - - name: itemId - in: path - description: The unique identifier of the annotation queue item - required: true - schema: - type: string - responses: - '200': - description: '' - content: - application/json: - schema: - $ref: '#/components/schemas/AnnotationQueueItem' - '400': - description: '' - content: - application/json: - schema: {} - '401': - description: '' - content: - application/json: - schema: {} - '403': - description: '' - content: - application/json: - schema: {} - '404': - description: '' - content: - application/json: - schema: {} - '405': - description: '' - content: - application/json: - schema: {} - security: *ref_0 - patch: - description: Update an annotation queue item - operationId: annotationQueues_updateQueueItem - tags: - - AnnotationQueues - parameters: - - name: queueId - in: path - description: The unique identifier of the annotation queue - required: true - schema: - type: string - - name: itemId - in: path - description: The unique identifier of the annotation queue item - required: true - schema: - type: string - responses: - '200': - description: '' - content: - application/json: - schema: - $ref: '#/components/schemas/AnnotationQueueItem' - '400': - description: '' - content: - application/json: - schema: {} - '401': - description: '' - content: - application/json: - schema: {} - '403': - description: '' - content: - application/json: - schema: {} - '404': - description: '' - content: - application/json: - schema: {} - '405': - description: '' - content: - application/json: - schema: {} - security: *ref_0 - requestBody: - required: true - content: - application/json: - schema: - $ref: '#/components/schemas/UpdateAnnotationQueueItemRequest' - delete: - description: Remove an item from an annotation queue - operationId: annotationQueues_deleteQueueItem - tags: - - AnnotationQueues - parameters: - - name: queueId - in: path - description: The unique identifier of the annotation queue - required: true - schema: - type: string - - name: itemId - in: path - description: The unique identifier of the annotation queue item - required: true - schema: - type: string - responses: - '200': - description: '' - content: - application/json: - schema: - $ref: '#/components/schemas/DeleteAnnotationQueueItemResponse' - '400': - description: '' - content: - application/json: - schema: {} - '401': - description: '' - content: - application/json: - schema: {} - '403': - description: '' - content: - application/json: - schema: {} - '404': - description: '' - content: - application/json: - schema: {} - '405': - description: '' - content: - application/json: - schema: {} - security: *ref_0 - /api/public/annotation-queues/{queueId}/assignments: - post: - description: Create an assignment for a user to an annotation queue - operationId: annotationQueues_createQueueAssignment - tags: - - AnnotationQueues - parameters: - - name: queueId - in: path - description: The unique identifier of the annotation queue - required: true - schema: - type: string - responses: - '200': - description: '' - content: - application/json: - schema: - $ref: '#/components/schemas/CreateAnnotationQueueAssignmentResponse' - '400': - description: '' - content: - application/json: - schema: {} - '401': - description: '' - content: - application/json: - schema: {} - '403': - description: '' - content: - application/json: - schema: {} - '404': - description: '' - content: - application/json: - schema: {} - '405': - description: '' - content: - application/json: - schema: {} - security: *ref_0 - requestBody: - required: true - content: - application/json: - schema: - $ref: '#/components/schemas/AnnotationQueueAssignmentRequest' - delete: - description: Delete an assignment for a user to an annotation queue - operationId: annotationQueues_deleteQueueAssignment - tags: - - AnnotationQueues - parameters: - - name: queueId - in: path - description: The unique identifier of the annotation queue - required: true - schema: - type: string - responses: - '200': - description: '' - content: - application/json: - schema: - $ref: '#/components/schemas/DeleteAnnotationQueueAssignmentResponse' - '400': - description: '' - content: - application/json: - schema: {} - '401': - description: '' - content: - application/json: - schema: {} - '403': - description: '' - content: - application/json: - schema: {} - '404': - description: '' - content: - application/json: - schema: {} - '405': - description: '' - content: - application/json: - schema: {} - security: *ref_0 - requestBody: - required: true - content: - application/json: - schema: - $ref: '#/components/schemas/AnnotationQueueAssignmentRequest' - /api/public/integrations/blob-storage: - get: - description: >- - Get all blob storage integrations for the organization (requires - organization-scoped API key) - operationId: blobStorageIntegrations_getBlobStorageIntegrations - tags: - - BlobStorageIntegrations - parameters: [] - responses: - '200': - description: '' - content: - application/json: - schema: - $ref: '#/components/schemas/BlobStorageIntegrationsResponse' - '400': - description: '' - content: - application/json: - schema: {} - '401': - description: '' - content: - application/json: - schema: {} - '403': - description: '' - content: - application/json: - schema: {} - '404': - description: '' - content: - application/json: - schema: {} - '405': - description: '' - content: - application/json: - schema: {} - security: *ref_0 - put: - description: >- - Create or update a blob storage integration for a specific project - (requires organization-scoped API key). The configuration is validated - by performing a test upload to the bucket. - operationId: blobStorageIntegrations_upsertBlobStorageIntegration - tags: - - BlobStorageIntegrations - parameters: [] - responses: - '200': - description: '' - content: - application/json: - schema: - $ref: '#/components/schemas/BlobStorageIntegrationResponse' - '400': - description: '' - content: - application/json: - schema: {} - '401': - description: '' - content: - application/json: - schema: {} - '403': - description: '' - content: - application/json: - schema: {} - '404': - description: '' - content: - application/json: - schema: {} - '405': - description: '' - content: - application/json: - schema: {} - security: *ref_0 - requestBody: - required: true - content: - application/json: - schema: - $ref: '#/components/schemas/CreateBlobStorageIntegrationRequest' - /api/public/integrations/blob-storage/{id}: - get: - description: >- - Get the sync status of a blob storage integration by integration ID - (requires organization-scoped API key) - operationId: blobStorageIntegrations_getBlobStorageIntegrationStatus - tags: - - BlobStorageIntegrations - parameters: - - name: id - in: path - required: true - schema: - type: string - responses: - '200': - description: '' - content: - application/json: - schema: - $ref: '#/components/schemas/BlobStorageIntegrationStatusResponse' - '400': - description: '' - content: - application/json: - schema: {} - '401': - description: '' - content: - application/json: - schema: {} - '403': - description: '' - content: - application/json: - schema: {} - '404': - description: '' - content: - application/json: - schema: {} - '405': - description: '' - content: - application/json: - schema: {} - security: *ref_0 - delete: - description: >- - Delete a blob storage integration by ID (requires organization-scoped - API key) - operationId: blobStorageIntegrations_deleteBlobStorageIntegration - tags: - - BlobStorageIntegrations - parameters: - - name: id - in: path - required: true - schema: - type: string - responses: - '200': - description: '' - content: - application/json: - schema: - $ref: '#/components/schemas/BlobStorageIntegrationDeletionResponse' - '400': - description: '' - content: - application/json: - schema: {} - '401': - description: '' - content: - application/json: - schema: {} - '403': - description: '' - content: - application/json: - schema: {} - '404': - description: '' - content: - application/json: - schema: {} - '405': - description: '' - content: - application/json: - schema: {} - security: *ref_0 - /api/public/comments: - post: - description: >- - Create a comment. Comments may be attached to different object types - (trace, observation, session, prompt). - operationId: comments_create - tags: - - Comments - parameters: [] - responses: - '200': - description: '' - content: - application/json: - schema: - $ref: '#/components/schemas/CreateCommentResponse' - '400': - description: '' - content: - application/json: - schema: {} - '401': - description: '' - content: - application/json: - schema: {} - '403': - description: '' - content: - application/json: - schema: {} - '404': - description: '' - content: - application/json: - schema: {} - '405': - description: '' - content: - application/json: - schema: {} - security: *ref_0 - requestBody: - required: true - content: - application/json: - schema: - $ref: '#/components/schemas/CreateCommentRequest' - get: - description: Get all comments - operationId: comments_get - tags: - - Comments - parameters: - - name: page - in: query - description: Page number, starts at 1. - required: false - schema: - type: integer - nullable: true - - name: limit - in: query - description: >- - Limit of items per page. If you encounter api issues due to too - large page sizes, try to reduce the limit - required: false - schema: - type: integer - nullable: true - - name: objectType - in: query - description: >- - Filter comments by object type (trace, observation, session, prompt). - required: false - schema: - type: string - nullable: true - - name: objectId - in: query - description: >- - Filter comments by object id. If objectType is not provided, an - error will be thrown. - required: false - schema: - type: string - nullable: true - - name: authorUserId - in: query - description: Filter comments by author user id. - required: false - schema: - type: string - nullable: true - responses: - '200': - description: '' - content: - application/json: - schema: - $ref: '#/components/schemas/GetCommentsResponse' - '400': - description: '' - content: - application/json: - schema: {} - '401': - description: '' - content: - application/json: - schema: {} - '403': - description: '' - content: - application/json: - schema: {} - '404': - description: '' - content: - application/json: - schema: {} - '405': - description: '' - content: - application/json: - schema: {} - security: *ref_0 - /api/public/comments/{commentId}: - get: - description: Get a comment by id - operationId: comments_get-by-id - tags: - - Comments - parameters: - - name: commentId - in: path - description: The unique langfuse identifier of a comment - required: true - schema: - type: string - responses: - '200': - description: '' - content: - application/json: - schema: - $ref: '#/components/schemas/Comment' - '400': - description: '' - content: - application/json: - schema: {} - '401': - description: '' - content: - application/json: - schema: {} - '403': - description: '' - content: - application/json: - schema: {} - '404': - description: '' - content: - application/json: - schema: {} - '405': - description: '' - content: - application/json: - schema: {} - security: *ref_0 - /api/public/dataset-items: - post: - description: Create a dataset item - operationId: datasetItems_create - tags: - - DatasetItems - parameters: [] - responses: - '200': - description: '' - content: - application/json: - schema: - $ref: '#/components/schemas/DatasetItem' - '400': - description: '' - content: - application/json: - schema: {} - '401': - description: '' - content: - application/json: - schema: {} - '403': - description: '' - content: - application/json: - schema: {} - '404': - description: '' - content: - application/json: - schema: {} - '405': - description: '' - content: - application/json: - schema: {} - security: *ref_0 - requestBody: - required: true - content: - application/json: - schema: - $ref: '#/components/schemas/CreateDatasetItemRequest' - get: - description: >- - Get dataset items. Optionally specify a version to get the items as they - existed at that point in time. - - Note: If version parameter is provided, datasetName must also be - provided. - operationId: datasetItems_list - tags: - - DatasetItems - parameters: - - name: datasetName - in: query - required: false - schema: - type: string - nullable: true - - name: sourceTraceId - in: query - required: false - schema: - type: string - nullable: true - - name: sourceObservationId - in: query - required: false - schema: - type: string - nullable: true - - name: version - in: query - description: >- - ISO 8601 timestamp (RFC 3339, Section 5.6) in UTC (e.g., - "2026-01-21T14:35:42Z"). - - If provided, returns state of dataset at this timestamp. - - If not provided, returns the latest version. Requires datasetName to - be specified. - required: false - schema: - type: string - format: date-time - nullable: true - - name: page - in: query - description: page number, starts at 1 - required: false - schema: - type: integer - nullable: true - - name: limit - in: query - description: limit of items per page - required: false - schema: - type: integer - nullable: true - responses: - '200': - description: '' - content: - application/json: - schema: - $ref: '#/components/schemas/PaginatedDatasetItems' - '400': - description: '' - content: - application/json: - schema: {} - '401': - description: '' - content: - application/json: - schema: {} - '403': - description: '' - content: - application/json: - schema: {} - '404': - description: '' - content: - application/json: - schema: {} - '405': - description: '' - content: - application/json: - schema: {} - security: *ref_0 - /api/public/dataset-items/{id}: - get: - description: Get a dataset item - operationId: datasetItems_get - tags: - - DatasetItems - parameters: - - name: id - in: path - required: true - schema: - type: string - responses: - '200': - description: '' - content: - application/json: - schema: - $ref: '#/components/schemas/DatasetItem' - '400': - description: '' - content: - application/json: - schema: {} - '401': - description: '' - content: - application/json: - schema: {} - '403': - description: '' - content: - application/json: - schema: {} - '404': - description: '' - content: - application/json: - schema: {} - '405': - description: '' - content: - application/json: - schema: {} - security: *ref_0 - delete: - description: >- - Delete a dataset item and all its run items. This action is irreversible. - operationId: datasetItems_delete - tags: - - DatasetItems - parameters: - - name: id - in: path - required: true - schema: - type: string - responses: - '200': - description: '' - content: - application/json: - schema: - $ref: '#/components/schemas/DeleteDatasetItemResponse' - '400': - description: '' - content: - application/json: - schema: {} - '401': - description: '' - content: - application/json: - schema: {} - '403': - description: '' - content: - application/json: - schema: {} - '404': - description: '' - content: - application/json: - schema: {} - '405': - description: '' - content: - application/json: - schema: {} - security: *ref_0 - /api/public/dataset-run-items: - post: - description: Create a dataset run item - operationId: datasetRunItems_create - tags: - - DatasetRunItems - parameters: [] - responses: - '200': - description: '' - content: - application/json: - schema: - $ref: '#/components/schemas/DatasetRunItem' - '400': - description: '' - content: - application/json: - schema: {} - '401': - description: '' - content: - application/json: - schema: {} - '403': - description: '' - content: - application/json: - schema: {} - '404': - description: '' - content: - application/json: - schema: {} - '405': - description: '' - content: - application/json: - schema: {} - security: *ref_0 - requestBody: - required: true - content: - application/json: - schema: - $ref: '#/components/schemas/CreateDatasetRunItemRequest' - get: - description: List dataset run items - operationId: datasetRunItems_list - tags: - - DatasetRunItems - parameters: - - name: datasetId - in: query - required: true - schema: - type: string - - name: runName - in: query - required: true - schema: - type: string - - name: page - in: query - description: page number, starts at 1 - required: false - schema: - type: integer - nullable: true - - name: limit - in: query - description: limit of items per page - required: false - schema: - type: integer - nullable: true - responses: - '200': - description: '' - content: - application/json: - schema: - $ref: '#/components/schemas/PaginatedDatasetRunItems' - '400': - description: '' - content: - application/json: - schema: {} - '401': - description: '' - content: - application/json: - schema: {} - '403': - description: '' - content: - application/json: - schema: {} - '404': - description: '' - content: - application/json: - schema: {} - '405': - description: '' - content: - application/json: - schema: {} - security: *ref_0 - /api/public/v2/datasets: - get: - description: Get all datasets - operationId: datasets_list - tags: - - Datasets - parameters: - - name: page - in: query - description: page number, starts at 1 - required: false - schema: - type: integer - nullable: true - - name: limit - in: query - description: limit of items per page - required: false - schema: - type: integer - nullable: true - responses: - '200': - description: '' - content: - application/json: - schema: - $ref: '#/components/schemas/PaginatedDatasets' - '400': - description: '' - content: - application/json: - schema: {} - '401': - description: '' - content: - application/json: - schema: {} - '403': - description: '' - content: - application/json: - schema: {} - '404': - description: '' - content: - application/json: - schema: {} - '405': - description: '' - content: - application/json: - schema: {} - security: *ref_0 - post: - description: Create a dataset - operationId: datasets_create - tags: - - Datasets - parameters: [] - responses: - '200': - description: '' - content: - application/json: - schema: - $ref: '#/components/schemas/Dataset' - '400': - description: '' - content: - application/json: - schema: {} - '401': - description: '' - content: - application/json: - schema: {} - '403': - description: '' - content: - application/json: - schema: {} - '404': - description: '' - content: - application/json: - schema: {} - '405': - description: '' - content: - application/json: - schema: {} - security: *ref_0 - requestBody: - required: true - content: - application/json: - schema: - $ref: '#/components/schemas/CreateDatasetRequest' - /api/public/v2/datasets/{datasetName}: - get: - description: Get a dataset - operationId: datasets_get - tags: - - Datasets - parameters: - - name: datasetName - in: path - required: true - schema: - type: string - responses: - '200': - description: '' - content: - application/json: - schema: - $ref: '#/components/schemas/Dataset' - '400': - description: '' - content: - application/json: - schema: {} - '401': - description: '' - content: - application/json: - schema: {} - '403': - description: '' - content: - application/json: - schema: {} - '404': - description: '' - content: - application/json: - schema: {} - '405': - description: '' - content: - application/json: - schema: {} - security: *ref_0 - /api/public/datasets/{datasetName}/runs/{runName}: - get: - description: Get a dataset run and its items - operationId: datasets_getRun - tags: - - Datasets - parameters: - - name: datasetName - in: path - required: true - schema: - type: string - - name: runName - in: path - required: true - schema: - type: string - responses: - '200': - description: '' - content: - application/json: - schema: - $ref: '#/components/schemas/DatasetRunWithItems' - '400': - description: '' - content: - application/json: - schema: {} - '401': - description: '' - content: - application/json: - schema: {} - '403': - description: '' - content: - application/json: - schema: {} - '404': - description: '' - content: - application/json: - schema: {} - '405': - description: '' - content: - application/json: - schema: {} - security: *ref_0 - delete: - description: Delete a dataset run and all its run items. This action is irreversible. - operationId: datasets_deleteRun - tags: - - Datasets - parameters: - - name: datasetName - in: path - required: true - schema: - type: string - - name: runName - in: path - required: true - schema: - type: string - responses: - '200': - description: '' - content: - application/json: - schema: - $ref: '#/components/schemas/DeleteDatasetRunResponse' - '400': - description: '' - content: - application/json: - schema: {} - '401': - description: '' - content: - application/json: - schema: {} - '403': - description: '' - content: - application/json: - schema: {} - '404': - description: '' - content: - application/json: - schema: {} - '405': - description: '' - content: - application/json: - schema: {} - security: *ref_0 - /api/public/datasets/{datasetName}/runs: - get: - description: Get dataset runs - operationId: datasets_getRuns - tags: - - Datasets - parameters: - - name: datasetName - in: path - required: true - schema: - type: string - - name: page - in: query - description: page number, starts at 1 - required: false - schema: - type: integer - nullable: true - - name: limit - in: query - description: limit of items per page - required: false - schema: - type: integer - nullable: true - responses: - '200': - description: '' - content: - application/json: - schema: - $ref: '#/components/schemas/PaginatedDatasetRuns' - '400': - description: '' - content: - application/json: - schema: {} - '401': - description: '' - content: - application/json: - schema: {} - '403': - description: '' - content: - application/json: - schema: {} - '404': - description: '' - content: - application/json: - schema: {} - '405': - description: '' - content: - application/json: - schema: {} - security: *ref_0 - /api/public/experiments: - get: - description: |- - List experiments with cursor-based pagination. Results are ordered by - latest experiment activity descending. - operationId: experiments_list - tags: - - Experiments - parameters: - - name: fields - in: query - description: |- - Comma-separated list of field groups to include. Available groups: - `core`, `metadata`, `scores`. If omitted, `core` is returned. - required: false - schema: - type: string - nullable: true - - name: limit - in: query - description: Number of experiments to return per page. Maximum 100, default 50. - required: false - schema: - type: integer - nullable: true - - name: scoreLimit - in: query - description: >- - Number of scores to return per experiment when `fields=scores` is - requested. Maximum 50, default 50. - required: false - schema: - type: integer - nullable: true - - name: cursor - in: query - description: Versioned base64url cursor from the previous response page. - required: false - schema: - type: string - nullable: true - - name: fromStartTime - in: query - description: Retrieve only experiments on or after this datetime. - required: true - schema: - type: string - format: date-time - - name: toStartTime - in: query - description: Retrieve only experiments before this datetime. - required: false - schema: - type: string - format: date-time - nullable: true - - name: id - in: query - description: Comma-separated list of experiment IDs. - required: false - schema: - type: string - nullable: true - - name: name - in: query - description: Comma-separated list of experiment names. - required: false - schema: - type: string - nullable: true - - name: datasetId - in: query - description: Comma-separated list of dataset IDs. - required: false - schema: - type: string - nullable: true - - name: filter - in: query - description: |- - JSON string containing an array of structured filter conditions. - Supported columns are `id`, `name`, and `datasetId`. - required: false - schema: - type: string - nullable: true - responses: - '200': - description: '' - content: - application/json: - schema: - $ref: '#/components/schemas/ExperimentsResponse' - '400': - description: '' - content: - application/json: - schema: {} - '401': - description: '' - content: - application/json: - schema: {} - '403': - description: '' - content: - application/json: - schema: {} - '404': - description: '' - content: - application/json: - schema: {} - '405': - description: '' - content: - application/json: - schema: {} - security: *ref_0 - /api/public/experiment-items: - get: - description: |- - List experiment items with cursor-based pagination. Use this endpoint - to export experiment item inputs, outputs, expected outputs, metadata, - and optionally item/trace scores. Results are ordered by time - descending. - operationId: experiments_listItems - tags: - - Experiments - parameters: - - name: fields - in: query - description: |- - Comma-separated list of field groups to include. Available groups: - `core`, `dataset`, `io`, `metadata`, `itemMetadata`, - `experimentMetadata`, `scores`. If omitted, `core,dataset` is - returned. - required: false - schema: - type: string - nullable: true - - name: limit - in: query - description: >- - Number of experiment items to return per page. Maximum 100, default - 50. - required: false - schema: - type: integer - nullable: true - - name: scoreLimit - in: query - description: >- - Number of scores to return per experiment item when `fields=scores` - is requested. Maximum 50, default 50. - required: false - schema: - type: integer - nullable: true - - name: cursor - in: query - description: Versioned base64url cursor from the previous response page. - required: false - schema: - type: string - nullable: true - - name: fromStartTime - in: query - description: Retrieve only experiment items started on or after this datetime. - required: true - schema: - type: string - format: date-time - - name: toStartTime - in: query - description: Retrieve only experiment items started before this datetime. - required: false - schema: - type: string - format: date-time - nullable: true - - name: experimentId - in: query - description: Comma-separated list of experiment IDs. - required: false - schema: - type: string - nullable: true - - name: experimentName - in: query - description: Comma-separated list of experiment names. - required: false - schema: - type: string - nullable: true - - name: experimentItemId - in: query - description: Comma-separated list of experiment item IDs. - required: false - schema: - type: string - nullable: true - - name: datasetId - in: query - description: Comma-separated list of dataset IDs. - required: false - schema: - type: string - nullable: true - - name: filter - in: query - description: |- - JSON string containing an array of structured filter conditions. - Supported columns are `experimentId`, `experimentName`, - `experimentItemId`, and `datasetId`. - required: false - schema: - type: string - nullable: true - responses: - '200': - description: '' - content: - application/json: - schema: - $ref: '#/components/schemas/ExperimentItemsResponse' - '400': - description: '' - content: - application/json: - schema: {} - '401': - description: '' - content: - application/json: - schema: {} - '403': - description: '' - content: - application/json: - schema: {} - '404': - description: '' - content: - application/json: - schema: {} - '405': - description: '' - content: - application/json: - schema: {} - security: *ref_0 - /api/public/health: - get: - description: Check health of API and database - operationId: health_health - tags: - - Health - parameters: [] - responses: - '200': - description: '' - content: - application/json: - schema: - $ref: '#/components/schemas/HealthResponse' - '400': - description: '' - content: - application/json: - schema: {} - '401': - description: '' - content: - application/json: - schema: {} - '403': - description: '' - content: - application/json: - schema: {} - '404': - description: '' - content: - application/json: - schema: {} - '405': - description: '' - content: - application/json: - schema: {} - '503': - description: '' - /api/public/ingestion: - post: - description: >- - **Legacy endpoint for batch ingestion for Langfuse Observability.** - - - -> Please use the OpenTelemetry endpoint (`/api/public/otel/v1/traces`). - Learn more: https://langfuse.com/integrations/native/opentelemetry - - - Within each batch, there can be multiple events. - - Each event has a type, an id, a timestamp, metadata and a body. - - Internally, we refer to this as the "event envelope" as it tells us - something about the event but not the trace. - - We use the event id within this envelope to deduplicate messages to - avoid processing the same event twice, i.e. the event id should be - unique per request. - - The event.body.id is the ID of the actual trace and will be used for - updates and will be visible within the Langfuse App. - - I.e. if you want to update a trace, you'd use the same body id, but - separate event IDs. - - - Notes: - - - Introduction to data model: - https://langfuse.com/docs/observability/data-model - - - Batch sizes are limited to 3.5 MB in total. You need to adjust the - number of events per batch accordingly. - - - The API does not return a 4xx status code for input errors. Instead, - it responds with a 207 status code, which includes a list of the - encountered errors. - operationId: ingestion_batch - tags: - - Ingestion - parameters: [] - responses: - '207': - description: '' - content: - application/json: - schema: - $ref: '#/components/schemas/IngestionResponse' - '400': - description: '' - content: - application/json: - schema: {} - '401': - description: '' - content: - application/json: - schema: {} - '403': - description: '' - content: - application/json: - schema: {} - '404': - description: '' - content: - application/json: - schema: {} - '405': - description: '' - content: - application/json: - schema: {} - security: *ref_0 - requestBody: - required: true - content: - application/json: - schema: - type: object - properties: - batch: - type: array - items: - $ref: '#/components/schemas/IngestionEvent' - description: >- - Batch of tracing events to be ingested. Discriminated by - attribute `type`. - metadata: - nullable: true - description: >- - Optional. Metadata field used by the Langfuse SDKs for - debugging. - required: - - batch - /api/public/metrics: - get: - description: >- - Get metrics from the Langfuse project using a query object. - - - Consider using the [v2 metrics - endpoint](/api-reference#tag/metricsv2/GET/api/public/v2/metrics) for - better performance. - - - For more details, see the [Metrics API - documentation](https://langfuse.com/docs/metrics/features/metrics-api). - operationId: legacy_metricsV1_metrics - tags: - - LegacyMetricsV1 - parameters: - - name: query - in: query - description: >- - JSON string containing the query parameters with the following - structure: - - ```json - - { - "view": string, // Required. One of "traces", "observations", "scores-numeric", "scores-categorical" - "dimensions": [ // Optional. Default: [] - { - "field": string // Field to group by, e.g. "name", "userId", "sessionId" - } - ], - "metrics": [ // Required. At least one metric must be provided - { - "measure": string, // What to measure, e.g. "count", "latency", "value" - "aggregation": string // How to aggregate, e.g. "count", "sum", "avg", "p95", "histogram" - } - ], - "filters": [ // Optional. Default: [] - { - "column": string, // Column to filter on - "operator": string, // Operator, e.g. "=", ">", "<", "contains" - "value": any, // Value to compare against - "type": string, // Data type, e.g. "string", "number", "stringObject" - "key": string // Required only when filtering on metadata - } - ], - "timeDimension": { // Optional. Default: null. If provided, results will be grouped by time - "granularity": string // One of "minute", "hour", "day", "week", "month", "auto" - }, - "fromTimestamp": string, // Required. ISO datetime string for start of time range - "toTimestamp": string, // Required. ISO datetime string for end of time range - "orderBy": [ // Optional. Default: null - { - "field": string, // Field to order by - "direction": string // "asc" or "desc" - } - ], - "config": { // Optional. Query-specific configuration - "bins": number, // Optional. Number of bins for histogram (1-100), default: 10 - "row_limit": number // Optional. Row limit for results (1-1000) - } - } - - ``` - required: true - schema: - type: string - responses: - '200': - description: '' - content: - application/json: - schema: - $ref: '#/components/schemas/legacyMetricsResponse' - '400': - description: '' - content: - application/json: - schema: {} - '401': - description: '' - content: - application/json: - schema: {} - '403': - description: '' - content: - application/json: - schema: {} - '404': - description: '' - content: - application/json: - schema: {} - '405': - description: '' - content: - application/json: - schema: {} - security: *ref_0 - /api/public/observations/{observationId}: - get: - description: Get a observation - operationId: legacy_observationsV1_get - tags: - - LegacyObservationsV1 - parameters: - - name: observationId - in: path - description: >- - The unique langfuse identifier of an observation, can be an event, - span or generation - required: true - schema: - type: string - responses: - '200': - description: '' - content: - application/json: - schema: - $ref: '#/components/schemas/ObservationsView' - '400': - description: '' - content: - application/json: - schema: {} - '401': - description: '' - content: - application/json: - schema: {} - '403': - description: '' - content: - application/json: - schema: {} - '404': - description: '' - content: - application/json: - schema: {} - '405': - description: '' - content: - application/json: - schema: {} - security: *ref_0 - /api/public/observations: - get: - description: >- - Get a list of observations. - - - Consider using the [v2 observations - endpoint](/api-reference#tag/observationsv2/GET/api/public/v2/observations) - for cursor-based pagination and field selection. - operationId: legacy_observationsV1_getMany - tags: - - LegacyObservationsV1 - parameters: - - name: page - in: query - description: Page number, starts at 1. - required: false - schema: - type: integer - nullable: true - - name: limit - in: query - description: >- - Limit of items per page. If you encounter api issues due to too - large page sizes, try to reduce the limit. - required: false - schema: - type: integer - nullable: true - - name: name - in: query - required: false - schema: - type: string - nullable: true - - name: userId - in: query - required: false - schema: - type: string - nullable: true - - name: type - in: query - required: false - schema: - type: string - nullable: true - - name: traceId - in: query - required: false - schema: - type: string - nullable: true - - name: level - in: query - description: >- - Optional filter for observations with a specific level (e.g. - "DEBUG", "DEFAULT", "WARNING", "ERROR"). - required: false - schema: - $ref: '#/components/schemas/ObservationLevel' - nullable: true - - name: parentObservationId - in: query - required: false - schema: - type: string - nullable: true - - name: environment - in: query - description: >- - Optional filter for observations where the environment is one of the - provided values. - required: false - schema: - type: array - items: - type: string - nullable: true - - name: fromStartTime - in: query - description: >- - Retrieve only observations with a start_time on or after this - datetime (ISO 8601). - required: false - schema: - type: string - format: date-time - nullable: true - - name: toStartTime - in: query - description: >- - Retrieve only observations with a start_time before this datetime - (ISO 8601). - required: false - schema: - type: string - format: date-time - nullable: true - - name: version - in: query - description: Optional filter to only include observations with a certain version. - required: false - schema: - type: string - nullable: true - - name: filter - in: query - description: >- - JSON string containing an array of filter conditions. When provided, - this takes precedence over query parameter filters (userId, name, - type, level, environment, fromStartTime, ...). - - - ## Filter Structure - - Each filter condition has the following structure: - - ```json - - [ - { - "type": string, // Required. One of: "datetime", "string", "number", "stringOptions", "categoryOptions", "arrayOptions", "stringObject", "numberObject", "boolean", "null" - "column": string, // Required. Column to filter on (see available columns below) - "operator": string, // Required. Operator based on type: - // - datetime: ">", "<", ">=", "<=" - // - string: "=", "contains", "does not contain", "starts with", "ends with" - // - stringOptions: "any of", "none of" - // - categoryOptions: "any of", "none of" - // - arrayOptions: "any of", "none of", "all of" - // - number: "=", ">", "<", ">=", "<=" - // - stringObject: "=", "contains", "does not contain", "starts with", "ends with" - // - numberObject: "=", ">", "<", ">=", "<=" - // - boolean: "=", "<>" - // - null: "is null", "is not null" - "value": any, // Required (except for null type). Value to compare against. Type depends on filter type - "key": string // Required only for stringObject, numberObject, and categoryOptions types when filtering on nested fields like metadata - } - ] - - ``` - - - ## Available Columns - - - ### Core Observation Fields - - - `id` (string) - Observation ID - - - `type` (string) - Observation type (SPAN, GENERATION, EVENT) - - - `name` (string) - Observation name - - - `traceId` (string) - Associated trace ID - - - `startTime` (datetime) - Observation start time - - - `endTime` (datetime) - Observation end time - - - `environment` (string) - Environment tag - - - `level` (string) - Log level (DEBUG, DEFAULT, WARNING, ERROR) - - - `statusMessage` (string) - Status message - - - `version` (string) - Version tag - - - ### Performance Metrics - - - `latency` (number) - Latency in seconds (calculated: end_time - - start_time) - - - `timeToFirstToken` (number) - Time to first token in seconds - - - `tokensPerSecond` (number) - Output tokens per second - - - ### Token Usage - - - `inputTokens` (number) - Number of input tokens - - - `outputTokens` (number) - Number of output tokens - - - `totalTokens` (number) - Total tokens (alias: `tokens`) - - - ### Cost Metrics - - - `inputCost` (number) - Input cost in USD - - - `outputCost` (number) - Output cost in USD - - - `totalCost` (number) - Total cost in USD - - - ### Model Information - - - `model` (string) - Provided model name - - - `promptName` (string) - Associated prompt name - - - `promptVersion` (number) - Associated prompt version - - - ### Structured Data - - - `metadata` (stringObject/numberObject/categoryOptions) - Metadata - key-value pairs. Use `key` parameter to filter on specific metadata - keys. - - - ### Associated Trace Fields (requires join with traces table) - - - `userId` (string) - User ID from associated trace - - - `traceName` (string) - Name from associated trace - - - `traceEnvironment` (string) - Environment from associated trace - - - `traceTags` (arrayOptions) - Tags from associated trace - - - ## Filter Examples - - ```json - - [ - { - "type": "string", - "column": "type", - "operator": "=", - "value": "GENERATION" - }, - { - "type": "number", - "column": "latency", - "operator": ">=", - "value": 2.5 - }, - { - "type": "stringObject", - "column": "metadata", - "key": "environment", - "operator": "=", - "value": "production" - } - ] - - ``` - required: false - schema: - type: string - nullable: true - responses: - '200': - description: '' - content: - application/json: - schema: - $ref: '#/components/schemas/legacyObservationsViews' - '400': - description: '' - content: - application/json: - schema: {} - '401': - description: '' - content: - application/json: - schema: {} - '403': - description: '' - content: - application/json: - schema: {} - '404': - description: '' - content: - application/json: - schema: {} - '405': - description: '' - content: - application/json: - schema: {} - security: *ref_0 - /api/public/scores: - post: - description: Create a score (supports both trace and session scores) - operationId: legacy_scoreV1_create - tags: - - LegacyScoreV1 - parameters: [] - responses: - '200': - description: '' - content: - application/json: - schema: - $ref: '#/components/schemas/legacyCreateScoreResponse' - '400': - description: '' - content: - application/json: - schema: {} - '401': - description: '' - content: - application/json: - schema: {} - '403': - description: '' - content: - application/json: - schema: {} - '404': - description: '' - content: - application/json: - schema: {} - '405': - description: '' - content: - application/json: - schema: {} - security: *ref_0 - requestBody: - required: true - content: - application/json: - schema: - $ref: '#/components/schemas/legacyCreateScoreRequest' - /api/public/scores/{scoreId}: - delete: - description: Delete a score (supports both trace and session scores) - operationId: legacy_scoreV1_delete - tags: - - LegacyScoreV1 - parameters: - - name: scoreId - in: path - description: The unique langfuse identifier of a score - required: true - schema: - type: string - responses: - '204': - description: '' - '400': - description: '' - content: - application/json: - schema: {} - '401': - description: '' - content: - application/json: - schema: {} - '403': - description: '' - content: - application/json: - schema: {} - '404': - description: '' - content: - application/json: - schema: {} - '405': - description: '' - content: - application/json: - schema: {} - security: *ref_0 - /api/public/llm-connections: - get: - description: Get all LLM connections in a project - operationId: llmConnections_list - tags: - - LlmConnections - parameters: - - name: page - in: query - description: page number, starts at 1 - required: false - schema: - type: integer - nullable: true - - name: limit - in: query - description: limit of items per page - required: false - schema: - type: integer - nullable: true - responses: - '200': - description: '' - content: - application/json: - schema: - $ref: '#/components/schemas/PaginatedLlmConnections' - '400': - description: '' - content: - application/json: - schema: {} - '401': - description: '' - content: - application/json: - schema: {} - '403': - description: '' - content: - application/json: - schema: {} - '404': - description: '' - content: - application/json: - schema: {} - '405': - description: '' - content: - application/json: - schema: {} - security: *ref_0 - put: - description: >- - Create or update an LLM connection. The connection is upserted on - provider. - operationId: llmConnections_upsert - tags: - - LlmConnections - parameters: [] - responses: - '200': - description: '' - content: - application/json: - schema: - $ref: '#/components/schemas/LlmConnection' - '400': - description: '' - content: - application/json: - schema: {} - '401': - description: '' - content: - application/json: - schema: {} - '403': - description: '' - content: - application/json: - schema: {} - '404': - description: '' - content: - application/json: - schema: {} - '405': - description: '' - content: - application/json: - schema: {} - security: *ref_0 - requestBody: - required: true - content: - application/json: - schema: - $ref: '#/components/schemas/UpsertLlmConnectionRequest' - /api/public/llm-connections/{id}: - delete: - description: >- - Delete an LLM connection by id. Evaluators that depend on the deleted - connection are automatically paused. - operationId: llmConnections_delete - tags: - - LlmConnections - parameters: - - name: id - in: path - required: true - schema: - type: string - responses: - '200': - description: '' - content: - application/json: - schema: - $ref: '#/components/schemas/DeleteLlmConnectionResponse' - '400': - description: '' - content: - application/json: - schema: {} - '401': - description: '' - content: - application/json: - schema: {} - '403': - description: '' - content: - application/json: - schema: {} - '404': - description: '' - content: - application/json: - schema: {} - '405': - description: '' - content: - application/json: - schema: {} - security: *ref_0 - /api/public/media/{mediaId}: - get: - description: Get a media record - operationId: media_get - tags: - - Media - parameters: - - name: mediaId - in: path - description: The unique langfuse identifier of a media record - required: true - schema: - type: string - responses: - '200': - description: '' - content: - application/json: - schema: - $ref: '#/components/schemas/GetMediaResponse' - '400': - description: '' - content: - application/json: - schema: {} - '401': - description: '' - content: - application/json: - schema: {} - '403': - description: '' - content: - application/json: - schema: {} - '404': - description: '' - content: - application/json: - schema: {} - '405': - description: '' - content: - application/json: - schema: {} - security: *ref_0 - patch: - description: Patch a media record - operationId: media_patch - tags: - - Media - parameters: - - name: mediaId - in: path - description: The unique langfuse identifier of a media record - required: true - schema: - type: string - responses: - '204': - description: '' - '400': - description: '' - content: - application/json: - schema: {} - '401': - description: '' - content: - application/json: - schema: {} - '403': - description: '' - content: - application/json: - schema: {} - '404': - description: '' - content: - application/json: - schema: {} - '405': - description: '' - content: - application/json: - schema: {} - security: *ref_0 - requestBody: - required: true - content: - application/json: - schema: - $ref: '#/components/schemas/PatchMediaBody' - /api/public/media: - post: - description: Get a presigned upload URL for a media record - operationId: media_getUploadUrl - tags: - - Media - parameters: [] - responses: - '200': - description: '' - content: - application/json: - schema: - $ref: '#/components/schemas/GetMediaUploadUrlResponse' - '400': - description: '' - content: - application/json: - schema: {} - '401': - description: '' - content: - application/json: - schema: {} - '403': - description: '' - content: - application/json: - schema: {} - '404': - description: '' - content: - application/json: - schema: {} - '405': - description: '' - content: - application/json: - schema: {} - security: *ref_0 - requestBody: - required: true - content: - application/json: - schema: - $ref: '#/components/schemas/GetMediaUploadUrlRequest' - /api/public/v2/metrics: - get: - description: >- - Get metrics from the Langfuse project using a query object. V2 endpoint - with optimized performance. - - - ## V2 Differences - - - Supports `observations`, `scores-numeric`, and `scores-categorical` - views only (traces view not supported) - - - Direct access to tags and release fields on observations - - - Backwards-compatible: traceName, traceRelease, traceVersion dimensions - are still available on observations view - - - High cardinality dimensions are not supported and will return a 400 - error (see below) - - - For more details, see the [Metrics API - documentation](https://langfuse.com/docs/metrics/features/metrics-api). - - - ## Available Views - - - ### observations - - Query observation-level data (spans, generations, events). - - - **Dimensions:** - - - `environment` - Deployment environment (e.g., production, staging) - - - `type` - Type of observation (SPAN, GENERATION, EVENT) - - - `name` - Name of the observation - - - `level` - Logging level of the observation - - - `version` - Version of the observation - - - `tags` - User-defined tags - - - `release` - Release version - - - `traceName` - Name of the parent trace (backwards-compatible) - - - `traceRelease` - Release version of the parent trace - (backwards-compatible, maps to release) - - - `traceVersion` - Version of the parent trace (backwards-compatible, - maps to version) - - - `providedModelName` - Name of the model used - - - `promptName` - Name of the prompt used - - - `promptVersion` - Version of the prompt used - - - `startTimeMonth` - Month of start_time in YYYY-MM format - - - **Measures:** - - - `count` - Total number of observations - - - `latency` - Observation latency (milliseconds) - - - `streamingLatency` - Generation latency from completion start to end - (milliseconds) - - - `inputTokens` - Sum of input tokens consumed - - - `outputTokens` - Sum of output tokens produced - - - `totalTokens` - Sum of all tokens consumed - - - `outputTokensPerSecond` - Output tokens per second - - - `tokensPerSecond` - Total tokens per second - - - `inputCost` - Input cost (USD) - - - `outputCost` - Output cost (USD) - - - `totalCost` - Total cost (USD) - - - `timeToFirstToken` - Time to first token (milliseconds) - - - `countScores` - Number of scores attached to the observation - - - ### scores-numeric - - Query numeric and boolean score data. - - - **Dimensions:** - - - `environment` - Deployment environment - - - `name` - Name of the score (e.g., accuracy, toxicity) - - - `source` - Origin of the score (API, ANNOTATION, EVAL) - - - `dataType` - Data type (NUMERIC, BOOLEAN) - - - `configId` - Identifier of the score config - - - `timestampMonth` - Month in YYYY-MM format - - - `timestampDay` - Day in YYYY-MM-DD format - - - `value` - Numeric value of the score - - - `traceName` - Name of the parent trace - - - `tags` - Tags - - - `traceRelease` - Release version - - - `traceVersion` - Version - - - `observationName` - Name of the associated observation - - - `observationModelName` - Model name of the associated observation - - - `observationPromptName` - Prompt name of the associated observation - - - `observationPromptVersion` - Prompt version of the associated - observation - - - **Measures:** - - - `count` - Total number of scores - - - `value` - Score value (for aggregations) - - - ### scores-categorical - - Query categorical score data. Same dimensions as scores-numeric except - uses `stringValue` instead of `value`. - - - **Measures:** - - - `count` - Total number of scores - - - ## High Cardinality Dimensions - - The following dimensions cannot be used as grouping dimensions in v2 - metrics API as they can cause performance issues. - - Use them in filters instead. - - - **observations view:** - - - `id` - Use traceId filter to narrow down results - - - `traceId` - Use traceId filter instead - - - `userId` - Use userId filter instead - - - `sessionId` - Use sessionId filter instead - - - `parentObservationId` - Use parentObservationId filter instead - - - **scores-numeric / scores-categorical views:** - - - `id` - Use specific filters to narrow down results - - - `traceId` - Use traceId filter instead - - - `userId` - Use userId filter instead - - - `sessionId` - Use sessionId filter instead - - - `observationId` - Use observationId filter instead - - - ## Aggregations - - Available aggregation functions: `sum`, `avg`, `count`, `max`, `min`, - `p50`, `p75`, `p90`, `p95`, `p99`, `histogram` - - - ## Time Granularities - - Available granularities for timeDimension: `auto`, `minute`, `hour`, - `day`, `week`, `month` - - - `auto` bins the data into approximately 50 buckets based on the time - range - operationId: metrics_metrics - tags: - - Metrics - parameters: - - name: query - in: query - description: >- - JSON string containing the query parameters with the following - structure: - - ```json - - { - "view": string, // Required. One of "observations", "scores-numeric", "scores-categorical" - "dimensions": [ // Optional. Default: [] - { - "field": string // Field to group by (see available dimensions above) - } - ], - "metrics": [ // Required. At least one metric must be provided - { - "measure": string, // What to measure (see available measures above) - "aggregation": string // How to aggregate: "sum", "avg", "count", "max", "min", "p50", "p75", "p90", "p95", "p99", "histogram" - } - ], - "filters": [ // Optional. Default: [] - { - "column": string, // Column to filter on (any dimension field) - "operator": string, // Operator based on type: - // - datetime: ">", "<", ">=", "<=" - // - string: "=", "contains", "does not contain", "starts with", "ends with" - // - stringOptions: "any of", "none of" - // - arrayOptions: "any of", "none of", "all of" - // - number: "=", ">", "<", ">=", "<=" - // - stringObject/numberObject: same as string/number with required "key" - // - boolean: "=", "<>" - // - null: "is null", "is not null" - "value": any, // Value to compare against - "type": string, // Data type: "datetime", "string", "number", "stringOptions", "categoryOptions", "arrayOptions", "stringObject", "numberObject", "boolean", "null" - "key": string // Required only for stringObject/numberObject types (e.g., metadata filtering) - } - ], - "timeDimension": { // Optional. Default: null. If provided, results will be grouped by time - "granularity": string // One of "auto", "minute", "hour", "day", "week", "month" - }, - "fromTimestamp": string, // Required. ISO datetime string for start of time range - "toTimestamp": string, // Required. ISO datetime string for end of time range (must be after fromTimestamp) - "orderBy": [ // Optional. Default: null - { - "field": string, // Field to order by (dimension or metric alias) - "direction": string // "asc" or "desc" - } - ], - "config": { // Optional. Query-specific configuration - "bins": number, // Optional. Number of bins for histogram aggregation (1-100), default: 10 - "row_limit": number // Optional. Maximum number of rows to return (1-1000), default: 100 - } - } - - ``` - required: true - schema: - type: string - responses: - '200': - description: '' - content: - application/json: - schema: - $ref: '#/components/schemas/MetricsV2Response' - '400': - description: '' - content: - application/json: - schema: {} - '401': - description: '' - content: - application/json: - schema: {} - '403': - description: '' - content: - application/json: - schema: {} - '404': - description: '' - content: - application/json: - schema: {} - '405': - description: '' - content: - application/json: - schema: {} - security: *ref_0 - /api/public/models: - post: - description: Create a model - operationId: models_create - tags: - - Models - parameters: [] - responses: - '200': - description: '' - content: - application/json: - schema: - $ref: '#/components/schemas/Model' - '400': - description: '' - content: - application/json: - schema: {} - '401': - description: '' - content: - application/json: - schema: {} - '403': - description: '' - content: - application/json: - schema: {} - '404': - description: '' - content: - application/json: - schema: {} - '405': - description: '' - content: - application/json: - schema: {} - security: *ref_0 - requestBody: - required: true - content: - application/json: - schema: - $ref: '#/components/schemas/CreateModelRequest' - get: - description: Get all models - operationId: models_list - tags: - - Models - parameters: - - name: page - in: query - description: page number, starts at 1 - required: false - schema: - type: integer - nullable: true - - name: limit - in: query - description: limit of items per page - required: false - schema: - type: integer - nullable: true - responses: - '200': - description: '' - content: - application/json: - schema: - $ref: '#/components/schemas/PaginatedModels' - '400': - description: '' - content: - application/json: - schema: {} - '401': - description: '' - content: - application/json: - schema: {} - '403': - description: '' - content: - application/json: - schema: {} - '404': - description: '' - content: - application/json: - schema: {} - '405': - description: '' - content: - application/json: - schema: {} - security: *ref_0 - /api/public/models/{id}: - get: - description: Get a model - operationId: models_get - tags: - - Models - parameters: - - name: id - in: path - required: true - schema: - type: string - responses: - '200': - description: '' - content: - application/json: - schema: - $ref: '#/components/schemas/Model' - '400': - description: '' - content: - application/json: - schema: {} - '401': - description: '' - content: - application/json: - schema: {} - '403': - description: '' - content: - application/json: - schema: {} - '404': - description: '' - content: - application/json: - schema: {} - '405': - description: '' - content: - application/json: - schema: {} - security: *ref_0 - delete: - description: >- - Delete a model. Cannot delete models managed by Langfuse. You can create - your own definition with the same modelName to override the definition - though. - operationId: models_delete - tags: - - Models - parameters: - - name: id - in: path - required: true - schema: - type: string - responses: - '204': - description: '' - '400': - description: '' - content: - application/json: - schema: {} - '401': - description: '' - content: - application/json: - schema: {} - '403': - description: '' - content: - application/json: - schema: {} - '404': - description: '' - content: - application/json: - schema: {} - '405': - description: '' - content: - application/json: - schema: {} - security: *ref_0 - /api/public/v2/observations: - get: - description: >- - Get a list of observations with cursor-based pagination and flexible - field selection. - - - ## Cursor-based Pagination - - This endpoint uses cursor-based pagination for efficient traversal of - large datasets. - - The cursor is returned in the response metadata and should be passed in - subsequent requests - - to retrieve the next page of results. - - - ## Field Selection - - Use the `fields` parameter to control which observation fields are - returned: - - - `core` - Always included: id, traceId, startTime, endTime, projectId, - parentObservationId, type - - - `basic` - name, level, statusMessage, version, environment, - bookmarked, public, userId, sessionId - - - `time` - completionStartTime, createdAt, updatedAt - - - `io` - input, output - - - `metadata` - metadata (truncated to 200 chars by default, use - `expandMetadata` to get full values) - - - `model` - providedModelName, internalModelId, modelParameters - - - `usage` - usageDetails, costDetails, totalCost, usagePricingTierName - - - `prompt` - promptId, promptName, promptVersion - - - `metrics` - latency, timeToFirstToken - - - `trace_context` - tags, release, traceName - - - If not specified, `core` and `basic` field groups are returned. - - - ## Filters - - Multiple filtering options are available via query parameters or the - structured `filter` parameter. - - When using the `filter` parameter, it takes precedence over individual - query parameter filters. - operationId: observations_getMany - tags: - - Observations - parameters: - - name: fields - in: query - description: >- - Comma-separated list of field groups to include in the response. - - Available groups: core, basic, time, io, metadata, model, usage, - prompt, metrics, trace_context. - - If not specified, `core` and `basic` field groups are returned. - - Example: "basic,usage,model" - required: false - schema: - type: string - nullable: true - - name: expandMetadata - in: query - description: |- - Comma-separated list of metadata keys to return non-truncated. - By default, metadata values over 200 characters are truncated. - Use this parameter to retrieve full values for specific keys. - Example: "key1,key2" - required: false - schema: - type: string - nullable: true - - name: limit - in: query - description: Number of items to return per page. Maximum 1000, default 50. - required: false - schema: - type: integer - nullable: true - - name: cursor - in: query - description: >- - Base64-encoded cursor for pagination. Use the cursor from the - previous response to get the next page. - required: false - schema: - type: string - nullable: true - - name: parseIoAsJson - in: query - description: |- - **Deprecated.** Setting this to `true` will return a 400 error. - Input/output fields are always returned as raw strings. - Remove this parameter or set it to `false`. - required: false - schema: - type: boolean - nullable: true - - name: name - in: query - required: false - schema: - type: string - nullable: true - - name: userId - in: query - required: false - schema: - type: string - nullable: true - - name: type - in: query - description: >- - Filter by observation type (e.g., "GENERATION", "SPAN", "EVENT", - "AGENT", "TOOL", "CHAIN", "RETRIEVER", "EVALUATOR", "EMBEDDING", - "GUARDRAIL") - required: false - schema: - type: string - nullable: true - - name: traceId - in: query - required: false - schema: - type: string - nullable: true - - name: level - in: query - description: >- - Optional filter for observations with a specific level (e.g. - "DEBUG", "DEFAULT", "WARNING", "ERROR"). - required: false - schema: - $ref: '#/components/schemas/ObservationLevel' - nullable: true - - name: parentObservationId - in: query - required: false - schema: - type: string - nullable: true - - name: environment - in: query - description: >- - Optional filter for observations where the environment is one of the - provided values. - required: false - schema: - type: array - items: - type: string - nullable: true - - name: fromStartTime - in: query - description: >- - Retrieve only observations with a start_time on or after this - datetime (ISO 8601). - required: false - schema: - type: string - format: date-time - nullable: true - - name: toStartTime - in: query - description: >- - Retrieve only observations with a start_time before this datetime - (ISO 8601). - required: false - schema: - type: string - format: date-time - nullable: true - - name: version - in: query - description: Optional filter to only include observations with a certain version. - required: false - schema: - type: string - nullable: true - - name: filter - in: query - description: >- - JSON string containing an array of filter conditions. When provided, - this takes precedence over query parameter filters (userId, name, - type, level, environment, fromStartTime, ...). - - - ## Filter Structure - - Each filter condition has the following structure: - - ```json - - [ - { - "type": string, // Required. One of: "datetime", "string", "number", "stringOptions", "categoryOptions", "arrayOptions", "stringObject", "numberObject", "boolean", "null" - "column": string, // Required. Column to filter on (see available columns below) - "operator": string, // Required. Operator based on type: - // - datetime: ">", "<", ">=", "<=" - // - string: "=", "contains", "does not contain", "starts with", "ends with", "matches" - // - stringOptions: "any of", "none of" - // - categoryOptions: "any of", "none of" - // - arrayOptions: "any of", "none of", "all of" - // - number: "=", ">", "<", ">=", "<=" - // - stringObject: "=", "contains", "does not contain", "starts with", "ends with", "matches" - // - numberObject: "=", ">", "<", ">=", "<=" - // - boolean: "=", "<>" - // - null: "is null", "is not null" - "value": any, // Required (except for null type). Value to compare against. Type depends on filter type - "key": string // Required only for stringObject, numberObject, and categoryOptions types when filtering on nested fields like metadata - } - ] - - ``` - - - ## Available Columns - - - ### Core Observation Fields - - - `id` (string) - Observation ID - - - `type` (string) - Observation type (SPAN, GENERATION, EVENT) - - - `name` (string) - Observation name - - - `traceId` (string) - Associated trace ID - - - `startTime` (datetime) - Observation start time - - - `endTime` (datetime) - Observation end time - - - `environment` (string) - Environment tag - - - `level` (string) - Log level (DEBUG, DEFAULT, WARNING, ERROR) - - - `statusMessage` (string) - Status message - - - `version` (string) - Version tag - - - `userId` (string) - User ID - - - `sessionId` (string) - Session ID - - - ### Trace-Related Fields - - - `traceName` (string) - Name of the parent trace - - - `traceTags` (arrayOptions) - Tags from the parent trace - - - `tags` (arrayOptions) - Alias for traceTags - - - ### Performance Metrics - - - `latency` (number) - Latency in seconds (calculated: end_time - - start_time) - - - `timeToFirstToken` (number) - Time to first token in seconds - - - `tokensPerSecond` (number) - Output tokens per second - - - ### Token Usage - - - `inputTokens` (number) - Number of input tokens - - - `outputTokens` (number) - Number of output tokens - - - `totalTokens` (number) - Total tokens (alias: `tokens`) - - - ### Cost Metrics - - - `inputCost` (number) - Input cost in USD - - - `outputCost` (number) - Output cost in USD - - - `totalCost` (number) - Total cost in USD - - - ### Model Information - - - `model` (string) - Provided model name (alias: - `providedModelName`) - - - `promptName` (string) - Associated prompt name - - - `promptVersion` (number) - Associated prompt version - - - ### Structured Data - - - `input` (string) - Observation input. Supports accelerated indexed - literal search with the `matches` operator. - - - `output` (string) - Observation output. Supports accelerated - indexed literal search with the `matches` operator. - - - `metadata` (stringObject/numberObject/categoryOptions) - Metadata - key-value pairs. Use `key` parameter to filter on specific metadata - keys. - - - The `matches` operator is only supported for `input`, `output`, and - stringObject `metadata` filters. It performs indexed literal search - with token-boundary pruning using the events table text indexes. - Case sensitivity differs by target: `input` and `output` matches are - case-insensitive, while metadata value matches are case-sensitive. - Unlike SQL `LIKE`, `%` and `_` are treated as literal characters. - Use `contains` for legacy substring semantics where the API allows - it. Any v2 `input` or `output` filter must be accompanied by at - least one `=` or `matches` filter on `input` or `output`; standalone - `contains`, `starts with`, `ends with`, and `does not contain` - filters on these columns are rejected. - - - ## Filter Examples - - ```json - - [ - { - "type": "string", - "column": "type", - "operator": "=", - "value": "GENERATION" - }, - { - "type": "number", - "column": "latency", - "operator": ">=", - "value": 2.5 - }, - { - "type": "stringObject", - "column": "metadata", - "key": "environment", - "operator": "=", - "value": "production" - }, - { - "type": "string", - "column": "output", - "operator": "matches", - "value": "needle" - } - ] - - ``` - required: false - schema: - type: string - nullable: true - responses: - '200': - description: '' - content: - application/json: - schema: - $ref: '#/components/schemas/ObservationsV2Response' - '400': - description: '' - content: - application/json: - schema: {} - '401': - description: '' - content: - application/json: - schema: {} - '403': - description: '' - content: - application/json: - schema: {} - '404': - description: '' - content: - application/json: - schema: {} - '405': - description: '' - content: - application/json: - schema: {} - security: *ref_0 - /api/public/otel/v1/traces: - post: - description: >- - **OpenTelemetry Traces Ingestion Endpoint** - - - This endpoint implements the OTLP/HTTP specification for trace - ingestion, providing native OpenTelemetry integration for Langfuse - Observability. - - - **Supported Formats:** - - - Binary Protobuf: `Content-Type: application/x-protobuf` - - - JSON Protobuf: `Content-Type: application/json` - - - Supports gzip compression via `Content-Encoding: gzip` header - - - **Specification Compliance:** - - - Conforms to [OTLP/HTTP Trace - Export](https://opentelemetry.io/docs/specs/otlp/#otlphttp) - - - Implements `ExportTraceServiceRequest` message format - - - **Documentation:** - - - Integration guide: - https://langfuse.com/integrations/native/opentelemetry - - - Data model: https://langfuse.com/docs/observability/data-model - operationId: opentelemetry_exportTraces - tags: - - Opentelemetry - parameters: [] - responses: - '200': - description: '' - content: - application/json: - schema: - $ref: '#/components/schemas/OtelTraceResponse' - '400': - description: '' - content: - application/json: - schema: {} - '401': - description: '' - content: - application/json: - schema: {} - '403': - description: '' - content: - application/json: - schema: {} - '404': - description: '' - content: - application/json: - schema: {} - '405': - description: '' - content: - application/json: - schema: {} - security: *ref_0 - requestBody: - required: true - content: - application/json: - schema: - type: object - properties: - resourceSpans: - type: array - items: - $ref: '#/components/schemas/OtelResourceSpan' - description: >- - Array of resource spans containing trace data as defined in - the OTLP specification - required: - - resourceSpans - /api/public/organizations/memberships: - get: - description: >- - Get all memberships for the organization associated with the API key - (requires organization-scoped API key) - operationId: organizations_getOrganizationMemberships - tags: - - Organizations - parameters: [] - responses: - '200': - description: '' - content: - application/json: - schema: - $ref: '#/components/schemas/MembershipsResponse' - '400': - description: '' - content: - application/json: - schema: {} - '401': - description: '' - content: - application/json: - schema: {} - '403': - description: '' - content: - application/json: - schema: {} - '404': - description: '' - content: - application/json: - schema: {} - '405': - description: '' - content: - application/json: - schema: {} - security: *ref_0 - put: - description: >- - Create or update a membership for the organization associated with the - API key (requires organization-scoped API key) - operationId: organizations_updateOrganizationMembership - tags: - - Organizations - parameters: [] - responses: - '200': - description: '' - content: - application/json: - schema: - $ref: '#/components/schemas/MembershipResponse' - '400': - description: '' - content: - application/json: - schema: {} - '401': - description: '' - content: - application/json: - schema: {} - '403': - description: '' - content: - application/json: - schema: {} - '404': - description: '' - content: - application/json: - schema: {} - '405': - description: '' - content: - application/json: - schema: {} - security: *ref_0 - requestBody: - required: true - content: - application/json: - schema: - $ref: '#/components/schemas/MembershipRequest' - delete: - description: >- - Delete a membership from the organization associated with the API key - (requires organization-scoped API key) - operationId: organizations_deleteOrganizationMembership - tags: - - Organizations - parameters: [] - responses: - '200': - description: '' - content: - application/json: - schema: - $ref: '#/components/schemas/MembershipDeletionResponse' - '400': - description: '' - content: - application/json: - schema: {} - '401': - description: '' - content: - application/json: - schema: {} - '403': - description: '' - content: - application/json: - schema: {} - '404': - description: '' - content: - application/json: - schema: {} - '405': - description: '' - content: - application/json: - schema: {} - security: *ref_0 - requestBody: - required: true - content: - application/json: - schema: - $ref: '#/components/schemas/DeleteMembershipRequest' - /api/public/projects/{projectId}/memberships: - get: - description: >- - Get all memberships for a specific project (requires organization-scoped - API key) - operationId: organizations_getProjectMemberships - tags: - - Organizations - parameters: - - name: projectId - in: path - required: true - schema: - type: string - responses: - '200': - description: '' - content: - application/json: - schema: - $ref: '#/components/schemas/MembershipsResponse' - '400': - description: '' - content: - application/json: - schema: {} - '401': - description: '' - content: - application/json: - schema: {} - '403': - description: '' - content: - application/json: - schema: {} - '404': - description: '' - content: - application/json: - schema: {} - '405': - description: '' - content: - application/json: - schema: {} - security: *ref_0 - put: - description: >- - Create or update a membership for a specific project (requires - organization-scoped API key). The user must already be a member of the - organization. - operationId: organizations_updateProjectMembership - tags: - - Organizations - parameters: - - name: projectId - in: path - required: true - schema: - type: string - responses: - '200': - description: '' - content: - application/json: - schema: - $ref: '#/components/schemas/MembershipResponse' - '400': - description: '' - content: - application/json: - schema: {} - '401': - description: '' - content: - application/json: - schema: {} - '403': - description: '' - content: - application/json: - schema: {} - '404': - description: '' - content: - application/json: - schema: {} - '405': - description: '' - content: - application/json: - schema: {} - security: *ref_0 - requestBody: - required: true - content: - application/json: - schema: - $ref: '#/components/schemas/MembershipRequest' - delete: - description: >- - Delete a membership from a specific project (requires - organization-scoped API key). The user must be a member of the - organization. - operationId: organizations_deleteProjectMembership - tags: - - Organizations - parameters: - - name: projectId - in: path - required: true - schema: - type: string - responses: - '200': - description: '' - content: - application/json: - schema: - $ref: '#/components/schemas/MembershipDeletionResponse' - '400': - description: '' - content: - application/json: - schema: {} - '401': - description: '' - content: - application/json: - schema: {} - '403': - description: '' - content: - application/json: - schema: {} - '404': - description: '' - content: - application/json: - schema: {} - '405': - description: '' - content: - application/json: - schema: {} - security: *ref_0 - requestBody: - required: true - content: - application/json: - schema: - $ref: '#/components/schemas/DeleteMembershipRequest' - /api/public/organizations/projects: - get: - description: >- - Get all projects for the organization associated with the API key - (requires organization-scoped API key) - operationId: organizations_getOrganizationProjects - tags: - - Organizations - parameters: [] - responses: - '200': - description: '' - content: - application/json: - schema: - $ref: '#/components/schemas/OrganizationProjectsResponse' - '400': - description: '' - content: - application/json: - schema: {} - '401': - description: '' - content: - application/json: - schema: {} - '403': - description: '' - content: - application/json: - schema: {} - '404': - description: '' - content: - application/json: - schema: {} - '405': - description: '' - content: - application/json: - schema: {} - security: *ref_0 - /api/public/organizations/apiKeys: - get: - description: >- - Get all API keys for the organization associated with the API key - (requires organization-scoped API key) - operationId: organizations_getOrganizationApiKeys - tags: - - Organizations - parameters: [] - responses: - '200': - description: '' - content: - application/json: - schema: - $ref: '#/components/schemas/OrganizationApiKeysResponse' - '400': - description: '' - content: - application/json: - schema: {} - '401': - description: '' - content: - application/json: - schema: {} - '403': - description: '' - content: - application/json: - schema: {} - '404': - description: '' - content: - application/json: - schema: {} - '405': - description: '' - content: - application/json: - schema: {} - security: *ref_0 - /api/public/projects: - get: - description: >- - Get Project associated with API key (requires project-scoped API key). - You can use GET /api/public/organizations/projects to get all projects - with an organization-scoped key. - operationId: projects_get - tags: - - Projects - parameters: [] - responses: - '200': - description: '' - content: - application/json: - schema: - $ref: '#/components/schemas/Projects' - '400': - description: '' - content: - application/json: - schema: {} - '401': - description: '' - content: - application/json: - schema: {} - '403': - description: '' - content: - application/json: - schema: {} - '404': - description: '' - content: - application/json: - schema: {} - '405': - description: '' - content: - application/json: - schema: {} - security: *ref_0 - post: - description: Create a new project (requires organization-scoped API key) - operationId: projects_create - tags: - - Projects - parameters: [] - responses: - '200': - description: '' - content: - application/json: - schema: - $ref: '#/components/schemas/Project' - '400': - description: '' - content: - application/json: - schema: {} - '401': - description: '' - content: - application/json: - schema: {} - '403': - description: '' - content: - application/json: - schema: {} - '404': - description: '' - content: - application/json: - schema: {} - '405': - description: '' - content: - application/json: - schema: {} - security: *ref_0 - requestBody: - required: true - content: - application/json: - schema: - type: object - properties: - name: - type: string - metadata: - type: object - additionalProperties: true - nullable: true - description: Optional metadata for the project - retention: - type: integer - description: >- - Number of days to retain data. Must be 0 or at least 3 days. - Requires data-retention entitlement for non-zero values. - Optional. - required: - - name - - retention - /api/public/projects/{projectId}: - put: - description: Update a project by ID (requires organization-scoped API key). - operationId: projects_update - tags: - - Projects - parameters: - - name: projectId - in: path - required: true - schema: - type: string - responses: - '200': - description: '' - content: - application/json: - schema: - $ref: '#/components/schemas/Project' - '400': - description: '' - content: - application/json: - schema: {} - '401': - description: '' - content: - application/json: - schema: {} - '403': - description: '' - content: - application/json: - schema: {} - '404': - description: '' - content: - application/json: - schema: {} - '405': - description: '' - content: - application/json: - schema: {} - security: *ref_0 - requestBody: - required: true - content: - application/json: - schema: - type: object - properties: - name: - type: string - metadata: - type: object - additionalProperties: true - nullable: true - description: Optional metadata for the project - retention: - type: integer - nullable: true - description: |- - Number of days to retain data. - Must be 0 or at least 3 days. - Requires data-retention entitlement for non-zero values. - Optional. Will retain existing retention setting if omitted. - required: - - name - delete: - description: >- - Delete a project by ID (requires organization-scoped API key). Project - deletion is processed asynchronously. - operationId: projects_delete - tags: - - Projects - parameters: - - name: projectId - in: path - required: true - schema: - type: string - responses: - '202': - description: '' - content: - application/json: - schema: - $ref: '#/components/schemas/ProjectDeletionResponse' - '400': - description: '' - content: - application/json: - schema: {} - '401': - description: '' - content: - application/json: - schema: {} - '403': - description: '' - content: - application/json: - schema: {} - '404': - description: '' - content: - application/json: - schema: {} - '405': - description: '' - content: - application/json: - schema: {} - security: *ref_0 - /api/public/projects/{projectId}/apiKeys: - get: - description: Get all API keys for a project (requires organization-scoped API key) - operationId: projects_getApiKeys - tags: - - Projects - parameters: - - name: projectId - in: path - required: true - schema: - type: string - responses: - '200': - description: '' - content: - application/json: - schema: - $ref: '#/components/schemas/ApiKeyList' - '400': - description: '' - content: - application/json: - schema: {} - '401': - description: '' - content: - application/json: - schema: {} - '403': - description: '' - content: - application/json: - schema: {} - '404': - description: '' - content: - application/json: - schema: {} - '405': - description: '' - content: - application/json: - schema: {} - security: *ref_0 - post: - description: >- - Create a new API key for a project (requires organization-scoped API key) - operationId: projects_createApiKey - tags: - - Projects - parameters: - - name: projectId - in: path - required: true - schema: - type: string - responses: - '200': - description: '' - content: - application/json: - schema: - $ref: '#/components/schemas/ApiKeyResponse' - '400': - description: '' - content: - application/json: - schema: {} - '401': - description: '' - content: - application/json: - schema: {} - '403': - description: '' - content: - application/json: - schema: {} - '404': - description: '' - content: - application/json: - schema: {} - '405': - description: '' - content: - application/json: - schema: {} - security: *ref_0 - requestBody: - required: true - content: - application/json: - schema: - type: object - properties: - note: - type: string - nullable: true - description: Optional note for the API key - publicKey: - type: string - nullable: true - description: >- - Optional predefined public key. Must start with 'pk-lf-'. If - provided, secretKey must also be provided. - secretKey: - type: string - nullable: true - description: >- - Optional predefined secret key. Must start with 'sk-lf-'. If - provided, publicKey must also be provided. - /api/public/projects/{projectId}/apiKeys/{apiKeyId}: - delete: - description: Delete an API key for a project (requires organization-scoped API key) - operationId: projects_deleteApiKey - tags: - - Projects - parameters: - - name: projectId - in: path - required: true - schema: - type: string - - name: apiKeyId - in: path - required: true - schema: - type: string - responses: - '200': - description: '' - content: - application/json: - schema: - $ref: '#/components/schemas/ApiKeyDeletionResponse' - '400': - description: '' - content: - application/json: - schema: {} - '401': - description: '' - content: - application/json: - schema: {} - '403': - description: '' - content: - application/json: - schema: {} - '404': - description: '' - content: - application/json: - schema: {} - '405': - description: '' - content: - application/json: - schema: {} - security: *ref_0 - /api/public/v2/prompts/{name}/versions/{version}: - patch: - description: Update labels for a specific prompt version - operationId: promptVersion_update - tags: - - PromptVersion - parameters: - - name: name - in: path - description: >- - The name of the prompt. If the prompt is in a folder (e.g., - "folder/subfolder/prompt-name"), - - the folder path must be URL encoded. - required: true - schema: - type: string - - name: version - in: path - description: Version of the prompt to update - required: true - schema: - type: integer - responses: - '200': - description: '' - content: - application/json: - schema: - $ref: '#/components/schemas/Prompt' - '400': - description: '' - content: - application/json: - schema: {} - '401': - description: '' - content: - application/json: - schema: {} - '403': - description: '' - content: - application/json: - schema: {} - '404': - description: '' - content: - application/json: - schema: {} - '405': - description: '' - content: - application/json: - schema: {} - security: *ref_0 - requestBody: - required: true - content: - application/json: - schema: - type: object - properties: - newLabels: - type: array - items: - type: string - description: >- - New labels for the prompt version. Labels are unique across - versions. The "latest" label is reserved and managed by - Langfuse. - required: - - newLabels - /api/public/v2/prompts/{promptName}: - get: - description: Get a prompt - operationId: prompts_get - tags: - - Prompts - parameters: - - name: promptName - in: path - description: >- - The name of the prompt. If the prompt is in a folder (e.g., - "folder/subfolder/prompt-name"), - - the folder path must be URL encoded. - required: true - schema: - type: string - - name: prompt-version - in: query - description: Version of the prompt to be retrieved. - required: false - schema: - type: integer - nullable: true - - name: label - in: query - description: >- - Label of the prompt to be retrieved. Defaults to "production" if no - label or version is set. - required: false - schema: - type: string - nullable: true - - name: resolve - in: query - description: >- - Resolve prompt dependencies before returning the prompt. Defaults to - `true`. Set to `false` to return the raw stored prompt with - dependency tags intact. This bypasses prompt caching and is intended - for debugging or one-off jobs, not production runtime fetches. - required: false - schema: - type: boolean - nullable: true - responses: - '200': - description: '' - content: - application/json: - schema: - $ref: '#/components/schemas/Prompt' - '400': - description: '' - content: - application/json: - schema: {} - '401': - description: '' - content: - application/json: - schema: {} - '403': - description: '' - content: - application/json: - schema: {} - '404': - description: '' - content: - application/json: - schema: {} - '405': - description: '' - content: - application/json: - schema: {} - security: *ref_0 - delete: - description: >- - Delete prompt versions. If neither version nor label is specified, all - versions of the prompt are deleted. - operationId: prompts_delete - tags: - - Prompts - parameters: - - name: promptName - in: path - description: The name of the prompt - required: true - schema: - type: string - - name: label - in: query - description: >- - Optional label to filter deletion. If specified, deletes all prompt - versions that have this label. - required: false - schema: - type: string - nullable: true - - name: version - in: query - description: >- - Optional version to filter deletion. If specified, deletes only this - specific version of the prompt. - required: false - schema: - type: integer - nullable: true - responses: - '204': - description: '' - '400': - description: '' - content: - application/json: - schema: {} - '401': - description: '' - content: - application/json: - schema: {} - '403': - description: '' - content: - application/json: - schema: {} - '404': - description: '' - content: - application/json: - schema: {} - '405': - description: '' - content: - application/json: - schema: {} - security: *ref_0 - /api/public/v2/prompts: - get: - description: Get a list of prompt names with versions and labels - operationId: prompts_list - tags: - - Prompts - parameters: - - name: name - in: query - required: false - schema: - type: string - nullable: true - - name: label - in: query - required: false - schema: - type: string - nullable: true - - name: tag - in: query - required: false - schema: - type: string - nullable: true - - name: page - in: query - description: page number, starts at 1 - required: false - schema: - type: integer - nullable: true - - name: limit - in: query - description: limit of items per page - required: false - schema: - type: integer - nullable: true - - name: fromUpdatedAt - in: query - description: >- - Optional filter to only include prompt versions created/updated on - or after a certain datetime (ISO 8601) - required: false - schema: - type: string - format: date-time - nullable: true - - name: toUpdatedAt - in: query - description: >- - Optional filter to only include prompt versions created/updated - before a certain datetime (ISO 8601) - required: false - schema: - type: string - format: date-time - nullable: true - responses: - '200': - description: '' - content: - application/json: - schema: - $ref: '#/components/schemas/PromptMetaListResponse' - '400': - description: '' - content: - application/json: - schema: {} - '401': - description: '' - content: - application/json: - schema: {} - '403': - description: '' - content: - application/json: - schema: {} - '404': - description: '' - content: - application/json: - schema: {} - '405': - description: '' - content: - application/json: - schema: {} - security: *ref_0 - post: - description: >- - Create a new version for the prompt with the given `name` - - - Example: - langfuse api prompts create --type text --name my-prompt --prompt 'Hello {{name}}' - operationId: prompts_create - tags: - - Prompts - parameters: [] - responses: - '200': - description: '' - content: - application/json: - schema: - $ref: '#/components/schemas/Prompt' - '400': - description: '' - content: - application/json: - schema: {} - '401': - description: '' - content: - application/json: - schema: {} - '403': - description: '' - content: - application/json: - schema: {} - '404': - description: '' - content: - application/json: - schema: {} - '405': - description: '' - content: - application/json: - schema: {} - security: *ref_0 - requestBody: - required: true - content: - application/json: - schema: - $ref: '#/components/schemas/CreatePromptRequest' - /api/public/scim/ServiceProviderConfig: - get: - description: >- - Get SCIM Service Provider Configuration (requires organization-scoped - API key) - operationId: scim_getServiceProviderConfig - tags: - - Scim - parameters: [] - responses: - '200': - description: '' - content: - application/json: - schema: - $ref: '#/components/schemas/ServiceProviderConfig' - '400': - description: '' - content: - application/json: - schema: {} - '401': - description: '' - content: - application/json: - schema: {} - '403': - description: '' - content: - application/json: - schema: {} - '404': - description: '' - content: - application/json: - schema: {} - '405': - description: '' - content: - application/json: - schema: {} - security: *ref_0 - /api/public/scim/ResourceTypes: - get: - description: Get SCIM Resource Types (requires organization-scoped API key) - operationId: scim_getResourceTypes - tags: - - Scim - parameters: [] - responses: - '200': - description: '' - content: - application/json: - schema: - $ref: '#/components/schemas/ResourceTypesResponse' - '400': - description: '' - content: - application/json: - schema: {} - '401': - description: '' - content: - application/json: - schema: {} - '403': - description: '' - content: - application/json: - schema: {} - '404': - description: '' - content: - application/json: - schema: {} - '405': - description: '' - content: - application/json: - schema: {} - security: *ref_0 - /api/public/scim/Schemas: - get: - description: Get SCIM Schemas (requires organization-scoped API key) - operationId: scim_getSchemas - tags: - - Scim - parameters: [] - responses: - '200': - description: '' - content: - application/json: - schema: - $ref: '#/components/schemas/SchemasResponse' - '400': - description: '' - content: - application/json: - schema: {} - '401': - description: '' - content: - application/json: - schema: {} - '403': - description: '' - content: - application/json: - schema: {} - '404': - description: '' - content: - application/json: - schema: {} - '405': - description: '' - content: - application/json: - schema: {} - security: *ref_0 - /api/public/scim/Users: - get: - description: List users in the organization (requires organization-scoped API key) - operationId: scim_listUsers - tags: - - Scim - parameters: - - name: filter - in: query - description: Filter expression (e.g. userName eq "value") - required: false - schema: - type: string - nullable: true - - name: startIndex - in: query - description: 1-based index of the first result to return (default 1) - required: false - schema: - type: integer - nullable: true - - name: count - in: query - description: Maximum number of results to return (default 100) - required: false - schema: - type: integer - nullable: true - responses: - '200': - description: '' - content: - application/json: - schema: - $ref: '#/components/schemas/ScimUsersListResponse' - '400': - description: '' - content: - application/json: - schema: {} - '401': - description: '' - content: - application/json: - schema: {} - '403': - description: '' - content: - application/json: - schema: {} - '404': - description: '' - content: - application/json: - schema: {} - '405': - description: '' - content: - application/json: - schema: {} - security: *ref_0 - post: - description: >- - Create a new user in the organization (requires organization-scoped API - key) - operationId: scim_createUser - tags: - - Scim - parameters: [] - responses: - '200': - description: '' - content: - application/json: - schema: - $ref: '#/components/schemas/ScimUser' - '400': - description: '' - content: - application/json: - schema: {} - '401': - description: '' - content: - application/json: - schema: {} - '403': - description: '' - content: - application/json: - schema: {} - '404': - description: '' - content: - application/json: - schema: {} - '405': - description: '' - content: - application/json: - schema: {} - security: *ref_0 - requestBody: - required: true - content: - application/json: - schema: - type: object - properties: - userName: - type: string - description: User's email address (required) - name: - $ref: '#/components/schemas/ScimName' - description: User's name information - emails: - type: array - items: - $ref: '#/components/schemas/ScimEmail' - nullable: true - description: User's email addresses - active: - type: boolean - nullable: true - description: Whether the user is active - password: - type: string - nullable: true - description: Initial password for the user - required: - - userName - - name - /api/public/scim/Users/{userId}: - get: - description: Get a specific user by ID (requires organization-scoped API key) - operationId: scim_getUser - tags: - - Scim - parameters: - - name: userId - in: path - required: true - schema: - type: string - responses: - '200': - description: '' - content: - application/json: - schema: - $ref: '#/components/schemas/ScimUser' - '400': - description: '' - content: - application/json: - schema: {} - '401': - description: '' - content: - application/json: - schema: {} - '403': - description: '' - content: - application/json: - schema: {} - '404': - description: '' - content: - application/json: - schema: {} - '405': - description: '' - content: - application/json: - schema: {} - security: *ref_0 - delete: - description: >- - Remove a user from the organization (requires organization-scoped API - key). Note that this only removes the user from the organization but - does not delete the user entity itself. - operationId: scim_deleteUser - tags: - - Scim - parameters: - - name: userId - in: path - required: true - schema: - type: string - responses: - '200': - description: '' - content: - application/json: - schema: - $ref: '#/components/schemas/EmptyResponse' - '400': - description: '' - content: - application/json: - schema: {} - '401': - description: '' - content: - application/json: - schema: {} - '403': - description: '' - content: - application/json: - schema: {} - '404': - description: '' - content: - application/json: - schema: {} - '405': - description: '' - content: - application/json: - schema: {} - security: *ref_0 - /api/public/score-configs: - post: - description: >- - Create a score configuration (config). Score configs are used to define - the structure of scores - operationId: scoreConfigs_create - tags: - - ScoreConfigs - parameters: [] - responses: - '200': - description: '' - content: - application/json: - schema: - $ref: '#/components/schemas/ScoreConfig' - '400': - description: '' - content: - application/json: - schema: {} - '401': - description: '' - content: - application/json: - schema: {} - '403': - description: '' - content: - application/json: - schema: {} - '404': - description: '' - content: - application/json: - schema: {} - '405': - description: '' - content: - application/json: - schema: {} - security: *ref_0 - requestBody: - required: true - content: - application/json: - schema: - $ref: '#/components/schemas/CreateScoreConfigRequest' - get: - description: Get all score configs - operationId: scoreConfigs_get - tags: - - ScoreConfigs - parameters: - - name: page - in: query - description: Page number, starts at 1. - required: false - schema: - type: integer - nullable: true - - name: limit - in: query - description: >- - Limit of items per page. If you encounter api issues due to too - large page sizes, try to reduce the limit - required: false - schema: - type: integer - nullable: true - responses: - '200': - description: '' - content: - application/json: - schema: - $ref: '#/components/schemas/ScoreConfigs' - '400': - description: '' - content: - application/json: - schema: {} - '401': - description: '' - content: - application/json: - schema: {} - '403': - description: '' - content: - application/json: - schema: {} - '404': - description: '' - content: - application/json: - schema: {} - '405': - description: '' - content: - application/json: - schema: {} - security: *ref_0 - /api/public/score-configs/{configId}: - get: - description: Get a score config - operationId: scoreConfigs_get-by-id - tags: - - ScoreConfigs - parameters: - - name: configId - in: path - description: The unique langfuse identifier of a score config - required: true - schema: - type: string - responses: - '200': - description: '' - content: - application/json: - schema: - $ref: '#/components/schemas/ScoreConfig' - '400': - description: '' - content: - application/json: - schema: {} - '401': - description: '' - content: - application/json: - schema: {} - '403': - description: '' - content: - application/json: - schema: {} - '404': - description: '' - content: - application/json: - schema: {} - '405': - description: '' - content: - application/json: - schema: {} - security: *ref_0 - patch: - description: Update a score config - operationId: scoreConfigs_update - tags: - - ScoreConfigs - parameters: - - name: configId - in: path - description: The unique langfuse identifier of a score config - required: true - schema: - type: string - responses: - '200': - description: '' - content: - application/json: - schema: - $ref: '#/components/schemas/ScoreConfig' - '400': - description: '' - content: - application/json: - schema: {} - '401': - description: '' - content: - application/json: - schema: {} - '403': - description: '' - content: - application/json: - schema: {} - '404': - description: '' - content: - application/json: - schema: {} - '405': - description: '' - content: - application/json: - schema: {} - security: *ref_0 - requestBody: - required: true - content: - application/json: - schema: - $ref: '#/components/schemas/UpdateScoreConfigRequest' - /api/public/v3/scores: - get: - description: |- - Get a list of scores with a polymorphic `value` field (v3). - - The `value` field type depends on `dataType`: - - `NUMERIC` → number - - `BOOLEAN` → boolean - - `CATEGORICAL`, `TEXT`, `CORRECTION` → string - - The response always includes the core fields: id, projectId, name, - value, dataType, source, timestamp, environment, createdAt, updatedAt. - - Additional field groups can be requested via the `fields` parameter: - - `details` — adds comment, configId, metadata - - `subject` — adds the subject object describing the entity the score - is attached to: kind (trace, observation, session, or experiment), - id, and traceId for observation-level scores - - `annotation` — adds authorUserId, queueId - - Unknown group names return HTTP 400. - operationId: scoresV3_getManyV3 - tags: - - ScoresV3 - parameters: - - name: limit - in: query - description: >- - Number of items per page. Maximum 100, default 50. Requests with a - limit greater than 100 return HTTP 400. - required: false - schema: - type: integer - nullable: true - - name: cursor - in: query - description: >- - URL-safe base64 (base64url) cursor for pagination. Use the cursor - from the previous response to get the next page. Absent on the final - page. - required: false - schema: - type: string - nullable: true - - name: fields - in: query - description: >- - Comma-separated field groups to include in addition to the - always-returned core fields. Allowed: details, subject, annotation — - see the endpoint description for the fields each group adds. Unknown - names return HTTP 400. - required: false - schema: - type: string - nullable: true - - name: id - in: query - description: >- - Comma-separated list of score IDs to filter by (OR within, AND - across filters). - required: false - schema: - type: string - nullable: true - - name: name - in: query - description: Comma-separated list of score names to filter by. - required: false - schema: - type: string - nullable: true - - name: source - in: query - description: >- - Comma-separated list of score sources to filter by (e.g. API, - ANNOTATION, EVAL). Case-insensitive — `api` and `API` are - equivalent. - required: false - schema: - type: string - nullable: true - - name: dataType - in: query - description: >- - Comma-separated list of data types to filter by (NUMERIC, BOOLEAN, - CATEGORICAL, TEXT, CORRECTION). Case-insensitive — `numeric` and - `NUMERIC` are equivalent. Must be a single value when used with - value, valueMin, or valueMax; otherwise the request returns HTTP - 400. Must be NUMERIC when used with valueMin or valueMax. - required: false - schema: - type: string - nullable: true - - name: environment - in: query - description: Comma-separated list of environments to filter by. - required: false - schema: - type: string - nullable: true - - name: configId - in: query - description: Comma-separated list of score config IDs to filter by. - required: false - schema: - type: string - nullable: true - - name: queueId - in: query - description: Comma-separated list of annotation queue IDs to filter by. - required: false - schema: - type: string - nullable: true - - name: authorUserId - in: query - description: Comma-separated list of author user IDs to filter by. - required: false - schema: - type: string - nullable: true - - name: value - in: query - description: >- - Comma-separated list of exact values to filter by. Requires a single - dataType from NUMERIC, BOOLEAN, or CATEGORICAL; any other dataType, - multiple dataTypes, or omitting dataType returns HTTP 400. For - BOOLEAN, each value must be "true" or "false"; for NUMERIC, each - value must be a finite number. Otherwise the request returns HTTP - 400. - required: false - schema: - type: string - nullable: true - - name: valueMin - in: query - description: >- - Inclusive lower bound on the numeric value. Requires - dataType=NUMERIC as a single value; otherwise the request returns - HTTP 400. - required: false - schema: - type: number - format: double - nullable: true - - name: valueMax - in: query - description: >- - Inclusive upper bound on the numeric value. Requires - dataType=NUMERIC as a single value; otherwise the request returns - HTTP 400. - required: false - schema: - type: number - format: double - nullable: true - - name: traceId - in: query - description: >- - Comma-separated list of trace IDs to filter by. Mutually exclusive - with sessionId, experimentId. May be combined with observationId to - scope the observation lookup to a specific trace. - required: false - schema: - type: string - nullable: true - - name: sessionId - in: query - description: >- - Comma-separated list of session IDs to filter by. Mutually exclusive - with traceId, observationId, experimentId. - required: false - schema: - type: string - nullable: true - - name: observationId - in: query - description: >- - Comma-separated list of observation IDs to filter by. Requires - traceId to be specified, because observation IDs are scoped to a - trace. Mutually exclusive with sessionId, experimentId. Returns HTTP - 400 when used without traceId. - required: false - schema: - type: string - nullable: true - - name: experimentId - in: query - description: >- - Comma-separated list of dataset run IDs (experiment IDs) to filter - by. Mutually exclusive with traceId, sessionId, observationId. - required: false - schema: - type: string - nullable: true - - name: fromTimestamp - in: query - description: Inclusive lower bound on the score timestamp. - required: false - schema: - type: string - format: date-time - nullable: true - - name: toTimestamp - in: query - description: Exclusive upper bound on the score timestamp. - required: false - schema: - type: string - format: date-time - nullable: true - responses: - '200': - description: '' - content: - application/json: - schema: - $ref: '#/components/schemas/GetScoresV3Response' - '400': - description: '' - content: - application/json: - schema: {} - '401': - description: '' - content: - application/json: - schema: {} - '403': - description: '' - content: - application/json: - schema: {} - '404': - description: '' - content: - application/json: - schema: {} - '405': - description: '' - content: - application/json: - schema: {} - security: *ref_0 - /api/public/v2/scores: - get: - description: |- - **Deprecated.** Use `GET /api/public/v3/scores` instead. This endpoint - is no longer available on Langfuse v4 and later. - - Get a list of scores (supports both trace and session scores) - operationId: scores_get-many - tags: - - Scores - parameters: - - name: page - in: query - description: Page number, starts at 1. - required: false - schema: - type: integer - nullable: true - - name: limit - in: query - description: >- - Limit of items per page. Maximum 100. Defaults to 50. Requests with - a limit greater than 100 return HTTP 400. If you encounter api - issues due to too large page sizes, try to reduce the limit. - required: false - schema: - type: integer - nullable: true - - name: userId - in: query - description: Retrieve only scores with this userId associated to the trace. - required: false - schema: - type: string - nullable: true - - name: name - in: query - description: Retrieve only scores with this name. - required: false - schema: - type: string - nullable: true - - name: fromTimestamp - in: query - description: >- - Optional filter to only include scores created on or after a certain - datetime (ISO 8601) - required: false - schema: - type: string - format: date-time - nullable: true - - name: toTimestamp - in: query - description: >- - Optional filter to only include scores created before a certain - datetime (ISO 8601) - required: false - schema: - type: string - format: date-time - nullable: true - - name: environment - in: query - description: >- - Optional filter for scores where the environment is one of the - provided values. - required: false - schema: - type: array - items: - type: string - nullable: true - - name: source - in: query - description: Retrieve only scores from a specific source. - required: false - schema: - $ref: '#/components/schemas/ScoreSource' - nullable: true - - name: operator - in: query - description: Retrieve only scores with value. - required: false - schema: - type: string - nullable: true - - name: value - in: query - description: Retrieve only scores with value. - required: false - schema: - type: number - format: double - nullable: true - - name: scoreIds - in: query - description: Comma-separated list of score IDs to limit the results to. - required: false - schema: - type: string - nullable: true - - name: configId - in: query - description: Retrieve only scores with a specific configId. - required: false - schema: - type: string - nullable: true - - name: sessionId - in: query - description: Retrieve only scores with a specific sessionId. - required: false - schema: - type: string - nullable: true - - name: datasetRunId - in: query - description: Retrieve only scores with a specific datasetRunId. - required: false - schema: - type: string - nullable: true - - name: traceId - in: query - description: Retrieve only scores with a specific traceId. - required: false - schema: - type: string - nullable: true - - name: observationId - in: query - description: Comma-separated list of observation IDs to filter scores by. - required: false - schema: - type: string - nullable: true - - name: queueId - in: query - description: Retrieve only scores with a specific annotation queueId. - required: false - schema: - type: string - nullable: true - - name: dataType - in: query - description: Retrieve only scores with a specific dataType. - required: false - schema: - $ref: '#/components/schemas/ScoreDataType' - nullable: true - - name: traceTags - in: query - description: >- - Only scores linked to traces that include all of these tags will be - returned. - required: false - schema: - type: array - items: - type: string - nullable: true - - name: fields - in: query - description: >- - Comma-separated list of field groups to include in the response. - Available field groups: 'score' (core score fields), 'trace' (trace - properties: userId, tags, environment, sessionId). If not specified, - both 'score' and 'trace' are returned by default. Example: 'score' - to exclude trace data, 'score,trace' to include both. Note: When - filtering by trace properties (using userId or traceTags - parameters), the 'trace' field group must be included, otherwise a - 400 error will be returned. - required: false - schema: - type: string - nullable: true - - name: filter - in: query - description: >- - A JSON stringified array of filter objects. Each object requires - type, column, operator, and value. Supports filtering by score - metadata using the stringObject type. Example: - [{"type":"stringObject","column":"metadata","key":"user_id","operator":"=","value":"abc123"}]. - Supported types: stringObject (metadata key-value filtering), - string, number, datetime, stringOptions, arrayOptions. Supported - operators for stringObject: =, contains, does not contain, starts - with, ends with. - required: false - schema: - type: string - nullable: true - responses: - '200': - description: '' - content: - application/json: - schema: - $ref: '#/components/schemas/GetScoresResponse' - '400': - description: '' - content: - application/json: - schema: {} - '401': - description: '' - content: - application/json: - schema: {} - '403': - description: '' - content: - application/json: - schema: {} - '404': - description: '' - content: - application/json: - schema: {} - '405': - description: '' - content: - application/json: - schema: {} - security: *ref_0 - /api/public/v2/scores/{scoreId}: - get: - description: |- - **Deprecated.** Use `GET /api/public/v3/scores` with the `id` filter - instead. This endpoint is no longer available on Langfuse v4 and later. - - Get a score (supports both trace and session scores) - operationId: scores_get-by-id - tags: - - Scores - parameters: - - name: scoreId - in: path - description: The unique langfuse identifier of a score - required: true - schema: - type: string - responses: - '200': - description: '' - content: - application/json: - schema: - $ref: '#/components/schemas/Score' - '400': - description: '' - content: - application/json: - schema: {} - '401': - description: '' - content: - application/json: - schema: {} - '403': - description: '' - content: - application/json: - schema: {} - '404': - description: '' - content: - application/json: - schema: {} - '405': - description: '' - content: - application/json: - schema: {} - security: *ref_0 - /api/public/sessions: - get: - description: >- - Get sessions. - - - This legacy endpoint is not recommended for new data extraction - workflows. - - Use the v2 observations endpoint with a bounded time range and group - rows by - - `sessionId` instead: - - `GET /api/public/v2/observations?fromStartTime=&toStartTime=`. - operationId: sessions_list - tags: - - Sessions - parameters: - - name: page - in: query - description: Page number, starts at 1 - required: false - schema: - type: integer - nullable: true - - name: limit - in: query - description: >- - Limit of items per page. If you encounter api issues due to too - large page sizes, try to reduce the limit. - required: false - schema: - type: integer - nullable: true - - name: fromTimestamp - in: query - description: >- - Optional filter to only include sessions created on or after a - certain datetime (ISO 8601) - required: false - schema: - type: string - format: date-time - nullable: true - - name: toTimestamp - in: query - description: >- - Optional filter to only include sessions created before a certain - datetime (ISO 8601) - required: false - schema: - type: string - format: date-time - nullable: true - - name: environment - in: query - description: >- - Optional filter for sessions where the environment is one of the - provided values. - required: false - schema: - type: array - items: - type: string - nullable: true - responses: - '200': - description: '' - content: - application/json: - schema: - $ref: '#/components/schemas/PaginatedSessions' - '400': - description: '' - content: - application/json: - schema: {} - '401': - description: '' - content: - application/json: - schema: {} - '403': - description: '' - content: - application/json: - schema: {} - '404': - description: '' - content: - application/json: - schema: {} - '405': - description: '' - content: - application/json: - schema: {} - security: *ref_0 - /api/public/sessions/{sessionId}: - get: - description: >- - Get a session. - - - Please note that `traces` on this endpoint are not paginated. For large - - sessions or new data extraction workflows, use the v2 observations - endpoint - - with a URL-encoded `sessionId` filter and a bounded time range: - - `GET /api/public/v2/observations?filter=&fromStartTime=&toStartTime=`. - operationId: sessions_get - tags: - - Sessions - parameters: - - name: sessionId - in: path - description: The unique id of a session - required: true - schema: - type: string - responses: - '200': - description: '' - content: - application/json: - schema: - $ref: '#/components/schemas/SessionWithTraces' - '400': - description: '' - content: - application/json: - schema: {} - '401': - description: '' - content: - application/json: - schema: {} - '403': - description: '' - content: - application/json: - schema: {} - '404': - description: '' - content: - application/json: - schema: {} - '405': - description: '' - content: - application/json: - schema: {} - security: *ref_0 - /api/public/traces/{traceId}: - get: - description: Get a specific trace - operationId: trace_get - tags: - - Trace - parameters: - - name: traceId - in: path - description: The unique langfuse identifier of a trace - required: true - schema: - type: string - - name: fields - in: query - description: >- - Comma-separated list of fields to include in the response. Available - field groups: 'core' (always included), 'io' (input, output, - metadata), 'scores', 'observations', 'metrics'. If not specified, - all fields are returned. Example: 'core,scores,metrics'. Note: - Excluded 'observations' or 'scores' fields return empty arrays; - excluded 'metrics' returns -1 for 'totalCost' and 'latency'. - required: false - schema: - type: string - nullable: true - responses: - '200': - description: '' - content: - application/json: - schema: - $ref: '#/components/schemas/TraceWithFullDetails' - '400': - description: '' - content: - application/json: - schema: {} - '401': - description: '' - content: - application/json: - schema: {} - '403': - description: '' - content: - application/json: - schema: {} - '404': - description: '' - content: - application/json: - schema: {} - '405': - description: '' - content: - application/json: - schema: {} - security: *ref_0 - delete: - description: Delete a specific trace - operationId: trace_delete - tags: - - Trace - parameters: - - name: traceId - in: path - description: The unique langfuse identifier of the trace to delete - required: true - schema: - type: string - responses: - '200': - description: '' - content: - application/json: - schema: - $ref: '#/components/schemas/DeleteTraceResponse' - '400': - description: '' - content: - application/json: - schema: {} - '401': - description: '' - content: - application/json: - schema: {} - '403': - description: '' - content: - application/json: - schema: {} - '404': - description: '' - content: - application/json: - schema: {} - '405': - description: '' - content: - application/json: - schema: {} - security: *ref_0 - /api/public/traces: - get: - description: Get list of traces - operationId: trace_list - tags: - - Trace - parameters: - - name: page - in: query - description: Page number, starts at 1 - required: false - schema: - type: integer - nullable: true - - name: limit - in: query - description: >- - Limit of items per page. If you encounter api issues due to too - large page sizes, try to reduce the limit. - required: false - schema: - type: integer - nullable: true - - name: userId - in: query - required: false - schema: - type: string - nullable: true - - name: name - in: query - required: false - schema: - type: string - nullable: true - - name: sessionId - in: query - required: false - schema: - type: string - nullable: true - - name: fromTimestamp - in: query - description: >- - Optional filter to only include traces with a trace.timestamp on or - after a certain datetime (ISO 8601) - required: false - schema: - type: string - format: date-time - nullable: true - - name: toTimestamp - in: query - description: >- - Optional filter to only include traces with a trace.timestamp before - a certain datetime (ISO 8601) - required: false - schema: - type: string - format: date-time - nullable: true - - name: orderBy - in: query - description: >- - Format of the string [field].[asc/desc]. Fields: id, timestamp, - name, userId, release, version, public, bookmarked, sessionId. - Example: timestamp.asc - required: false - schema: - type: string - nullable: true - - name: tags - in: query - description: Only traces that include all of these tags will be returned. - required: false - schema: - type: array - items: - type: string - nullable: true - - name: version - in: query - description: Optional filter to only include traces with a certain version. - required: false - schema: - type: string - nullable: true - - name: release - in: query - description: Optional filter to only include traces with a certain release. - required: false - schema: - type: string - nullable: true - - name: environment - in: query - description: >- - Optional filter for traces where the environment is one of the - provided values. - required: false - schema: - type: array - items: - type: string - nullable: true - - name: fields - in: query - description: >- - Comma-separated list of fields to include in the response. Available - field groups: 'core' (always included), 'io' (input, output, - metadata), 'scores', 'observations', 'metrics'. If not specified, - all fields are returned. Example: 'core,scores,metrics'. Note: - Excluded 'observations' or 'scores' fields return empty arrays; - excluded 'metrics' returns -1 for 'totalCost' and 'latency'. - required: false - schema: - type: string - nullable: true - - name: filter - in: query - description: >- - JSON string containing an array of filter conditions. When provided, - this takes precedence over query parameter filters (userId, name, - sessionId, tags, version, release, environment, fromTimestamp, - toTimestamp). - - - ## Filter Structure - - Each filter condition has the following structure: - - ```json - - [ - { - "type": string, // Required. One of: "datetime", "string", "number", "stringOptions", "categoryOptions", "arrayOptions", "stringObject", "numberObject", "boolean", "null" - "column": string, // Required. Column to filter on (see available columns below) - "operator": string, // Required. Operator based on type: - // - datetime: ">", "<", ">=", "<=" - // - string: "=", "contains", "does not contain", "starts with", "ends with" - // - stringOptions: "any of", "none of" - // - categoryOptions: "any of", "none of" - // - arrayOptions: "any of", "none of", "all of" - // - number: "=", ">", "<", ">=", "<=" - // - stringObject: "=", "contains", "does not contain", "starts with", "ends with" - // - numberObject: "=", ">", "<", ">=", "<=" - // - boolean: "=", "<>" - // - null: "is null", "is not null" - "value": any, // Required (except for null type). Value to compare against. Type depends on filter type - "key": string // Required only for stringObject, numberObject, and categoryOptions types when filtering on nested fields like metadata - } - ] - - ``` - - - ## Available Columns - - - ### Core Trace Fields - - - `id` (string) - Trace ID - - - `name` (string) - Trace name - - - `timestamp` (datetime) - Trace timestamp - - - `userId` (string) - User ID - - - `sessionId` (string) - Session ID - - - `environment` (string) - Environment tag - - - `version` (string) - Version tag - - - `release` (string) - Release tag - - - `tags` (arrayOptions) - Array of tags - - - `bookmarked` (boolean) - Bookmark status - - - ### Structured Data - - - `metadata` (stringObject/numberObject/categoryOptions) - Metadata - key-value pairs. Use `key` parameter to filter on specific metadata - keys. - - - ### Aggregated Metrics (from observations) - - These metrics are aggregated from all observations within the trace: - - - `latency` (number) - Latency in seconds (time from first - observation start to last observation end) - - - `inputTokens` (number) - Total input tokens across all - observations - - - `outputTokens` (number) - Total output tokens across all - observations - - - `totalTokens` (number) - Total tokens (alias: `tokens`) - - - `inputCost` (number) - Total input cost in USD - - - `outputCost` (number) - Total output cost in USD - - - `totalCost` (number) - Total cost in USD - - - ### Observation Level Aggregations - - These fields aggregate observation levels within the trace: - - - `level` (string) - Highest severity level (ERROR > WARNING > - DEFAULT > DEBUG) - - - `warningCount` (number) - Count of WARNING level observations - - - `errorCount` (number) - Count of ERROR level observations - - - `defaultCount` (number) - Count of DEFAULT level observations - - - `debugCount` (number) - Count of DEBUG level observations - - - ### Scores (requires join with scores table) - - - `scores_avg` (number) - Average of numeric scores (alias: - `scores`) - - - `score_categories` (categoryOptions) - Categorical score values - - - ## Filter Examples - - ```json - - [ - { - "type": "datetime", - "column": "timestamp", - "operator": ">=", - "value": "2024-01-01T00:00:00Z" - }, - { - "type": "string", - "column": "userId", - "operator": "=", - "value": "user-123" - }, - { - "type": "number", - "column": "totalCost", - "operator": ">=", - "value": 0.01 - }, - { - "type": "arrayOptions", - "column": "tags", - "operator": "all of", - "value": ["production", "critical"] - }, - { - "type": "stringObject", - "column": "metadata", - "key": "customer_tier", - "operator": "=", - "value": "enterprise" - } - ] - - ``` - - - ## Performance Notes - - - Filtering on `userId`, `sessionId`, or `metadata` may enable skip - indexes for better query performance - - - Score filters require a join with the scores table and may impact - query performance - required: false - schema: - type: string - nullable: true - responses: - '200': - description: '' - content: - application/json: - schema: - $ref: '#/components/schemas/Traces' - '400': - description: '' - content: - application/json: - schema: {} - '401': - description: '' - content: - application/json: - schema: {} - '403': - description: '' - content: - application/json: - schema: {} - '404': - description: '' - content: - application/json: - schema: {} - '405': - description: '' - content: - application/json: - schema: {} - security: *ref_0 - delete: - description: Delete multiple traces - operationId: trace_deleteMultiple - tags: - - Trace - parameters: [] - responses: - '200': - description: '' - content: - application/json: - schema: - $ref: '#/components/schemas/DeleteTraceResponse' - '400': - description: '' - content: - application/json: - schema: {} - '401': - description: '' - content: - application/json: - schema: {} - '403': - description: '' - content: - application/json: - schema: {} - '404': - description: '' - content: - application/json: - schema: {} - '405': - description: '' - content: - application/json: - schema: {} - security: *ref_0 - requestBody: - required: true - content: - application/json: - schema: - type: object - properties: - traceIds: - type: array - items: - type: string - description: List of trace IDs to delete - required: - - traceIds - /api/public/unstable/dashboard-widgets: - post: - description: >- - Create a reusable dashboard widget. - - - This endpoint creates the widget. It does not place the widget on a - dashboard grid, this has to be done in the UI. - - - Supported views are `observations`, `scores-numeric`, and - `scores-categorical`. - - The legacy `traces` view is not supported by this unstable API, - `minVersion` defaults to `2`; values below `2` are rejected. - - - Unstable API note: - - - This surface may evolve while dashboard/widget APIs are being - finalized. - operationId: unstable_dashboardWidgets_create - tags: - - UnstableDashboardWidgets - parameters: [] - responses: - '200': - description: '' - content: - application/json: - schema: - $ref: '#/components/schemas/unstableDashboardWidget' - '400': - description: '' - content: - application/json: - schema: {} - '401': - description: '' - content: - application/json: - schema: {} - '403': - description: '' - content: - application/json: - schema: {} - '404': - description: '' - content: - application/json: - schema: {} - '405': - description: '' - content: - application/json: - schema: {} - '429': - description: '' - content: - application/json: - schema: - $ref: '#/components/schemas/unstablePublicApiError' - '500': - description: '' - content: - application/json: - schema: - $ref: '#/components/schemas/unstablePublicApiError' - security: *ref_0 - requestBody: - required: true - content: - application/json: - schema: - $ref: '#/components/schemas/unstableCreateDashboardWidgetRequest' - /api/public/unstable/evaluation-rules: - post: - description: >- - Create an evaluation rule. - - - An evaluation rule defines **what** incoming data should be evaluated - and **how prompt variables should be populated** from that data. - - - Use this resource after choosing an evaluator from the evaluator - endpoints. - - - Key rules: - - - `name` must be unique within the project for public evaluation rules - - - `target` must be `observation` or `experiment` - - - `evaluator.name` + `evaluator.scope` must identify an existing - evaluator family returned by the evaluator endpoints - - - Langfuse resolves that family to its latest version before saving the - evaluation rule - - - for `target=experiment`, use dataset `id` values from `GET - /api/public/v2/datasets` when filtering by `datasetId` - - - for `llm_as_judge` evaluators, every evaluator prompt variable must be - mapped exactly once - - - for `code` evaluators, Langfuse uses the fixed code runtime mapping; - omit `mapping` in create and update requests - - - for user-provided `llm_as_judge` mappings, `expected_output` and - `experiment_item_metadata` are only valid for `target=experiment` - - - if `enabled=true`, Langfuse validates that the referenced evaluator - can currently run - - - at most 50 evaluation rules can be effectively active in one project - at the same time - - - If an evaluation rule with the same `name` already exists in the - project, the API returns `409`. - - In that case, update the existing resource with `PATCH - /api/public/unstable/evaluation-rules/{evaluationRuleId}` instead of - creating a second one. - - - If enabling this resource would exceed the 50-active limit, the API also - returns `409`. - - In that case, disable or pause another active evaluation rule before - enabling a new one. - - - Current scope: - - - evaluation rules are live-ingestion rules only - - - they do not trigger historical backfills - - - Recovery guidance: - - - `400 invalid_filter_value`: fix the filter `column` or `value` using - `details.column`, `details.invalidValues`, and `details.allowedValues` - - - `400 invalid_filter_value` with `details.column=datasetId`: call `GET - /api/public/v2/datasets`, then retry with dataset `id` values from that - response - - - `400 missing_variable_mapping`: for `llm_as_judge` evaluators, fetch - the evaluator again and make sure every variable in `variables` appears - exactly once in `mapping` - - - `400 duplicate_variable_mapping`: remove repeated mappings for the - same variable - - - `400 invalid_variable_mapping`: for `llm_as_judge`, switch to a valid - `source` for the selected `target`, or fix the variable name - - - `400 invalid_json_path`: remove or correct the `jsonPath` - - - `422 evaluator_preflight_failed`: the selected evaluator cannot run - with the resolved model configuration. Fix the evaluator/default model - setup, then retry the create request. - operationId: unstable_evaluationRules_create - tags: - - UnstableEvaluationRules - parameters: [] - responses: - '200': - description: '' - content: - application/json: - schema: - $ref: '#/components/schemas/unstableEvaluationRule' - '400': - description: '' - content: - application/json: - schema: {} - '401': - description: '' - content: - application/json: - schema: {} - '403': - description: '' - content: - application/json: - schema: {} - '404': - description: '' - content: - application/json: - schema: {} - '405': - description: '' - content: - application/json: - schema: {} - '409': - description: '' - content: - application/json: - schema: - $ref: '#/components/schemas/unstablePublicApiError' - '422': - description: '' - content: - application/json: - schema: - $ref: '#/components/schemas/unstablePublicApiError' - '429': - description: '' - content: - application/json: - schema: - $ref: '#/components/schemas/unstablePublicApiError' - '500': - description: '' - content: - application/json: - schema: - $ref: '#/components/schemas/unstablePublicApiError' - security: *ref_0 - requestBody: - required: true - content: - application/json: - schema: - $ref: '#/components/schemas/unstableCreateEvaluationRuleRequest' - get: - description: >- - List evaluation rules in the authenticated project. - - - Each item describes one live evaluation rule and its effective runtime - status. - operationId: unstable_evaluationRules_list - tags: - - UnstableEvaluationRules - parameters: - - name: page - in: query - description: 1-based page number. Defaults to `1`. - required: false - schema: - type: integer - nullable: true - - name: limit - in: query - description: Maximum number of items per page. Defaults to `50`. - required: false - schema: - type: integer - nullable: true - responses: - '200': - description: '' - content: - application/json: - schema: - $ref: '#/components/schemas/unstableEvaluationRules' - '400': - description: '' - content: - application/json: - schema: {} - '401': - description: '' - content: - application/json: - schema: {} - '403': - description: '' - content: - application/json: - schema: {} - '404': - description: '' - content: - application/json: - schema: {} - '405': - description: '' - content: - application/json: - schema: {} - '429': - description: '' - content: - application/json: - schema: - $ref: '#/components/schemas/unstablePublicApiError' - '500': - description: '' - content: - application/json: - schema: - $ref: '#/components/schemas/unstablePublicApiError' - security: *ref_0 - /api/public/unstable/evaluation-rules/{evaluationRuleId}: - get: - description: >- - Get one evaluation rule by its identifier. - - - Use this endpoint to inspect the current evaluator, target, mapping, - filters, and effective runtime status. - operationId: unstable_evaluationRules_get - tags: - - UnstableEvaluationRules - parameters: - - name: evaluationRuleId - in: path - description: >- - Evaluation rule identifier returned by the evaluation rule endpoints. - required: true - schema: - type: string - responses: - '200': - description: '' - content: - application/json: - schema: - $ref: '#/components/schemas/unstableEvaluationRule' - '400': - description: '' - content: - application/json: - schema: {} - '401': - description: '' - content: - application/json: - schema: {} - '403': - description: '' - content: - application/json: - schema: {} - '404': - description: '' - content: - application/json: - schema: {} - '405': - description: '' - content: - application/json: - schema: {} - '429': - description: '' - content: - application/json: - schema: - $ref: '#/components/schemas/unstablePublicApiError' - '500': - description: '' - content: - application/json: - schema: - $ref: '#/components/schemas/unstablePublicApiError' - security: *ref_0 - patch: - description: >- - Update an evaluation rule. - - - Typical uses: - - - enable or disable live execution - - - switch to another evaluator - - - adjust sampling - - - change filters - - - update LLM-as-judge variable mappings - - - Important behavior: - - - provide only the fields you want to change - - - if you provide `evaluator`, Langfuse resolves that evaluator family to - its latest version before saving - - - changing `target`, `filter`, or an LLM-as-judge `mapping` must still - produce a valid target-specific configuration - - - if you change `target` for an LLM-as-judge rule, also send a - compatible `filter` and `mapping` in the same request unless the - existing ones are still valid for the new target - - - for `code` evaluator rules, omit `mapping`; Langfuse stores the fixed - code runtime mapping automatically - - - if the resulting config is enabled, Langfuse re-validates that the - selected evaluator can run - - - if the update would move a non-active evaluation rule into the active - state and the project already has 50 active evaluation rules, the API - returns `409` - - - Recovery guidance: - - - if an LLM-as-judge update fails with `missing_variable_mapping` or - `invalid_variable_mapping` after changing `evaluator` or `target`, - resend the request with a complete new `mapping` - - - if the update fails with `invalid_filter_value` after changing - `target`, resend the request with a target-compatible `filter` - operationId: unstable_evaluationRules_update - tags: - - UnstableEvaluationRules - parameters: - - name: evaluationRuleId - in: path - description: Evaluation rule identifier. - required: true - schema: - type: string - responses: - '200': - description: '' - content: - application/json: - schema: - $ref: '#/components/schemas/unstableEvaluationRule' - '400': - description: '' - content: - application/json: - schema: {} - '401': - description: '' - content: - application/json: - schema: {} - '403': - description: '' - content: - application/json: - schema: {} - '404': - description: '' - content: - application/json: - schema: {} - '405': - description: '' - content: - application/json: - schema: {} - '422': - description: '' - content: - application/json: - schema: - $ref: '#/components/schemas/unstablePublicApiError' - '429': - description: '' - content: - application/json: - schema: - $ref: '#/components/schemas/unstablePublicApiError' - '500': - description: '' - content: - application/json: - schema: - $ref: '#/components/schemas/unstablePublicApiError' - security: *ref_0 - requestBody: - required: true - content: - application/json: - schema: - $ref: '#/components/schemas/unstableUpdateEvaluationRuleRequest' - delete: - description: >- - Delete an evaluation rule. - - - This removes the live-ingestion rule only. It does not delete the - referenced evaluator. - operationId: unstable_evaluationRules_delete - tags: - - UnstableEvaluationRules - parameters: - - name: evaluationRuleId - in: path - description: Evaluation rule identifier. - required: true - schema: - type: string - responses: - '200': - description: '' - content: - application/json: - schema: - $ref: '#/components/schemas/unstableDeleteEvaluationRuleResponse' - '400': - description: '' - content: - application/json: - schema: {} - '401': - description: '' - content: - application/json: - schema: {} - '403': - description: '' - content: - application/json: - schema: {} - '404': - description: '' - content: - application/json: - schema: {} - '405': - description: '' - content: - application/json: - schema: {} - '429': - description: '' - content: - application/json: - schema: - $ref: '#/components/schemas/unstablePublicApiError' - '500': - description: '' - content: - application/json: - schema: - $ref: '#/components/schemas/unstablePublicApiError' - security: *ref_0 - /api/public/unstable/evaluators: - post: - description: >- - Create an evaluator in the authenticated project. - - - Use evaluators to define **how** Langfuse should score data. - - LLM-as-a-judge evaluators define a prompt, expected structured output, - and optional model configuration. - - Code evaluators define source code and a runtime language. - - - Naming behavior: - - - If this is a new evaluator name in your project, Langfuse creates - version `1`. - - - If the name already exists in your project, Langfuse creates the next - version and returns it. - - - When a new project version is created, existing evaluation rules in - that project automatically move to the newest version for that evaluator - name. - - - Recommended workflow: - - 1. Create the evaluator. - - 2. Read the returned `variables` array. - - 3. Read the returned `outputDefinition.dataType` so the client knows - whether future scores will be numeric, boolean, or categorical. - - 4. Create one or more evaluation rules that reference the returned - evaluator family using `name` and `scope`. - - - Code evaluator validation: - - - At creation, Langfuse only validates the request shape - - - The `sourceCode` itself is not executed here. It is first run - (preflight-tested against a sample observation) when you link the - evaluator to an evaluation rule, so runtime errors in the code surface - at evaluation-rule creation, not at evaluator creation. - - - Recovery guidance: - - - `422` with `code=evaluator_preflight_failed`: the evaluator cannot run - with the resolved model configuration. Add a valid explicit - `modelConfig`, or configure the project's default evaluation model, then - retry the same request. - - - `400` with `code=invalid_body`: the request shape is malformed. Use - the structured `details.issues` array to fix the specific fields and - retry. - - - `400` with `code=invalid_body` on `outputDefinition`: for - `type=llm_as_judge`, send `dataType`, `reasoning.description`, and - `score.description`. Do not send `version`; it is not part of the public - request shape. - - - If `type` is omitted, Langfuse treats the request as - `type=llm_as_judge` for backwards compatibility. New clients should send - `type` explicitly. - - - Unstable API note: - - - This surface may evolve while the underlying evaluation data model is - being redesigned. - operationId: unstable_evaluators_create - tags: - - UnstableEvaluators - parameters: [] - responses: - '200': - description: '' - content: - application/json: - schema: - $ref: '#/components/schemas/unstableEvaluator' - '400': - description: '' - content: - application/json: - schema: {} - '401': - description: '' - content: - application/json: - schema: {} - '403': - description: '' - content: - application/json: - schema: {} - '404': - description: '' - content: - application/json: - schema: {} - '405': - description: '' - content: - application/json: - schema: {} - '409': - description: '' - content: - application/json: - schema: - $ref: '#/components/schemas/unstablePublicApiError' - '422': - description: '' - content: - application/json: - schema: - $ref: '#/components/schemas/unstablePublicApiError' - '429': - description: '' - content: - application/json: - schema: - $ref: '#/components/schemas/unstablePublicApiError' - '500': - description: '' - content: - application/json: - schema: - $ref: '#/components/schemas/unstablePublicApiError' - security: *ref_0 - requestBody: - required: true - content: - application/json: - schema: - $ref: '#/components/schemas/unstableCreateEvaluatorRequest' - get: - description: >- - List the evaluators available to the authenticated project. - - - Important behavior: - - - This endpoint returns the latest version of each available evaluator. - - - Results can include evaluators from your project and Langfuse-managed - evaluators. - - - If the same evaluator name exists in both places, both are returned as - separate items with different `scope` values. - operationId: unstable_evaluators_list - tags: - - UnstableEvaluators - parameters: - - name: page - in: query - description: 1-based page number. Defaults to `1`. - required: false - schema: - type: integer - nullable: true - - name: limit - in: query - description: Maximum number of items per page. Defaults to `50`. - required: false - schema: - type: integer - nullable: true - responses: - '200': - description: '' - content: - application/json: - schema: - $ref: '#/components/schemas/unstableEvaluators' - '400': - description: '' - content: - application/json: - schema: {} - '401': - description: '' - content: - application/json: - schema: {} - '403': - description: '' - content: - application/json: - schema: {} - '404': - description: '' - content: - application/json: - schema: {} - '405': - description: '' - content: - application/json: - schema: {} - '429': - description: '' - content: - application/json: - schema: - $ref: '#/components/schemas/unstablePublicApiError' - '500': - description: '' - content: - application/json: - schema: - $ref: '#/components/schemas/unstablePublicApiError' - security: *ref_0 - /api/public/unstable/evaluators/{evaluatorId}: - get: - description: >- - Get one evaluator by `id`. - - - Use this endpoint when you want the prompt, output definition, model - configuration, and derived variables for the evaluator you plan to use - in an evaluation rule. - operationId: unstable_evaluators_get - tags: - - UnstableEvaluators - parameters: - - name: evaluatorId - in: path - description: Evaluator identifier returned by the evaluator endpoints. - required: true - schema: - type: string - responses: - '200': - description: '' - content: - application/json: - schema: - $ref: '#/components/schemas/unstableEvaluator' - '400': - description: '' - content: - application/json: - schema: {} - '401': - description: '' - content: - application/json: - schema: {} - '403': - description: '' - content: - application/json: - schema: {} - '404': - description: '' - content: - application/json: - schema: {} - '405': - description: '' - content: - application/json: - schema: {} - '429': - description: '' - content: - application/json: - schema: - $ref: '#/components/schemas/unstablePublicApiError' - '500': - description: '' - content: - application/json: - schema: - $ref: '#/components/schemas/unstablePublicApiError' - security: *ref_0 - delete: - description: >- - Delete an evaluator. - - - Important behavior: - - - This deletes the evaluator including all of its stored versions; - `evaluatorId` may reference any version. - - - The API returns `409` while evaluation rules still reference the - evaluator. Delete those evaluation rules first. - - - Langfuse-managed evaluators (`scope=managed`) cannot be deleted; the - API returns `403`. - - - Scores already produced by the evaluator are not deleted. - operationId: unstable_evaluators_delete - tags: - - UnstableEvaluators - parameters: - - name: evaluatorId - in: path - description: Evaluator identifier returned by the evaluator endpoints. - required: true - schema: - type: string - responses: - '200': - description: '' - content: - application/json: - schema: - $ref: '#/components/schemas/unstableDeleteEvaluatorResponse' - '400': - description: '' - content: - application/json: - schema: {} - '401': - description: '' - content: - application/json: - schema: {} - '403': - description: '' - content: - application/json: - schema: {} - '404': - description: '' - content: - application/json: - schema: {} - '405': - description: '' - content: - application/json: - schema: {} - '409': - description: '' - content: - application/json: - schema: - $ref: '#/components/schemas/unstablePublicApiError' - '429': - description: '' - content: - application/json: - schema: - $ref: '#/components/schemas/unstablePublicApiError' - '500': - description: '' - content: - application/json: - schema: - $ref: '#/components/schemas/unstablePublicApiError' - security: *ref_0 -components: - schemas: - AnnotationQueueStatus: - title: AnnotationQueueStatus - type: string - enum: - - PENDING - - COMPLETED - AnnotationQueueObjectType: - title: AnnotationQueueObjectType - type: string - enum: - - TRACE - - OBSERVATION - - SESSION - AnnotationQueue: - title: AnnotationQueue - type: object - properties: - id: - type: string - name: - type: string - description: - type: string - nullable: true - scoreConfigIds: - type: array - items: - type: string - createdAt: - type: string - format: date-time - updatedAt: - type: string - format: date-time - required: - - id - - name - - description - - scoreConfigIds - - createdAt - - updatedAt - AnnotationQueueItem: - title: AnnotationQueueItem - type: object - properties: - id: - type: string - queueId: - type: string - objectId: - type: string - objectType: - $ref: '#/components/schemas/AnnotationQueueObjectType' - status: - $ref: '#/components/schemas/AnnotationQueueStatus' - completedAt: - type: string - format: date-time - nullable: true - createdAt: - type: string - format: date-time - updatedAt: - type: string - format: date-time - required: - - id - - queueId - - objectId - - objectType - - status - - createdAt - - updatedAt - PaginatedAnnotationQueues: - title: PaginatedAnnotationQueues - type: object - properties: - data: - type: array - items: - $ref: '#/components/schemas/AnnotationQueue' - meta: - $ref: '#/components/schemas/utilsMetaResponse' - required: - - data - - meta - PaginatedAnnotationQueueItems: - title: PaginatedAnnotationQueueItems - type: object - properties: - data: - type: array - items: - $ref: '#/components/schemas/AnnotationQueueItem' - meta: - $ref: '#/components/schemas/utilsMetaResponse' - required: - - data - - meta - CreateAnnotationQueueRequest: - title: CreateAnnotationQueueRequest - type: object - properties: - name: - type: string - description: - type: string - nullable: true - scoreConfigIds: - type: array - items: - type: string - required: - - name - - scoreConfigIds - CreateAnnotationQueueItemRequest: - title: CreateAnnotationQueueItemRequest - type: object - properties: - objectId: - type: string - objectType: - $ref: '#/components/schemas/AnnotationQueueObjectType' - status: - $ref: '#/components/schemas/AnnotationQueueStatus' - nullable: true - description: Defaults to PENDING for new queue items - required: - - objectId - - objectType - UpdateAnnotationQueueItemRequest: - title: UpdateAnnotationQueueItemRequest - type: object - properties: - status: - $ref: '#/components/schemas/AnnotationQueueStatus' - nullable: true - DeleteAnnotationQueueItemResponse: - title: DeleteAnnotationQueueItemResponse - type: object - properties: - success: - type: boolean - message: - type: string - required: - - success - - message - AnnotationQueueAssignmentRequest: - title: AnnotationQueueAssignmentRequest - type: object - properties: - userId: - type: string - required: - - userId - DeleteAnnotationQueueAssignmentResponse: - title: DeleteAnnotationQueueAssignmentResponse - type: object - properties: - success: - type: boolean - required: - - success - CreateAnnotationQueueAssignmentResponse: - title: CreateAnnotationQueueAssignmentResponse - type: object - properties: - userId: - type: string - queueId: - type: string - projectId: - type: string - required: - - userId - - queueId - - projectId - BlobStorageIntegrationType: - title: BlobStorageIntegrationType - type: string - enum: - - S3 - - S3_COMPATIBLE - - AZURE_BLOB_STORAGE - BlobStorageIntegrationFileType: - title: BlobStorageIntegrationFileType - type: string - enum: - - JSON - - CSV - - JSONL - BlobStorageIntegrationFileTypeResponse: - title: BlobStorageIntegrationFileTypeResponse - type: string - enum: - - JSON - - CSV - - JSONL - - PARQUET - description: >- - File type reported for an existing integration. Includes `PARQUET`, - which a project may enable through the Langfuse UI but cannot yet be set - via this API (the request `fileType` omits it). - BlobStorageExportMode: - title: BlobStorageExportMode - type: string - enum: - - FULL_HISTORY - - FROM_TODAY - - FROM_CUSTOM_DATE - BlobStorageExportFrequency: - title: BlobStorageExportFrequency - type: string - enum: - - every_20_minutes - - hourly - - daily - - weekly - BlobStorageExportSource: - title: BlobStorageExportSource - type: string - enum: - - LEGACY_TRACES_OBSERVATIONS - - OBSERVATIONS_V2 - - LEGACY_TRACES_AND_ENRICHED_OBSERVATIONS - description: >- - What data the integration exports. - - - `LEGACY_TRACES_OBSERVATIONS`: traces, observations, and scores tables. - Observation columns are controlled by `exportFieldGroups`; field groups - without a counterpart in this data model (e.g. `trace_context`) are - omitted. - - - `OBSERVATIONS_V2`: same data model as the - `/api/public/v2/observations` endpoint, plus scores. Columns are - controlled by `exportFieldGroups`. - - - `LEGACY_TRACES_AND_ENRICHED_OBSERVATIONS`: both sets. Observation - columns of both portions are controlled by `exportFieldGroups`. - - - **Note:** `OBSERVATIONS_V2` and the enriched-observations portion of - `LEGACY_TRACES_AND_ENRICHED_OBSERVATIONS` rely on the enriched - observations table (Langfuse Fast Preview / v4), which is currently - available on Langfuse Cloud only. See https://langfuse.com/docs/v4. - BlobStorageExportFieldGroup: - title: BlobStorageExportFieldGroup - type: string - enum: - - core - - basic - - time - - io - - metadata - - model - - usage - - prompt - - metrics - - tools - - trace_context - description: >- - Field group selecting which observation columns are included in the - export. Applies to all export sources; groups without a counterpart in - the legacy data model (e.g. `trace_context`) are omitted from the legacy - observations export. - CreateBlobStorageIntegrationRequest: - title: CreateBlobStorageIntegrationRequest - type: object - properties: - projectId: - type: string - description: ID of the project in which to configure the blob storage integration - type: - $ref: '#/components/schemas/BlobStorageIntegrationType' - bucketName: - type: string - description: >- - Name of the storage bucket. For AZURE_BLOB_STORAGE, must be a valid - Azure container name (3-63 chars, lowercase letters, numbers, and - hyphens only, must start and end with a letter or number, no - consecutive hyphens). - endpoint: - type: string - nullable: true - description: Custom endpoint URL (required for S3_COMPATIBLE type) - region: - type: string - description: Storage region - accessKeyId: - type: string - nullable: true - description: Access key ID for authentication - secretAccessKey: - type: string - nullable: true - description: Secret access key for authentication (will be encrypted when stored) - prefix: - type: string - nullable: true - description: >- - Path prefix for exported files (must end with forward slash if - provided) - exportFrequency: - $ref: '#/components/schemas/BlobStorageExportFrequency' - enabled: - type: boolean - description: Whether the integration is active - forcePathStyle: - type: boolean - description: Use path-style URLs for S3 requests - fileType: - $ref: '#/components/schemas/BlobStorageIntegrationFileType' - exportMode: - $ref: '#/components/schemas/BlobStorageExportMode' - exportStartDate: - type: string - format: date-time - nullable: true - description: >- - Custom start date for exports (required when exportMode is - FROM_CUSTOM_DATE). Must not be in the future (27 h tolerance for - timezone differences). - compressed: - type: boolean - nullable: true - description: >- - Enable gzip compression for exported files (.csv.gz, .json.gz, - .jsonl.gz). Defaults to true. - exportSource: - $ref: '#/components/schemas/BlobStorageExportSource' - nullable: true - description: >- - Data to export. When omitted on update, the existing value is - preserved. When omitted on create: integrations on Langfuse Cloud - default to `OBSERVATIONS_V2`; self-hosted deployments fall back to - `LEGACY_TRACES_OBSERVATIONS`. Required when `exportFieldGroups` is - provided. - - - **Cloud-only project deprecation gate (effective 2026-05-20):** For - projects created on or after 2026-05-20 on Langfuse Cloud, - `LEGACY_TRACES_OBSERVATIONS` and - `LEGACY_TRACES_AND_ENRICHED_OBSERVATIONS` are rejected with HTTP - 400. Use `OBSERVATIONS_V2` for all new integrations. Self-hosted - deployments are unaffected. - - - **Cloud-only integration deprecation gate (effective 2026-06-22):** - On Langfuse Cloud, legacy export sources are only accepted for blob - storage integrations created before 2026-06-22, regardless of - project age. Requests that would create a new integration with - `LEGACY_TRACES_OBSERVATIONS` or - `LEGACY_TRACES_AND_ENRICHED_OBSERVATIONS` are rejected with HTTP - 400. Use `OBSERVATIONS_V2` instead. Self-hosted deployments are - unaffected. - exportFieldGroups: - type: array - items: - $ref: '#/components/schemas/BlobStorageExportFieldGroup' - nullable: true - description: >- - Field groups to include in each exported observation row. Applies to - all export sources; must include `core` if provided. When omitted on - create, the column default (all groups) applies. When omitted on - update, the existing value is preserved. - - - `exportFieldGroups` requires `exportSource` to be provided in the - same request. - required: - - projectId - - type - - bucketName - - region - - exportFrequency - - enabled - - forcePathStyle - - fileType - - exportMode - BlobStorageIntegrationResponse: - title: BlobStorageIntegrationResponse - type: object - properties: - id: - type: string - projectId: - type: string - type: - $ref: '#/components/schemas/BlobStorageIntegrationType' - bucketName: - type: string - endpoint: - type: string - nullable: true - region: - type: string - accessKeyId: - type: string - nullable: true - prefix: - type: string - exportFrequency: - $ref: '#/components/schemas/BlobStorageExportFrequency' - enabled: - type: boolean - forcePathStyle: - type: boolean - fileType: - $ref: '#/components/schemas/BlobStorageIntegrationFileTypeResponse' - exportMode: - $ref: '#/components/schemas/BlobStorageExportMode' - exportStartDate: - type: string - format: date-time - nullable: true - compressed: - type: boolean - exportSource: - $ref: '#/components/schemas/BlobStorageExportSource' - exportFieldGroups: - type: array - items: - $ref: '#/components/schemas/BlobStorageExportFieldGroup' - nullable: true - description: >- - Field groups included in each exported observation row. An empty - list is treated as all groups during export. - nextSyncAt: - type: string - format: date-time - nullable: true - lastSyncAt: - type: string - format: date-time - nullable: true - lastError: - type: string - nullable: true - lastErrorAt: - type: string - format: date-time - nullable: true - createdAt: - type: string - format: date-time - updatedAt: - type: string - format: date-time - required: - - id - - projectId - - type - - bucketName - - endpoint - - region - - accessKeyId - - prefix - - exportFrequency - - enabled - - forcePathStyle - - fileType - - exportMode - - exportStartDate - - compressed - - exportSource - - exportFieldGroups - - nextSyncAt - - lastSyncAt - - lastError - - lastErrorAt - - createdAt - - updatedAt - BlobStorageIntegrationsResponse: - title: BlobStorageIntegrationsResponse - type: object - properties: - data: - type: array - items: - $ref: '#/components/schemas/BlobStorageIntegrationResponse' - required: - - data - BlobStorageSyncStatus: - title: BlobStorageSyncStatus - type: string - enum: - - idle - - running - - queued - - up_to_date - - disabled - - error - description: >- - Sync status of the blob storage integration: - - - `disabled` — integration is not enabled - - - `error` — last export failed (see `lastError` for details) - - - `running` — an export job is currently being processed - - - `queued` — next export is overdue (`nextSyncAt` is in the past) and - waiting to be picked up by the worker - - - `idle` — enabled but has never exported yet and no export is queued - - - `up_to_date` — all available data has been exported; next export is - scheduled for the future - - - **ETL usage**: poll this endpoint and check for `up_to_date` status. - Compare `lastSyncAt` against your - - ETL bookmark to determine if new data is available. Note that exports - run with a 20-minute lag buffer, - - so `lastSyncAt` will always be at least 20 minutes behind real-time. - BlobStorageIntegrationStatusResponse: - title: BlobStorageIntegrationStatusResponse - type: object - properties: - id: - type: string - projectId: - type: string - syncStatus: - $ref: '#/components/schemas/BlobStorageSyncStatus' - enabled: - type: boolean - lastSyncAt: - type: string - format: date-time - nullable: true - description: >- - End of the last successfully exported time window. Compare against - your ETL bookmark to determine if new data is available. Null if the - integration has never synced. - nextSyncAt: - type: string - format: date-time - nullable: true - description: When the next export is scheduled. Null if no sync has occurred yet. - lastError: - type: string - nullable: true - description: >- - Raw error message from the storage provider (S3/Azure/GCS) if the - last export failed. Cleared on successful export. - lastErrorAt: - type: string - format: date-time - nullable: true - description: When the last error occurred. Cleared on successful export. - required: - - id - - projectId - - syncStatus - - enabled - - lastSyncAt - - nextSyncAt - - lastError - - lastErrorAt - BlobStorageIntegrationDeletionResponse: - title: BlobStorageIntegrationDeletionResponse - type: object - properties: - message: - type: string - required: - - message - CreateCommentRequest: - title: CreateCommentRequest - type: object - properties: - projectId: - type: string - description: The id of the project to attach the comment to. - objectType: - type: string - description: >- - The type of the object to attach the comment to (trace, observation, - session, prompt). - objectId: - type: string - description: >- - The id of the object to attach the comment to. If this does not - reference a valid existing object, an error will be thrown. - content: - type: string - description: >- - The content of the comment. May include markdown. Currently limited - to 5000 characters. - authorUserId: - type: string - nullable: true - description: The id of the user who created the comment. - required: - - projectId - - objectType - - objectId - - content - CreateCommentResponse: - title: CreateCommentResponse - type: object - properties: - id: - type: string - description: The id of the created object in Langfuse - required: - - id - GetCommentsResponse: - title: GetCommentsResponse - type: object - properties: - data: - type: array - items: - $ref: '#/components/schemas/Comment' - meta: - $ref: '#/components/schemas/utilsMetaResponse' - required: - - data - - meta - Trace: - title: Trace - type: object - properties: - id: - type: string - description: The unique identifier of a trace - timestamp: - type: string - format: date-time - description: The timestamp when the trace was created - name: - type: string - nullable: true - description: The name of the trace - input: - nullable: true - description: The input data of the trace. Can be any JSON. - output: - nullable: true - description: The output data of the trace. Can be any JSON. - sessionId: - type: string - nullable: true - description: The session identifier associated with the trace - release: - type: string - nullable: true - description: The release version of the application when the trace was created - version: - type: string - nullable: true - description: The version of the trace - userId: - type: string - nullable: true - description: The user identifier associated with the trace - metadata: - nullable: true - description: The metadata associated with the trace. Can be any JSON. - tags: - type: array - items: - type: string - description: The tags associated with the trace. - public: - type: boolean - description: Public traces are accessible via url without login - environment: - type: string - description: >- - The environment from which this trace originated. Can be any - lowercase alphanumeric string with hyphens and underscores that does - not start with 'langfuse'. - required: - - id - - timestamp - - name - - sessionId - - release - - version - - userId - - tags - - public - - environment - TraceWithDetails: - title: TraceWithDetails - type: object - properties: - htmlPath: - type: string - description: Path of trace in Langfuse UI - latency: - type: number - format: double - nullable: true - description: Latency of trace in seconds - totalCost: - type: number - format: double - nullable: true - description: Cost of trace in USD - observations: - type: array - items: - type: string - nullable: true - description: List of observation ids - scores: - type: array - items: - type: string - nullable: true - description: List of score ids - required: - - htmlPath - allOf: - - $ref: '#/components/schemas/Trace' - TraceWithFullDetails: - title: TraceWithFullDetails - type: object - properties: - htmlPath: - type: string - description: Path of trace in Langfuse UI - latency: - type: number - format: double - nullable: true - description: Latency of trace in seconds - totalCost: - type: number - format: double - nullable: true - description: Cost of trace in USD - observations: - type: array - items: - $ref: '#/components/schemas/ObservationsView' - description: List of observations - scores: - type: array - items: - $ref: '#/components/schemas/ScoreV1' - description: List of scores - required: - - htmlPath - - observations - - scores - allOf: - - $ref: '#/components/schemas/Trace' - Session: - title: Session - type: object - properties: - id: - type: string - createdAt: - type: string - format: date-time - projectId: - type: string - environment: - type: string - description: The environment from which this session originated. - required: - - id - - createdAt - - projectId - - environment - SessionWithTraces: - title: SessionWithTraces - type: object - properties: - traces: - type: array - items: - $ref: '#/components/schemas/Trace' - required: - - traces - allOf: - - $ref: '#/components/schemas/Session' - Observation: - title: Observation - type: object - properties: - id: - type: string - description: The unique identifier of the observation - traceId: - type: string - nullable: true - description: The trace ID associated with the observation - type: - type: string - description: The type of the observation - name: - type: string - nullable: true - description: The name of the observation - startTime: - type: string - format: date-time - description: The start time of the observation - endTime: - type: string - format: date-time - nullable: true - description: The end time of the observation. - completionStartTime: - type: string - format: date-time - nullable: true - description: The completion start time of the observation - model: - type: string - nullable: true - description: The model used for the observation - modelParameters: - description: The parameters of the model used for the observation - input: - description: The input data of the observation - version: - type: string - nullable: true - description: The version of the observation - metadata: - description: Additional metadata of the observation - output: - description: The output data of the observation - usage: - $ref: '#/components/schemas/Usage' - description: >- - (Deprecated. Use usageDetails and costDetails instead.) The usage - data of the observation - level: - $ref: '#/components/schemas/ObservationLevel' - description: The level of the observation - statusMessage: - type: string - nullable: true - description: The status message of the observation - parentObservationId: - type: string - nullable: true - description: The parent observation ID - promptId: - type: string - nullable: true - description: The prompt ID associated with the observation - usageDetails: - type: object - additionalProperties: - type: integer - description: >- - The usage details of the observation. Key is the name of the usage - metric, value is the number of units consumed. The total key is the - sum of all (non-total) usage metrics or the total value ingested. - costDetails: - type: object - additionalProperties: - type: number - format: double - description: >- - The cost details of the observation. Key is the name of the cost - metric, value is the cost in USD. The total key is the sum of all - (non-total) cost metrics or the total value ingested. - environment: - type: string - description: >- - The environment from which this observation originated. Can be any - lowercase alphanumeric string with hyphens and underscores that does - not start with 'langfuse'. - required: - - id - - traceId - - type - - name - - startTime - - endTime - - completionStartTime - - model - - modelParameters - - input - - version - - metadata - - output - - usage - - level - - statusMessage - - parentObservationId - - promptId - - usageDetails - - costDetails - - environment - ObservationsView: - title: ObservationsView - type: object - properties: - promptName: - type: string - nullable: true - description: The name of the prompt associated with the observation - promptVersion: - type: integer - nullable: true - description: The version of the prompt associated with the observation - modelId: - type: string - nullable: true - description: The unique identifier of the model - inputPrice: - type: number - format: double - nullable: true - description: The price of the input in USD - outputPrice: - type: number - format: double - nullable: true - description: The price of the output in USD. - totalPrice: - type: number - format: double - nullable: true - description: The total price in USD. - calculatedInputCost: - type: number - format: double - nullable: true - description: >- - (Deprecated. Use usageDetails and costDetails instead.) The - calculated cost of the input in USD - calculatedOutputCost: - type: number - format: double - nullable: true - description: >- - (Deprecated. Use usageDetails and costDetails instead.) The - calculated cost of the output in USD - calculatedTotalCost: - type: number - format: double - nullable: true - description: >- - (Deprecated. Use usageDetails and costDetails instead.) The - calculated total cost in USD - latency: - type: number - format: double - nullable: true - description: The latency in seconds. - timeToFirstToken: - type: number - format: double - nullable: true - description: The time to the first token in seconds - required: - - promptName - - promptVersion - - modelId - - inputPrice - - outputPrice - - totalPrice - - calculatedInputCost - - calculatedOutputCost - - calculatedTotalCost - - latency - - timeToFirstToken - allOf: - - $ref: '#/components/schemas/Observation' - ObservationV2: - title: ObservationV2 - type: object - description: >- - An observation from the v2 API with field-group-based selection. - - Core fields are always present. Other fields are included only when - their field group is requested. - properties: - id: - type: string - description: The unique identifier of the observation - traceId: - type: string - nullable: true - description: The trace ID associated with the observation - startTime: - type: string - format: date-time - description: The start time of the observation - endTime: - type: string - format: date-time - nullable: true - description: The end time of the observation - projectId: - type: string - description: The project ID this observation belongs to - parentObservationId: - type: string - nullable: true - description: The parent observation ID - type: - type: string - description: The type of the observation (e.g. GENERATION, SPAN, EVENT) - name: - type: string - nullable: true - description: The name of the observation - level: - $ref: '#/components/schemas/ObservationLevel' - nullable: true - description: The level of the observation - statusMessage: - type: string - nullable: true - description: The status message of the observation - version: - type: string - nullable: true - description: The version of the observation - environment: - type: string - nullable: true - description: The environment from which this observation originated - bookmarked: - type: boolean - nullable: true - description: Whether the observation is bookmarked - public: - type: boolean - nullable: true - description: Whether the observation is public - userId: - type: string - nullable: true - description: The user ID associated with the observation - sessionId: - type: string - nullable: true - description: The session ID associated with the observation - completionStartTime: - type: string - format: date-time - nullable: true - description: The completion start time of the observation - createdAt: - type: string - format: date-time - nullable: true - description: The creation timestamp of the observation - updatedAt: - type: string - format: date-time - nullable: true - description: The last update timestamp of the observation - input: - nullable: true - description: The input data of the observation - output: - nullable: true - description: The output data of the observation - metadata: - nullable: true - description: Additional metadata of the observation - providedModelName: - type: string - nullable: true - description: The model name as provided by the user - internalModelId: - type: string - nullable: true - description: The internal model ID matched by Langfuse - modelParameters: - nullable: true - description: The parameters of the model used for the observation - usageDetails: - type: object - additionalProperties: - type: integer - nullable: true - description: >- - The usage details of the observation. Key is the usage metric name, - value is the number of units consumed. - costDetails: - type: object - additionalProperties: - type: number - format: double - nullable: true - description: >- - The cost details of the observation. Key is the cost metric name, - value is the cost in USD. - totalCost: - type: number - format: double - nullable: true - description: The total cost of the observation in USD - usagePricingTierName: - type: string - nullable: true - description: >- - The name of the pricing tier applied to this observation's usage - costs - promptId: - type: string - nullable: true - description: The prompt ID associated with the observation - promptName: - type: string - nullable: true - description: The prompt name associated with the observation - promptVersion: - type: integer - nullable: true - description: The prompt version associated with the observation - latency: - type: number - format: double - nullable: true - description: The latency in seconds - timeToFirstToken: - type: number - format: double - nullable: true - description: The time to first token in seconds - modelId: - type: string - nullable: true - description: >- - The matched model ID. Null when the `model` field group is not - requested. - inputPrice: - type: string - nullable: true - description: >- - The input token price (USD per unit) from the matched model, - serialized as a decimal string (e.g. "0.0001"). Null when the - `model` field group is not requested. - outputPrice: - type: string - nullable: true - description: >- - The output token price (USD per unit) from the matched model, - serialized as a decimal string (e.g. "0.0001"). Null when the - `model` field group is not requested. - totalPrice: - type: string - nullable: true - description: >- - The total token price (USD per unit) from the matched model, - serialized as a decimal string (e.g. "0.0001"). Null when the - `model` field group is not requested. - traceName: - type: string - nullable: true - description: The name of the parent trace - tags: - type: array - items: - type: string - nullable: true - description: Tags from the parent trace (denormalized onto the observation) - release: - type: string - nullable: true - description: The release version of the parent trace - required: - - id - - traceId - - startTime - - endTime - - projectId - - parentObservationId - - type - - modelId - - inputPrice - - outputPrice - - totalPrice - Usage: - title: Usage - type: object - description: >- - (Deprecated. Use usageDetails and costDetails instead.) Standard - interface for usage and cost - properties: - input: - type: integer - description: Number of input units (e.g. tokens) - output: - type: integer - description: Number of output units (e.g. tokens) - total: - type: integer - description: Defaults to input+output if not set - unit: - type: string - nullable: true - description: Unit of measurement - inputCost: - type: number - format: double - nullable: true - description: USD input cost - outputCost: - type: number - format: double - nullable: true - description: USD output cost - totalCost: - type: number - format: double - nullable: true - description: USD total cost, defaults to input+output - required: - - input - - output - - total - - unit - ScoreConfig: - title: ScoreConfig - type: object - description: Configuration for a score - properties: - id: - type: string - name: - type: string - createdAt: - type: string - format: date-time - updatedAt: - type: string - format: date-time - projectId: - type: string - dataType: - $ref: '#/components/schemas/ScoreConfigDataType' - isArchived: - type: boolean - description: Whether the score config is archived. Defaults to false - minValue: - type: number - format: double - nullable: true - description: >- - Sets minimum value for numerical scores. If not set, the minimum - value defaults to -∞ - maxValue: - type: number - format: double - nullable: true - description: >- - Sets maximum value for numerical scores. If not set, the maximum - value defaults to +∞ - categories: - type: array - items: - $ref: '#/components/schemas/ConfigCategory' - nullable: true - description: Configures custom categories for categorical scores - description: - type: string - nullable: true - description: Description of the score config - required: - - id - - name - - createdAt - - updatedAt - - projectId - - dataType - - isArchived - ConfigCategory: - title: ConfigCategory - type: object - properties: - value: - type: number - format: double - label: - type: string - required: - - value - - label - BaseScoreV1: - title: BaseScoreV1 - type: object - properties: - id: - type: string - traceId: - type: string - name: - type: string - source: - $ref: '#/components/schemas/ScoreSource' - observationId: - type: string - nullable: true - description: The observation ID associated with the score - timestamp: - type: string - format: date-time - createdAt: - type: string - format: date-time - updatedAt: - type: string - format: date-time - authorUserId: - type: string - nullable: true - description: The user ID of the author - comment: - type: string - nullable: true - description: Comment on the score - metadata: - description: Metadata associated with the score - configId: - type: string - nullable: true - description: >- - Reference a score config on a score. When set, config and score name - must be equal and value must comply to optionally defined numerical - range - queueId: - type: string - nullable: true - description: >- - The annotation queue referenced by the score. Indicates if score was - initially created while processing annotation queue. - environment: - type: string - description: >- - The environment from which this score originated. Can be any - lowercase alphanumeric string with hyphens and underscores that does - not start with 'langfuse'. - required: - - id - - traceId - - name - - source - - timestamp - - createdAt - - updatedAt - - authorUserId - - comment - - metadata - - configId - - queueId - - environment - NumericScoreV1: - title: NumericScoreV1 - type: object - properties: - value: - type: number - format: double - description: The numeric value of the score - required: - - value - allOf: - - $ref: '#/components/schemas/BaseScoreV1' - BooleanScoreV1: - title: BooleanScoreV1 - type: object - properties: - value: - type: number - format: double - description: >- - The numeric value of the score. Equals 1 for "True" and 0 for "False" - stringValue: - type: string - description: >- - The string representation of the score value. Is inferred from the - numeric value and equals "True" or "False" - required: - - value - - stringValue - allOf: - - $ref: '#/components/schemas/BaseScoreV1' - CategoricalScoreV1: - title: CategoricalScoreV1 - type: object - properties: - value: - type: number - format: double - description: >- - Represents the numeric category mapping of the stringValue. If no - config is linked, defaults to 0. - stringValue: - type: string - description: >- - The string representation of the score value. If no config is - linked, can be any string. Otherwise, must map to a config category - required: - - value - - stringValue - allOf: - - $ref: '#/components/schemas/BaseScoreV1' - TextScoreV1: - title: TextScoreV1 - type: object - properties: - stringValue: - type: string - description: The text content of the score (1-500 characters) - required: - - stringValue - allOf: - - $ref: '#/components/schemas/BaseScoreV1' - ScoreV1: - title: ScoreV1 - type: object - properties: - dataType: - type: string - enum: - - NUMERIC - - CATEGORICAL - - BOOLEAN - - TEXT - value: - type: string - description: The numeric value of the score - stringValue: - type: string - description: The string representation of the score value. If no config is - linked, can be any string. Otherwise, must map to a config category - required: - - dataType - BaseScore: - title: BaseScore - type: object - properties: - id: - type: string - traceId: - type: string - nullable: true - description: The trace ID associated with the score - sessionId: - type: string - nullable: true - description: The session ID associated with the score - observationId: - type: string - nullable: true - description: The observation ID associated with the score - datasetRunId: - type: string - nullable: true - description: The dataset run ID associated with the score - name: - type: string - source: - $ref: '#/components/schemas/ScoreSource' - timestamp: - type: string - format: date-time - createdAt: - type: string - format: date-time - updatedAt: - type: string - format: date-time - authorUserId: - type: string - nullable: true - description: The user ID of the author - comment: - type: string - nullable: true - description: Comment on the score - metadata: - description: Metadata associated with the score - configId: - type: string - nullable: true - description: >- - Reference a score config on a score. When set, config and score name - must be equal and value must comply to optionally defined numerical - range - queueId: - type: string - nullable: true - description: >- - The annotation queue referenced by the score. Indicates if score was - initially created while processing annotation queue. - environment: - type: string - description: >- - The environment from which this score originated. Can be any - lowercase alphanumeric string with hyphens and underscores that does - not start with 'langfuse'. - required: - - id - - name - - source - - timestamp - - createdAt - - updatedAt - - authorUserId - - comment - - metadata - - configId - - queueId - - environment - NumericScore: - title: NumericScore - type: object - properties: - value: - type: number - format: double - description: The numeric value of the score - required: - - value - allOf: - - $ref: '#/components/schemas/BaseScore' - BooleanScore: - title: BooleanScore - type: object - properties: - value: - type: number - format: double - description: >- - The numeric value of the score. Equals 1 for "True" and 0 for "False" - stringValue: - type: string - description: >- - The string representation of the score value. Is inferred from the - numeric value and equals "True" or "False" - required: - - value - - stringValue - allOf: - - $ref: '#/components/schemas/BaseScore' - CategoricalScore: - title: CategoricalScore - type: object - properties: - value: - type: number - format: double - description: >- - Represents the numeric category mapping of the stringValue. If no - config is linked, defaults to 0. - stringValue: - type: string - description: >- - The string representation of the score value. If no config is - linked, can be any string. Otherwise, must map to a config category - required: - - value - - stringValue - allOf: - - $ref: '#/components/schemas/BaseScore' - CorrectionScore: - title: CorrectionScore - type: object - properties: - value: - type: number - format: double - description: The numeric value of the score. Always 0 for correction scores. - stringValue: - type: string - description: The string representation of the correction content - required: - - value - - stringValue - allOf: - - $ref: '#/components/schemas/BaseScore' - TextScore: - title: TextScore - type: object - properties: - stringValue: - type: string - description: The text content of the score (1-500 characters) - required: - - stringValue - allOf: - - $ref: '#/components/schemas/BaseScore' - Score: - title: Score - type: object - properties: - dataType: - type: string - enum: - - NUMERIC - - CATEGORICAL - - BOOLEAN - - CORRECTION - - TEXT - value: - type: string - description: The numeric value of the score - stringValue: - type: string - description: The string representation of the score value. If no config is - linked, can be any string. Otherwise, must map to a config category - required: - - dataType - CreateScoreValue: - title: CreateScoreValue - oneOf: - - type: number - format: double - - type: string - description: >- - The value of the score. Must be passed as string for categorical and - text scores, and numeric for boolean and numeric scores - Comment: - title: Comment - type: object - properties: - id: - type: string - projectId: - type: string - createdAt: - type: string - format: date-time - updatedAt: - type: string - format: date-time - objectType: - $ref: '#/components/schemas/CommentObjectType' - objectId: - type: string - content: - type: string - authorUserId: - type: string - nullable: true - description: The user ID of the comment author - required: - - id - - projectId - - createdAt - - updatedAt - - objectType - - objectId - - content - Dataset: - title: Dataset - type: object - properties: - id: - type: string - name: - type: string - description: - type: string - nullable: true - description: Description of the dataset - metadata: - description: Metadata associated with the dataset - inputSchema: - nullable: true - description: JSON Schema for validating dataset item inputs - expectedOutputSchema: - nullable: true - description: JSON Schema for validating dataset item expected outputs - projectId: - type: string - createdAt: - type: string - format: date-time - updatedAt: - type: string - format: date-time - required: - - id - - name - - description - - metadata - - inputSchema - - expectedOutputSchema - - projectId - - createdAt - - updatedAt - DatasetItem: - title: DatasetItem - type: object - properties: - id: - type: string - status: - $ref: '#/components/schemas/DatasetStatus' - input: - description: Input data for the dataset item - expectedOutput: - description: Expected output for the dataset item - metadata: - description: Metadata associated with the dataset item - sourceTraceId: - type: string - nullable: true - description: The trace ID that sourced this dataset item - sourceObservationId: - type: string - nullable: true - description: The observation ID that sourced this dataset item - datasetId: - type: string - datasetName: - type: string - createdAt: - type: string - format: date-time - updatedAt: - type: string - format: date-time - mediaReferences: - type: array - items: - $ref: '#/components/schemas/DatasetItemMediaReference' - description: >- - Resolved Langfuse media references found in input, expectedOutput, - and metadata. - required: - - id - - status - - input - - expectedOutput - - metadata - - sourceTraceId - - sourceObservationId - - datasetId - - datasetName - - createdAt - - updatedAt - - mediaReferences - DatasetItemMediaReference: - title: DatasetItemMediaReference - type: object - properties: - field: - $ref: '#/components/schemas/DatasetItemMediaReferenceField' - description: The dataset item field containing the reference - referenceString: - type: string - description: >- - The Langfuse media reference string, e.g. - `@@@langfuseMedia:type=image/png|id=...|source=bytes@@@` - jsonPath: - type: string - description: >- - JSONPath of the string holding the reference within the field, e.g. - `$['image']` - media: - $ref: '#/components/schemas/DatasetItemMediaReferenceMedia' - description: The resolved media record. - required: - - field - - referenceString - - jsonPath - - media - DatasetItemMediaReferenceField: - title: DatasetItemMediaReferenceField - type: string - enum: - - input - - expectedOutput - - metadata - DatasetItemMediaReferenceMedia: - title: DatasetItemMediaReferenceMedia - type: object - properties: - mediaId: - type: string - description: The unique langfuse identifier of the media record - contentType: - type: string - description: The MIME type of the media record - contentLength: - type: integer - description: The size of the media record in bytes - url: - type: string - description: The signed download URL of the media record - urlExpiry: - type: string - description: The expiry date and time of the download URL - required: - - mediaId - - contentType - - contentLength - - url - - urlExpiry - DatasetRunItem: - title: DatasetRunItem - type: object - properties: - id: - type: string - datasetRunId: - type: string - datasetRunName: - type: string - datasetItemId: - type: string - traceId: - type: string - observationId: - type: string - nullable: true - description: The observation ID associated with this run item - createdAt: - type: string - format: date-time - updatedAt: - type: string - format: date-time - required: - - id - - datasetRunId - - datasetRunName - - datasetItemId - - traceId - - observationId - - createdAt - - updatedAt - DatasetRun: - title: DatasetRun - type: object - properties: - id: - type: string - description: Unique identifier of the dataset run - name: - type: string - description: Name of the dataset run - description: - type: string - nullable: true - description: Description of the run - metadata: - description: Metadata of the dataset run - datasetId: - type: string - description: Id of the associated dataset - datasetName: - type: string - description: Name of the associated dataset - createdAt: - type: string - format: date-time - description: The date and time when the dataset run was created - updatedAt: - type: string - format: date-time - description: The date and time when the dataset run was last updated - required: - - id - - name - - description - - metadata - - datasetId - - datasetName - - createdAt - - updatedAt - DatasetRunWithItems: - title: DatasetRunWithItems - type: object - properties: - datasetRunItems: - type: array - items: - $ref: '#/components/schemas/DatasetRunItem' - required: - - datasetRunItems - allOf: - - $ref: '#/components/schemas/DatasetRun' - Model: - title: Model - type: object - description: >- - Model definition used for transforming usage into USD cost and/or - tokenization. - - - Models can have either simple flat pricing or tiered pricing: - - - Flat pricing: Single price per usage type (legacy, but still - supported) - - - Tiered pricing: Multiple pricing tiers with conditional matching based - on usage patterns - - - The pricing tiers approach is recommended for models with usage-based - pricing variations. - - When using tiered pricing, the flat price fields (inputPrice, - outputPrice, prices) are populated - - from the default tier for backward compatibility. - properties: - id: - type: string - modelName: - type: string - description: >- - Name of the model definition. If multiple with the same name exist, - they are applied in the following order: (1) custom over built-in, - (2) newest according to startTime where - model.startTime- - Regex pattern which matches this model definition to - generation.model. Useful in case of fine-tuned models. If you want - to exact match, use `(?i)^modelname$` - startDate: - type: string - format: date-time - nullable: true - description: Apply only to generations which are newer than this ISO date. - unit: - $ref: '#/components/schemas/ModelUsageUnit' - nullable: true - description: Unit used by this model. - inputPrice: - type: number - format: double - nullable: true - description: Deprecated. See 'prices' instead. Price (USD) per input unit - outputPrice: - type: number - format: double - nullable: true - description: Deprecated. See 'prices' instead. Price (USD) per output unit - totalPrice: - type: number - format: double - nullable: true - description: >- - Deprecated. See 'prices' instead. Price (USD) per total unit. Cannot - be set if input or output price is set. - tokenizerId: - type: string - nullable: true - description: >- - Optional. Tokenizer to be applied to observations which match to - this model. See docs for more details. - tokenizerConfig: - description: >- - Optional. Configuration for the selected tokenizer. Needs to be - JSON. See docs for more details. - isLangfuseManaged: - type: boolean - createdAt: - type: string - format: date-time - description: Timestamp when the model was created - prices: - type: object - additionalProperties: - $ref: '#/components/schemas/ModelPrice' - description: >- - Deprecated. Use 'pricingTiers' instead for models with usage-based - pricing variations. - - - This field shows prices by usage type from the default pricing tier. - Maintained for backward compatibility. - - If the model uses tiered pricing, this field will be populated from - the default tier's prices. - pricingTiers: - type: array - items: - $ref: '#/components/schemas/PricingTier' - description: >- - Array of pricing tiers with conditional pricing based on usage - thresholds. - - - Pricing tiers enable accurate cost tracking for models that charge - different rates based on usage patterns - - (e.g., different rates for high-volume usage, large context windows, - or cached tokens). - - - Each model must have exactly one default tier (isDefault=true, - priority=0) that serves as a fallback. - - Additional conditional tiers can be defined with specific matching - criteria. - - - If this array is empty, the model uses legacy flat pricing from the - inputPrice/outputPrice/totalPrice fields. - required: - - id - - modelName - - matchPattern - - startDate - - inputPrice - - outputPrice - - totalPrice - - tokenizerId - - tokenizerConfig - - isLangfuseManaged - - createdAt - - prices - - pricingTiers - ModelPrice: - title: ModelPrice - type: object - properties: - price: - type: number - format: double - required: - - price - PricingTierCondition: - title: PricingTierCondition - type: object - description: >- - Condition for matching a pricing tier based on usage details. Used to - implement tiered pricing models where costs vary based on usage - thresholds. - - - How it works: - - 1. The regex pattern matches against usage detail keys (e.g., - "input_tokens", "input_cached") - - 2. Values of all matching keys are summed together - - 3. The sum is compared against the threshold value using the specified - operator - - 4. All conditions in a tier must be met (AND logic) for the tier to - match - - - Common use cases: - - - Threshold-based pricing: Match when accumulated usage exceeds a - certain amount - - - Usage-type-specific pricing: Different rates for cached vs non-cached - tokens, or input vs output - - - Volume-based pricing: Different rates based on total request or token - count - properties: - usageDetailPattern: - type: string - description: >- - Regex pattern to match against usage detail keys. All matching keys' - values are summed for threshold comparison. - - - Examples: - - - "^input" matches "input", "input_tokens", "input_cached", etc. - - - "^(input|prompt)" matches both "input_tokens" and "prompt_tokens" - - - "_cache$" matches "input_cache", "output_cache", etc. - - - The pattern is case-insensitive by default. If no keys match, the - sum is treated as zero. - operator: - $ref: '#/components/schemas/PricingTierOperator' - description: >- - Comparison operator to apply between the summed value and the - threshold. - - - - gt: greater than (sum > threshold) - - - gte: greater than or equal (sum >= threshold) - - - lt: less than (sum < threshold) - - - lte: less than or equal (sum <= threshold) - - - eq: equal (sum == threshold) - - - neq: not equal (sum != threshold) - value: - type: number - format: double - description: >- - Threshold value for comparison. For token-based pricing, this is - typically the token count threshold (e.g., 200000 for a 200K token - threshold). - caseSensitive: - type: boolean - description: >- - Whether the regex pattern matching is case-sensitive. Default is - false (case-insensitive matching). - required: - - usageDetailPattern - - operator - - value - - caseSensitive - PricingTier: - title: PricingTier - type: object - description: >- - Pricing tier definition with conditional pricing based on usage - thresholds. - - - Pricing tiers enable accurate cost tracking for LLM providers that - charge different rates based on usage patterns. - - For example, some providers charge higher rates when context size - exceeds certain thresholds. - - - How tier matching works: - - 1. Tiers are evaluated in ascending priority order (priority 1 before - priority 2, etc.) - - 2. The first tier where ALL conditions match is selected - - 3. If no conditional tiers match, the default tier is used as a fallback - - 4. The default tier has priority 0 and no conditions - - - Why priorities matter: - - - Lower priority numbers are evaluated first, allowing you to define - specific cases before general ones - - - Example: Priority 1 for "high usage" (>200K tokens), Priority 2 for - "medium usage" (>100K tokens), Priority 0 for default - - - Without proper ordering, a less specific condition might match before - a more specific one - - - Every model must have exactly one default tier to ensure cost - calculation always succeeds. - properties: - id: - type: string - description: Unique identifier for the pricing tier - name: - type: string - description: >- - Name of the pricing tier for display and identification purposes. - - - Examples: "Standard", "High Volume Tier", "Large Context", "Extended - Context Tier" - isDefault: - type: boolean - description: >- - Whether this is the default tier. Every model must have exactly one - default tier with priority 0 and no conditions. - - - The default tier serves as a fallback when no conditional tiers - match, ensuring cost calculation always succeeds. - - It typically represents the base pricing for standard usage - patterns. - priority: - type: integer - description: >- - Priority for tier matching evaluation. Lower numbers = higher - priority (evaluated first). - - - The default tier must always have priority 0. Conditional tiers - should have priority 1, 2, 3, etc. - - - Example ordering: - - - Priority 0: Default tier (no conditions, always matches as - fallback) - - - Priority 1: High usage tier (e.g., >200K tokens) - - - Priority 2: Medium usage tier (e.g., >100K tokens) - - - This ensures more specific conditions are checked before general - ones. - conditions: - type: array - items: - $ref: '#/components/schemas/PricingTierCondition' - description: >- - Array of conditions that must ALL be met for this tier to match (AND - logic). - - - The default tier must have an empty conditions array. Conditional - tiers should have one or more conditions - - that define when this tier's pricing applies. - - - Multiple conditions enable complex matching scenarios (e.g., "high - input tokens AND low output tokens"). - prices: - type: object - additionalProperties: - type: number - format: double - description: >- - Prices (USD) by usage type for this tier. - - - Common usage types: "input", "output", "total", "request", "image" - - Prices are specified in USD per unit (e.g., per token, per request, - per second). - - - Example: {"input": 0.000003, "output": 0.000015} means $3 per - million input tokens and $15 per million output tokens. - required: - - id - - name - - isDefault - - priority - - conditions - - prices - PricingTierInput: - title: PricingTierInput - type: object - description: >- - Input schema for creating a pricing tier. The tier ID will be - automatically generated server-side. - - - When creating a model with pricing tiers: - - - Exactly one tier must have isDefault=true (the fallback tier) - - - The default tier must have priority=0 and conditions=[] - - - All tier names and priorities must be unique within the model - - - Each tier must define at least one price - - - See PricingTier for detailed information about how tiers work and why - they're useful. - properties: - name: - type: string - description: >- - Name of the pricing tier for display and identification purposes. - - - Must be unique within the model. Common patterns: "Standard", "High - Volume Tier", "Extended Context" - isDefault: - type: boolean - description: >- - Whether this is the default tier. Exactly one tier per model must be - marked as default. - - - Requirements for default tier: - - - Must have isDefault=true - - - Must have priority=0 - - - Must have empty conditions array (conditions=[]) - - - The default tier acts as a fallback when no conditional tiers match. - priority: - type: integer - description: >- - Priority for tier matching evaluation. Lower numbers = higher - priority (evaluated first). - - - Must be unique within the model. The default tier must have - priority=0. - - Conditional tiers should use priority 1, 2, 3, etc. based on their - specificity. - conditions: - type: array - items: - $ref: '#/components/schemas/PricingTierCondition' - description: >- - Array of conditions that must ALL be met for this tier to match (AND - logic). - - - The default tier must have an empty array (conditions=[]). - - Conditional tiers should define one or more conditions that specify - when this tier's pricing applies. - - - Each condition specifies a regex pattern, operator, and threshold - value for matching against usage details. - prices: - type: object - additionalProperties: - type: number - format: double - description: >- - Prices (USD) by usage type for this tier. At least one price must be - defined. - - - Common usage types: "input", "output", "total", "request", "image" - - Prices are in USD per unit (e.g., per token). - - - Example: {"input": 0.000003, "output": 0.000015} represents $3 per - million input tokens and $15 per million output tokens. - required: - - name - - isDefault - - priority - - conditions - - prices - PricingTierOperator: - title: PricingTierOperator - type: string - enum: - - gt - - gte - - lt - - lte - - eq - - neq - description: Comparison operators for pricing tier conditions - ModelUsageUnit: - title: ModelUsageUnit - type: string - enum: - - CHARACTERS - - TOKENS - - MILLISECONDS - - SECONDS - - IMAGES - - REQUESTS - description: Unit of usage in Langfuse - ObservationLevel: - title: ObservationLevel - type: string - enum: - - DEBUG - - DEFAULT - - WARNING - - ERROR - MapValue: - title: MapValue - oneOf: - - type: string - nullable: true - - type: integer - nullable: true - - type: number - format: float - nullable: true - - type: boolean - nullable: true - - type: array - items: - type: string - nullable: true - CommentObjectType: - title: CommentObjectType - type: string - enum: - - TRACE - - OBSERVATION - - SESSION - - PROMPT - DatasetStatus: - title: DatasetStatus - type: string - enum: - - ACTIVE - - ARCHIVED - ScoreSource: - title: ScoreSource - type: string - enum: - - ANNOTATION - - API - - EVAL - ScoreConfigDataType: - title: ScoreConfigDataType - type: string - enum: - - NUMERIC - - BOOLEAN - - CATEGORICAL - - TEXT - ScoreDataType: - title: ScoreDataType - type: string - enum: - - NUMERIC - - BOOLEAN - - CATEGORICAL - - CORRECTION - - TEXT - DeleteDatasetItemResponse: - title: DeleteDatasetItemResponse - type: object - properties: - message: - type: string - description: Success message after deletion - required: - - message - CreateDatasetItemRequest: - title: CreateDatasetItemRequest - type: object - properties: - datasetName: - type: string - input: - nullable: true - expectedOutput: - nullable: true - metadata: - nullable: true - sourceTraceId: - type: string - nullable: true - sourceObservationId: - type: string - nullable: true - id: - type: string - nullable: true - description: >- - Dataset items are upserted on their id. Id needs to be unique - (project-level), cannot be reused across datasets, and must be at - most 255 characters. - status: - $ref: '#/components/schemas/DatasetStatus' - nullable: true - description: Defaults to ACTIVE for newly created items - required: - - datasetName - PaginatedDatasetItems: - title: PaginatedDatasetItems - type: object - properties: - data: - type: array - items: - $ref: '#/components/schemas/DatasetItem' - meta: - $ref: '#/components/schemas/utilsMetaResponse' - required: - - data - - meta - CreateDatasetRunItemRequest: - title: CreateDatasetRunItemRequest - type: object - properties: - runName: - type: string - runDescription: - type: string - nullable: true - description: Description of the run. If run exists, description will be updated. - metadata: - nullable: true - description: Metadata of the dataset run, updates run if run already exists - datasetItemId: - type: string - observationId: - type: string - nullable: true - traceId: - type: string - nullable: true - description: >- - traceId should always be provided. For compatibility with older SDK - versions it can also be inferred from the provided observationId. - datasetVersion: - type: string - format: date-time - nullable: true - description: >- - ISO 8601 timestamp (RFC 3339, Section 5.6) in UTC (e.g., - "2026-01-21T14:35:42Z"). - - Specifies the dataset version to use for this experiment run. - - If provided, the experiment will use dataset items as they existed - at or before this timestamp. - - If not provided, uses the latest version of dataset items. - createdAt: - type: string - format: date-time - nullable: true - description: >- - Optional timestamp to set the createdAt field of the dataset run - item. If not provided or null, defaults to current timestamp. - required: - - runName - - datasetItemId - PaginatedDatasetRunItems: - title: PaginatedDatasetRunItems - type: object - properties: - data: - type: array - items: - $ref: '#/components/schemas/DatasetRunItem' - meta: - $ref: '#/components/schemas/utilsMetaResponse' - required: - - data - - meta - PaginatedDatasets: - title: PaginatedDatasets - type: object - properties: - data: - type: array - items: - $ref: '#/components/schemas/Dataset' - meta: - $ref: '#/components/schemas/utilsMetaResponse' - required: - - data - - meta - CreateDatasetRequest: - title: CreateDatasetRequest - type: object - properties: - name: - type: string - description: - type: string - nullable: true - metadata: - nullable: true - inputSchema: - nullable: true - description: >- - JSON Schema for validating dataset item inputs. When set, all new - and existing dataset items will be validated against this schema. - expectedOutputSchema: - nullable: true - description: >- - JSON Schema for validating dataset item expected outputs. When set, - all new and existing dataset items will be validated against this - schema. - required: - - name - PaginatedDatasetRuns: - title: PaginatedDatasetRuns - type: object - properties: - data: - type: array - items: - $ref: '#/components/schemas/DatasetRun' - meta: - $ref: '#/components/schemas/utilsMetaResponse' - required: - - data - - meta - DeleteDatasetRunResponse: - title: DeleteDatasetRunResponse - type: object - properties: - message: - type: string - required: - - message - ExperimentsResponse: - title: ExperimentsResponse - type: object - properties: - data: - type: array - items: - $ref: '#/components/schemas/Experiment' - meta: - $ref: '#/components/schemas/ExperimentsResponseMeta' - required: - - data - - meta - ExperimentsResponseMeta: - title: ExperimentsResponseMeta - type: object - properties: - cursor: - type: string - nullable: true - description: >- - Versioned base64url cursor for retrieving the next page. Absent when - there are no more results. - Experiment: - title: Experiment - type: object - properties: - id: - type: string - name: - type: string - description: - type: string - nullable: true - startTime: - type: string - format: date-time - description: |- - Start of the experiment, i.e. the earliest event within the - requested time range. Clipped to `fromStartTime` when the - experiment started before the requested range. - endTime: - type: string - format: date-time - description: |- - End of the experiment, i.e. the latest event end within the - requested time range. - itemCount: - type: integer - description: Number of experiment items within the requested time range. - datasetId: - type: string - nullable: true - description: Null when the experiment is not associated with a dataset. - metadata: - type: object - additionalProperties: true - nullable: true - description: Included only when `fields=metadata` is requested. - scores: - type: array - items: - $ref: '#/components/schemas/ScoreV3' - nullable: true - description: >- - Included only when `fields=scores` is requested. Contains scores - directly attached to the experiment. - required: - - id - - name - - description - - startTime - - endTime - - itemCount - - datasetId - ExperimentItemsResponse: - title: ExperimentItemsResponse - type: object - properties: - data: - type: array - items: - $ref: '#/components/schemas/ExperimentItem' - meta: - $ref: '#/components/schemas/ExperimentsResponseMeta' - required: - - data - - meta - ExperimentItem: - title: ExperimentItem - type: object - properties: - id: - type: string - traceId: - type: string - startTime: - type: string - format: date-time - endTime: - type: string - format: date-time - nullable: true - level: - $ref: '#/components/schemas/ObservationLevel' - environment: - type: string - experimentId: - type: string - experimentName: - type: string - experimentItemId: - type: string - experimentDatasetId: - type: string - nullable: true - description: Included when `fields=dataset` is requested. - experimentItemVersion: - type: string - format: date-time - nullable: true - description: Included when `fields=dataset` is requested. - input: - nullable: true - description: Included when `fields=io` is requested. - output: - nullable: true - description: Included when `fields=io` is requested. - expectedOutput: - nullable: true - description: Included when `fields=io` is requested. - metadata: - type: object - additionalProperties: true - nullable: true - description: Included when `fields=metadata` is requested. - experimentItemMetadata: - type: object - additionalProperties: true - nullable: true - description: Included when `fields=itemMetadata` is requested. - experimentMetadata: - type: object - additionalProperties: true - nullable: true - description: Included when `fields=experimentMetadata` is requested. - experimentDescription: - type: string - nullable: true - description: Included when `fields=experimentMetadata` is requested. - scores: - type: array - items: - $ref: '#/components/schemas/ScoreV3' - nullable: true - description: >- - Included only when `fields=scores` is requested. Contains item and - trace scores only; experiment-level scores are returned by the - experiments endpoint. - required: - - id - - traceId - - startTime - - endTime - - level - - environment - - experimentId - - experimentName - - experimentItemId - HealthResponse: - title: HealthResponse - type: object - properties: - version: - type: string - description: Langfuse server version - status: - type: string - required: - - version - - status - IngestionEvent: - title: IngestionEvent - type: object - properties: - type: - type: string - enum: - - trace-create - - score-create - - span-create - - span-update - - generation-create - - generation-update - - event-create - - sdk-log - - observation-create - - observation-update - body: - type: string - required: - - type - - body - ObservationType: - title: ObservationType - type: string - enum: - - SPAN - - GENERATION - - EVENT - - AGENT - - TOOL - - CHAIN - - RETRIEVER - - EVALUATOR - - EMBEDDING - - GUARDRAIL - IngestionUsage: - title: IngestionUsage - oneOf: - - $ref: '#/components/schemas/Usage' - - $ref: '#/components/schemas/OpenAIUsage' - OpenAIUsage: - title: OpenAIUsage - type: object - description: Usage interface of OpenAI for improved compatibility. - properties: - promptTokens: - type: integer - nullable: true - completionTokens: - type: integer - nullable: true - totalTokens: - type: integer - nullable: true - OptionalObservationBody: - title: OptionalObservationBody - type: object - properties: - traceId: - type: string - nullable: true - name: - type: string - nullable: true - startTime: - type: string - format: date-time - nullable: true - metadata: - nullable: true - input: - nullable: true - output: - nullable: true - level: - $ref: '#/components/schemas/ObservationLevel' - nullable: true - statusMessage: - type: string - nullable: true - parentObservationId: - type: string - nullable: true - version: - type: string - nullable: true - environment: - type: string - nullable: true - CreateEventBody: - title: CreateEventBody - type: object - properties: - id: - type: string - nullable: true - allOf: - - $ref: '#/components/schemas/OptionalObservationBody' - UpdateEventBody: - title: UpdateEventBody - type: object - properties: - id: - type: string - required: - - id - allOf: - - $ref: '#/components/schemas/OptionalObservationBody' - CreateSpanBody: - title: CreateSpanBody - type: object - properties: - endTime: - type: string - format: date-time - nullable: true - allOf: - - $ref: '#/components/schemas/CreateEventBody' - UpdateSpanBody: - title: UpdateSpanBody - type: object - properties: - endTime: - type: string - format: date-time - nullable: true - allOf: - - $ref: '#/components/schemas/UpdateEventBody' - CreateGenerationBody: - title: CreateGenerationBody - type: object - properties: - completionStartTime: - type: string - format: date-time - nullable: true - model: - type: string - nullable: true - modelParameters: - type: object - additionalProperties: - $ref: '#/components/schemas/MapValue' - nullable: true - usage: - $ref: '#/components/schemas/IngestionUsage' - nullable: true - usageDetails: - $ref: '#/components/schemas/UsageDetails' - nullable: true - costDetails: - type: object - additionalProperties: - type: number - format: double - nullable: true - promptName: - type: string - nullable: true - promptVersion: - type: integer - nullable: true - allOf: - - $ref: '#/components/schemas/CreateSpanBody' - UpdateGenerationBody: - title: UpdateGenerationBody - type: object - properties: - completionStartTime: - type: string - format: date-time - nullable: true - model: - type: string - nullable: true - modelParameters: - type: object - additionalProperties: - $ref: '#/components/schemas/MapValue' - nullable: true - usage: - $ref: '#/components/schemas/IngestionUsage' - nullable: true - promptName: - type: string - nullable: true - usageDetails: - $ref: '#/components/schemas/UsageDetails' - nullable: true - costDetails: - type: object - additionalProperties: - type: number - format: double - nullable: true - promptVersion: - type: integer - nullable: true - allOf: - - $ref: '#/components/schemas/UpdateSpanBody' - ObservationBody: - title: ObservationBody - type: object - properties: - id: - type: string - nullable: true - traceId: - type: string - nullable: true - type: - $ref: '#/components/schemas/ObservationType' - name: - type: string - nullable: true - startTime: - type: string - format: date-time - nullable: true - endTime: - type: string - format: date-time - nullable: true - completionStartTime: - type: string - format: date-time - nullable: true - model: - type: string - nullable: true - modelParameters: - type: object - additionalProperties: - $ref: '#/components/schemas/MapValue' - nullable: true - input: - nullable: true - version: - type: string - nullable: true - metadata: - nullable: true - output: - nullable: true - usage: - $ref: '#/components/schemas/Usage' - nullable: true - level: - $ref: '#/components/schemas/ObservationLevel' - nullable: true - statusMessage: - type: string - nullable: true - parentObservationId: - type: string - nullable: true - environment: - type: string - nullable: true - required: - - type - TraceBody: - title: TraceBody - type: object - properties: - id: - type: string - nullable: true - timestamp: - type: string - format: date-time - nullable: true - name: - type: string - nullable: true - userId: - type: string - nullable: true - input: - nullable: true - output: - nullable: true - sessionId: - type: string - nullable: true - release: - type: string - nullable: true - version: - type: string - nullable: true - metadata: - nullable: true - tags: - type: array - items: - type: string - nullable: true - environment: - type: string - nullable: true - public: - type: boolean - nullable: true - description: Make trace publicly accessible via url - SDKLogBody: - title: SDKLogBody - type: object - properties: - log: {} - required: - - log - ScoreBody: - title: ScoreBody - type: object - properties: - id: - type: string - nullable: true - traceId: - type: string - nullable: true - sessionId: - type: string - nullable: true - observationId: - type: string - nullable: true - datasetRunId: - type: string - nullable: true - name: - type: string - description: >- - The name of the score. Always overrides "output" for correction - scores. - environment: - type: string - nullable: true - queueId: - type: string - nullable: true - description: >- - The annotation queue referenced by the score. Indicates if score was - initially created while processing annotation queue. - value: - $ref: '#/components/schemas/CreateScoreValue' - description: >- - The value of the score. Must be passed as string for categorical and - text scores, and numeric for boolean and numeric scores. Boolean - score values must equal either 1 or 0 (true or false). Text score - values must be between 1 and 500 characters. - comment: - type: string - nullable: true - metadata: - nullable: true - dataType: - $ref: '#/components/schemas/ScoreDataType' - nullable: true - description: >- - When set, must match the score value's type. If not set, will be - inferred from the score value or config - configId: - type: string - nullable: true - description: >- - Reference a score config on a score. When set, the score name must - equal the config name and scores must comply with the config's range - and data type. For categorical scores, the value must map to a - config category. Numeric scores might be constrained by the score - config's max and min values - required: - - name - - value - BaseEvent: - title: BaseEvent - type: object - properties: - id: - type: string - description: UUID v4 that identifies the event - timestamp: - type: string - description: >- - Datetime (ISO 8601) of event creation in client. Should be as close - to actual event creation in client as possible, this timestamp will - be used for ordering of events in future release. Resolution: - milliseconds (required), microseconds (optimal). - metadata: - nullable: true - description: Optional. Metadata field used by the Langfuse SDKs for debugging. - required: - - id - - timestamp - TraceEvent: - title: TraceEvent - type: object - properties: - body: - $ref: '#/components/schemas/TraceBody' - required: - - body - allOf: - - $ref: '#/components/schemas/BaseEvent' - CreateObservationEvent: - title: CreateObservationEvent - type: object - properties: - body: - $ref: '#/components/schemas/ObservationBody' - required: - - body - allOf: - - $ref: '#/components/schemas/BaseEvent' - UpdateObservationEvent: - title: UpdateObservationEvent - type: object - properties: - body: - $ref: '#/components/schemas/ObservationBody' - required: - - body - allOf: - - $ref: '#/components/schemas/BaseEvent' - ScoreEvent: - title: ScoreEvent - type: object - properties: - body: - $ref: '#/components/schemas/ScoreBody' - required: - - body - allOf: - - $ref: '#/components/schemas/BaseEvent' - SDKLogEvent: - title: SDKLogEvent - type: object - properties: - body: - $ref: '#/components/schemas/SDKLogBody' - required: - - body - allOf: - - $ref: '#/components/schemas/BaseEvent' - CreateGenerationEvent: - title: CreateGenerationEvent - type: object - properties: - body: - $ref: '#/components/schemas/CreateGenerationBody' - required: - - body - allOf: - - $ref: '#/components/schemas/BaseEvent' - UpdateGenerationEvent: - title: UpdateGenerationEvent - type: object - properties: - body: - $ref: '#/components/schemas/UpdateGenerationBody' - required: - - body - allOf: - - $ref: '#/components/schemas/BaseEvent' - CreateSpanEvent: - title: CreateSpanEvent - type: object - properties: - body: - $ref: '#/components/schemas/CreateSpanBody' - required: - - body - allOf: - - $ref: '#/components/schemas/BaseEvent' - UpdateSpanEvent: - title: UpdateSpanEvent - type: object - properties: - body: - $ref: '#/components/schemas/UpdateSpanBody' - required: - - body - allOf: - - $ref: '#/components/schemas/BaseEvent' - CreateEventEvent: - title: CreateEventEvent - type: object - properties: - body: - $ref: '#/components/schemas/CreateEventBody' - required: - - body - allOf: - - $ref: '#/components/schemas/BaseEvent' - IngestionSuccess: - title: IngestionSuccess - type: object - properties: - id: - type: string - status: - type: integer - required: - - id - - status - IngestionError: - title: IngestionError - type: object - properties: - id: - type: string - status: - type: integer - message: - type: string - nullable: true - error: - nullable: true - required: - - id - - status - IngestionResponse: - title: IngestionResponse - type: object - properties: - successes: - type: array - items: - $ref: '#/components/schemas/IngestionSuccess' - errors: - type: array - items: - $ref: '#/components/schemas/IngestionError' - required: - - successes - - errors - OpenAICompletionUsageSchema: - title: OpenAICompletionUsageSchema - type: object - description: OpenAI Usage schema from (Chat-)Completion APIs - properties: - prompt_tokens: - type: integer - completion_tokens: - type: integer - total_tokens: - type: integer - prompt_tokens_details: - type: object - additionalProperties: - type: integer - nullable: true - nullable: true - completion_tokens_details: - type: object - additionalProperties: - type: integer - nullable: true - nullable: true - required: - - prompt_tokens - - completion_tokens - - total_tokens - OpenAIResponseUsageSchema: - title: OpenAIResponseUsageSchema - type: object - description: OpenAI Usage schema from Response API - properties: - input_tokens: - type: integer - output_tokens: - type: integer - total_tokens: - type: integer - input_tokens_details: - type: object - additionalProperties: - type: integer - nullable: true - nullable: true - output_tokens_details: - type: object - additionalProperties: - type: integer - nullable: true - nullable: true - required: - - input_tokens - - output_tokens - - total_tokens - UsageDetails: - title: UsageDetails - oneOf: - - type: object - additionalProperties: - type: integer - - $ref: '#/components/schemas/OpenAICompletionUsageSchema' - - $ref: '#/components/schemas/OpenAIResponseUsageSchema' - legacyMetricsResponse: - title: legacyMetricsResponse - type: object - properties: - data: - type: array - items: - type: object - additionalProperties: true - description: >- - The metrics data. Each item in the list contains the metric values - and dimensions requested in the query. - - Format varies based on the query parameters. - - Histograms will return an array with [lower, upper, height] tuples. - required: - - data - legacyObservations: - title: legacyObservations - type: object - properties: - data: - type: array - items: - $ref: '#/components/schemas/Observation' - meta: - $ref: '#/components/schemas/utilsMetaResponse' - required: - - data - - meta - legacyObservationsViews: - title: legacyObservationsViews - type: object - properties: - data: - type: array - items: - $ref: '#/components/schemas/ObservationsView' - meta: - $ref: '#/components/schemas/utilsMetaResponse' - required: - - data - - meta - legacyCreateScoreRequest: - title: legacyCreateScoreRequest - type: object - properties: - id: - type: string - nullable: true - traceId: - type: string - nullable: true - sessionId: - type: string - nullable: true - observationId: - type: string - nullable: true - datasetRunId: - type: string - nullable: true - name: - type: string - value: - $ref: '#/components/schemas/CreateScoreValue' - description: >- - The value of the score. Must be passed as string for categorical and - text scores, and numeric for boolean and numeric scores. Boolean - score values must equal either 1 or 0 (true or false). Text score - values must be between 1 and 500 characters. - comment: - type: string - nullable: true - metadata: - type: object - additionalProperties: true - nullable: true - environment: - type: string - nullable: true - description: >- - The environment of the score. Can be any lowercase alphanumeric - string with hyphens and underscores that does not start with - 'langfuse'. - queueId: - type: string - nullable: true - description: >- - The annotation queue referenced by the score. Indicates if score was - initially created while processing annotation queue. - dataType: - $ref: '#/components/schemas/ScoreDataType' - nullable: true - description: >- - The data type of the score. When passing a configId this field is - inferred. Otherwise, this field must be passed or will default to - numeric. - configId: - type: string - nullable: true - description: >- - Reference a score config on a score. The unique langfuse identifier - of a score config. When passing this field, the dataType and - stringValue fields are automatically populated. - source: - $ref: '#/components/schemas/legacyCreateScoreSource' - nullable: true - description: >- - The source of the score. Defaults to API. Set to ANNOTATION to - prefill scores (e.g. from an LLM) for a human reviewer to verify in - an annotation queue. When source is ANNOTATION, a configId is - required unless dataType is CORRECTION. EVAL is reserved for - internal evaluator outputs and is not accepted on this endpoint. - required: - - name - - value - legacyCreateScoreSource: - title: legacyCreateScoreSource - type: string - enum: - - API - - ANNOTATION - description: |- - Source values accepted when creating a score via the public REST API. - EVAL is reserved for internal evaluator outputs and is intentionally not - exposed here — use commons.ScoreSource when reading scores. - legacyCreateScoreResponse: - title: legacyCreateScoreResponse - type: object - properties: - id: - type: string - description: The id of the created object in Langfuse - required: - - id - LlmConnection: - title: LlmConnection - type: object - description: LLM API connection configuration (secrets excluded) - properties: - id: - type: string - provider: - type: string - description: >- - Provider name (e.g., 'openai', 'my-gateway'). Must be unique in - project, used for upserting. - adapter: - type: string - description: The adapter used to interface with the LLM - displaySecretKey: - type: string - description: Masked version of the secret key for display purposes - baseURL: - type: string - nullable: true - description: Custom base URL for the LLM API - customModels: - type: array - items: - type: string - description: List of custom model names available for this connection - withDefaultModels: - type: boolean - description: Whether to include default models for this adapter - extraHeaderKeys: - type: array - items: - type: string - description: >- - Keys of extra headers sent with requests (values excluded for - security) - config: - type: object - additionalProperties: true - nullable: true - description: >- - Adapter-specific configuration. Required for Bedrock - (`{"region":"us-east-1"}`), optional for OpenAI - (`{"useResponsesApi":true}`), optional for VertexAI - (`{"location":"us-central1"}`), not used by other adapters. - createdAt: - type: string - format: date-time - updatedAt: - type: string - format: date-time - required: - - id - - provider - - adapter - - displaySecretKey - - customModels - - withDefaultModels - - extraHeaderKeys - - createdAt - - updatedAt - PaginatedLlmConnections: - title: PaginatedLlmConnections - type: object - properties: - data: - type: array - items: - $ref: '#/components/schemas/LlmConnection' - meta: - $ref: '#/components/schemas/utilsMetaResponse' - required: - - data - - meta - UpsertLlmConnectionRequest: - title: UpsertLlmConnectionRequest - type: object - description: Request to create or update an LLM connection (upsert) - properties: - provider: - type: string - description: >- - Provider name (e.g., 'openai', 'my-gateway'). Must be unique in - project, used for upserting. - adapter: - $ref: '#/components/schemas/LlmAdapter' - description: The adapter used to interface with the LLM - secretKey: - type: string - description: Secret key for the LLM API. - baseURL: - type: string - nullable: true - description: Custom base URL for the LLM API - customModels: - type: array - items: - type: string - nullable: true - description: List of custom model names - withDefaultModels: - type: boolean - nullable: true - description: Whether to include default models. Default is true. - extraHeaders: - type: object - additionalProperties: - type: string - nullable: true - description: Extra headers to send with requests - config: - type: object - additionalProperties: true - nullable: true - description: >- - Adapter-specific configuration. Validation rules: - **Bedrock**: - Required. Must be `{"region": ""}` (e.g., - `{"region":"us-east-1"}`) - **OpenAI**: Optional. If provided, must - be `{"useResponsesApi": }` to control whether Langfuse - routes calls through OpenAI's Responses API. - **VertexAI**: - Optional. If provided, must be `{"location": ""}` - (e.g., `{"location":"us-central1"}`) - **Other adapters**: Not - supported. Omit this field or set to null. - required: - - provider - - adapter - - secretKey - DeleteLlmConnectionResponse: - title: DeleteLlmConnectionResponse - type: object - properties: - message: - type: string - required: - - message - LlmAdapter: - title: LlmAdapter - type: string - enum: - - anthropic - - openai - - azure - - bedrock - - google-vertex-ai - - google-ai-studio - GetMediaResponse: - title: GetMediaResponse - type: object - properties: - mediaId: - type: string - description: The unique langfuse identifier of a media record - contentType: - type: string - description: The MIME type of the media record - contentLength: - type: integer - description: The size of the media record in bytes - uploadedAt: - type: string - format: date-time - description: The date and time when the media record was uploaded - url: - type: string - description: The download URL of the media record - urlExpiry: - type: string - description: The expiry date and time of the media record download URL - required: - - mediaId - - contentType - - contentLength - - uploadedAt - - url - - urlExpiry - PatchMediaBody: - title: PatchMediaBody - type: object - properties: - uploadedAt: - type: string - format: date-time - description: The date and time when the media record was uploaded - uploadHttpStatus: - type: integer - description: The HTTP status code of the upload - uploadHttpError: - type: string - nullable: true - description: The HTTP error message of the upload - uploadTimeMs: - type: integer - nullable: true - description: The time in milliseconds it took to upload the media record - required: - - uploadedAt - - uploadHttpStatus - GetMediaUploadUrlRequest: - title: GetMediaUploadUrlRequest - type: object - description: >- - Request a presigned media upload URL. Provide exactly one context: a - trace (traceId, optionally observationId) or a dataset item (datasetId + - datasetItemId). field is required and must match the chosen context. - properties: - traceId: - type: string - nullable: true - description: >- - The trace the media is associated with. Null for dataset item media - uploads. - observationId: - type: string - nullable: true - description: >- - The observation ID associated with the media record. If the media - record is associated directly with a trace, this will be null. - datasetId: - type: string - nullable: true - description: >- - The dataset the media belongs to. Null for trace/observation media - uploads. - datasetItemId: - type: string - nullable: true - description: >- - The dataset item the media is associated with (need not exist yet). - Null for trace/observation media uploads. - contentType: - $ref: '#/components/schemas/MediaContentType' - contentLength: - type: integer - description: The size of the media record in bytes - sha256Hash: - type: string - description: The SHA-256 hash of the media record - field: - type: string - description: >- - The item field the media is in: `input`/`output`/`metadata` (trace) - or `input`/`expectedOutput`/`metadata` (dataset item). - required: - - contentType - - contentLength - - sha256Hash - - field - GetMediaUploadUrlResponse: - title: GetMediaUploadUrlResponse - type: object - properties: - uploadUrl: - type: string - nullable: true - description: >- - The presigned upload URL. If the asset is already uploaded, this - will be null - mediaId: - type: string - description: The unique langfuse identifier of a media record - required: - - mediaId - MediaContentType: - title: MediaContentType - type: string - enum: - - image/png - - image/jpeg - - image/jpg - - image/webp - - image/gif - - image/svg+xml - - image/tiff - - image/bmp - - image/avif - - image/heic - - audio/mpeg - - audio/mp3 - - audio/wav - - audio/ogg - - audio/oga - - audio/aac - - audio/mp4 - - audio/flac - - audio/opus - - audio/webm - - video/mp4 - - video/webm - - video/ogg - - video/mpeg - - video/quicktime - - video/x-msvideo - - video/x-matroska - - text/plain - - text/html - - text/css - - text/csv - - text/markdown - - text/x-python - - application/javascript - - text/x-typescript - - application/x-yaml - - application/pdf - - application/msword - - application/vnd.ms-excel - - application/vnd.openxmlformats-officedocument.spreadsheetml.sheet - - application/zip - - application/json - - application/xml - - application/octet-stream - - >- - application/vnd.openxmlformats-officedocument.wordprocessingml.document - - >- - application/vnd.openxmlformats-officedocument.presentationml.presentation - - application/rtf - - application/x-ndjson - - application/vnd.apache.parquet - - application/gzip - - application/x-tar - - application/x-7z-compressed - description: The MIME type of the media record - MetricsV2Response: - title: MetricsV2Response - type: object - properties: - data: - type: array - items: - type: object - additionalProperties: true - description: >- - The metrics data. Each item in the list contains the metric values - and dimensions requested in the query. - - Format varies based on the query parameters. - - Histograms will return an array with [lower, upper, height] tuples. - required: - - data - PaginatedModels: - title: PaginatedModels - type: object - properties: - data: - type: array - items: - $ref: '#/components/schemas/Model' - meta: - $ref: '#/components/schemas/utilsMetaResponse' - required: - - data - - meta - CreateModelRequest: - title: CreateModelRequest - type: object - properties: - modelName: - type: string - description: >- - Name of the model definition. If multiple with the same name exist, - they are applied in the following order: (1) custom over built-in, - (2) newest according to startTime where - model.startTime- - Regex pattern which matches this model definition to - generation.model. Useful in case of fine-tuned models. If you want - to exact match, use `(?i)^modelname$` - startDate: - type: string - format: date-time - nullable: true - description: Apply only to generations which are newer than this ISO date. - unit: - $ref: '#/components/schemas/ModelUsageUnit' - nullable: true - description: Unit used by this model. - inputPrice: - type: number - format: double - nullable: true - description: >- - Deprecated. Use 'pricingTiers' instead. Price (USD) per input unit. - Creates a default tier if pricingTiers not provided. - outputPrice: - type: number - format: double - nullable: true - description: >- - Deprecated. Use 'pricingTiers' instead. Price (USD) per output unit. - Creates a default tier if pricingTiers not provided. - totalPrice: - type: number - format: double - nullable: true - description: >- - Deprecated. Use 'pricingTiers' instead. Price (USD) per total units. - Cannot be set if input or output price is set. Creates a default - tier if pricingTiers not provided. - pricingTiers: - type: array - items: - $ref: '#/components/schemas/PricingTierInput' - nullable: true - description: >- - Optional. Array of pricing tiers for this model. - - - Use pricing tiers for all models - both those with threshold-based - pricing variations and those with simple flat pricing: - - - - For models with standard flat pricing: Create a single default - tier with your prices - (e.g., one tier with isDefault=true, priority=0, conditions=[], and your standard prices) - - - For models with threshold-based pricing: Create a default tier - plus additional conditional tiers - (e.g., default tier for standard usage + high-volume tier for usage above certain thresholds) - - Requirements: - - - Cannot be provided with flat prices - (inputPrice/outputPrice/totalPrice) - use one approach or the other - - - Must include exactly one default tier with isDefault=true, - priority=0, and conditions=[] - - - All tier names and priorities must be unique within the model - - - Each tier must define at least one price - - - If omitted, you must provide flat prices instead - (inputPrice/outputPrice/totalPrice), - - which will automatically create a single default tier named - "Standard". - tokenizerId: - type: string - nullable: true - description: >- - Optional. Tokenizer to be applied to observations which match to - this model. See docs for more details. - tokenizerConfig: - nullable: true - description: >- - Optional. Configuration for the selected tokenizer. Needs to be - JSON. See docs for more details. - required: - - modelName - - matchPattern - ObservationsV2Response: - title: ObservationsV2Response - type: object - description: >- - Response containing observations with field-group-based filtering and - cursor-based pagination. - - - The `data` array contains observation objects with only the requested - field groups included. - - Use the `cursor` in `meta` to retrieve the next page of results. - properties: - data: - type: array - items: - $ref: '#/components/schemas/ObservationV2' - description: >- - Array of observation objects. Fields included depend on the `fields` - parameter in the request. - meta: - $ref: '#/components/schemas/ObservationsV2Meta' - required: - - data - - meta - ObservationsV2Meta: - title: ObservationsV2Meta - type: object - description: Metadata for cursor-based pagination - properties: - cursor: - type: string - nullable: true - description: >- - Base64-encoded cursor to use for retrieving the next page. If not - present, there are no more results. - OtelResourceSpan: - title: OtelResourceSpan - type: object - description: >- - Represents a collection of spans from a single resource as per OTLP - specification - properties: - resource: - $ref: '#/components/schemas/OtelResource' - nullable: true - description: Resource information - scopeSpans: - type: array - items: - $ref: '#/components/schemas/OtelScopeSpan' - nullable: true - description: Array of scope spans - OtelResource: - title: OtelResource - type: object - description: Resource attributes identifying the source of telemetry - properties: - attributes: - type: array - items: - $ref: '#/components/schemas/OtelAttribute' - nullable: true - description: Resource attributes like service.name, service.version, etc. - OtelScopeSpan: - title: OtelScopeSpan - type: object - description: Collection of spans from a single instrumentation scope - properties: - scope: - $ref: '#/components/schemas/OtelScope' - nullable: true - description: Instrumentation scope information - spans: - type: array - items: - $ref: '#/components/schemas/OtelSpan' - nullable: true - description: Array of spans - OtelScope: - title: OtelScope - type: object - description: Instrumentation scope information - properties: - name: - type: string - nullable: true - description: Instrumentation scope name - version: - type: string - nullable: true - description: Instrumentation scope version - attributes: - type: array - items: - $ref: '#/components/schemas/OtelAttribute' - nullable: true - description: Additional scope attributes - OtelSpan: - title: OtelSpan - type: object - description: Individual span representing a unit of work or operation - properties: - traceId: - nullable: true - description: Trace ID (16 bytes, hex-encoded string in JSON or Buffer in binary) - spanId: - nullable: true - description: Span ID (8 bytes, hex-encoded string in JSON or Buffer in binary) - parentSpanId: - nullable: true - description: Parent span ID if this is a child span - name: - type: string - nullable: true - description: Span name describing the operation - kind: - type: integer - nullable: true - description: Span kind (1=INTERNAL, 2=SERVER, 3=CLIENT, 4=PRODUCER, 5=CONSUMER) - startTimeUnixNano: - nullable: true - description: Start time in nanoseconds since Unix epoch - endTimeUnixNano: - nullable: true - description: End time in nanoseconds since Unix epoch - attributes: - type: array - items: - $ref: '#/components/schemas/OtelAttribute' - nullable: true - description: >- - Span attributes including Langfuse-specific attributes - (langfuse.observation.*) - status: - nullable: true - description: Span status object - OtelAttribute: - title: OtelAttribute - type: object - description: Key-value attribute pair for resources, scopes, or spans - properties: - key: - type: string - nullable: true - description: Attribute key (e.g., "service.name", "langfuse.observation.type") - value: - $ref: '#/components/schemas/OtelAttributeValue' - nullable: true - description: Attribute value - OtelAttributeValue: - title: OtelAttributeValue - type: object - description: Attribute value wrapper supporting different value types - properties: - stringValue: - type: string - nullable: true - description: String value - intValue: - type: integer - nullable: true - description: Integer value - doubleValue: - type: number - format: double - nullable: true - description: Double value - boolValue: - type: boolean - nullable: true - description: Boolean value - OtelTraceResponse: - title: OtelTraceResponse - type: object - description: Response from trace export request. Empty object indicates success. - properties: {} - MembershipRole: - title: MembershipRole - type: string - enum: - - OWNER - - ADMIN - - MEMBER - - VIEWER - MembershipRequest: - title: MembershipRequest - type: object - properties: - userId: - type: string - role: - $ref: '#/components/schemas/MembershipRole' - required: - - userId - - role - DeleteMembershipRequest: - title: DeleteMembershipRequest - type: object - properties: - userId: - type: string - required: - - userId - MembershipResponse: - title: MembershipResponse - type: object - properties: - userId: - type: string - role: - $ref: '#/components/schemas/MembershipRole' - email: - type: string - name: - type: string - required: - - userId - - role - - email - - name - MembershipDeletionResponse: - title: MembershipDeletionResponse - type: object - properties: - message: - type: string - userId: - type: string - required: - - message - - userId - MembershipsResponse: - title: MembershipsResponse - type: object - properties: - memberships: - type: array - items: - $ref: '#/components/schemas/MembershipResponse' - required: - - memberships - OrganizationProject: - title: OrganizationProject - type: object - properties: - id: - type: string - name: - type: string - metadata: - type: object - additionalProperties: true - nullable: true - createdAt: - type: string - format: date-time - updatedAt: - type: string - format: date-time - required: - - id - - name - - createdAt - - updatedAt - OrganizationProjectsResponse: - title: OrganizationProjectsResponse - type: object - properties: - projects: - type: array - items: - $ref: '#/components/schemas/OrganizationProject' - required: - - projects - OrganizationApiKey: - title: OrganizationApiKey - type: object - properties: - id: - type: string - createdAt: - type: string - format: date-time - expiresAt: - type: string - format: date-time - nullable: true - lastUsedAt: - type: string - format: date-time - nullable: true - note: - type: string - nullable: true - publicKey: - type: string - displaySecretKey: - type: string - required: - - id - - createdAt - - publicKey - - displaySecretKey - OrganizationApiKeysResponse: - title: OrganizationApiKeysResponse - type: object - properties: - apiKeys: - type: array - items: - $ref: '#/components/schemas/OrganizationApiKey' - required: - - apiKeys - Projects: - title: Projects - type: object - properties: - data: - type: array - items: - $ref: '#/components/schemas/Project' - required: - - data - Organization: - title: Organization - type: object - properties: - id: - type: string - description: The unique identifier of the organization - name: - type: string - description: The name of the organization - required: - - id - - name - Project: - title: Project - type: object - properties: - id: - type: string - name: - type: string - organization: - $ref: '#/components/schemas/Organization' - description: The organization this project belongs to - metadata: - type: object - additionalProperties: true - description: Metadata for the project - retentionDays: - type: integer - nullable: true - description: >- - Number of days to retain data. Null or 0 means no retention. Omitted - if no retention is configured. - required: - - id - - name - - organization - - metadata - ProjectDeletionResponse: - title: ProjectDeletionResponse - type: object - properties: - success: - type: boolean - message: - type: string - required: - - success - - message - ApiKeyList: - title: ApiKeyList - type: object - description: List of API keys for a project - properties: - apiKeys: - type: array - items: - $ref: '#/components/schemas/ApiKeySummary' - required: - - apiKeys - ApiKeySummary: - title: ApiKeySummary - type: object - description: Summary of an API key - properties: - id: - type: string - createdAt: - type: string - format: date-time - expiresAt: - type: string - format: date-time - nullable: true - lastUsedAt: - type: string - format: date-time - nullable: true - note: - type: string - nullable: true - publicKey: - type: string - displaySecretKey: - type: string - required: - - id - - createdAt - - publicKey - - displaySecretKey - ApiKeyResponse: - title: ApiKeyResponse - type: object - description: Response for API key creation - properties: - id: - type: string - createdAt: - type: string - format: date-time - publicKey: - type: string - secretKey: - type: string - displaySecretKey: - type: string - note: - type: string - nullable: true - required: - - id - - createdAt - - publicKey - - secretKey - - displaySecretKey - ApiKeyDeletionResponse: - title: ApiKeyDeletionResponse - type: object - description: Response for API key deletion - properties: - success: - type: boolean - required: - - success - PromptMetaListResponse: - title: PromptMetaListResponse - type: object - properties: - data: - type: array - items: - $ref: '#/components/schemas/PromptMeta' - meta: - $ref: '#/components/schemas/utilsMetaResponse' - required: - - data - - meta - PromptMeta: - title: PromptMeta - type: object - properties: - name: - type: string - type: - $ref: '#/components/schemas/PromptType' - description: Indicates whether the prompt is a text or chat prompt. - versions: - type: array - items: - type: integer - labels: - type: array - items: - type: string - tags: - type: array - items: - type: string - lastUpdatedAt: - type: string - format: date-time - lastConfig: - description: >- - Config object of the most recent prompt version that matches the - filters (if any are provided) - required: - - name - - type - - versions - - labels - - tags - - lastUpdatedAt - - lastConfig - CreatePromptRequest: - title: CreatePromptRequest - oneOf: - - $ref: '#/components/schemas/CreateChatPromptRequest' - - $ref: '#/components/schemas/CreateTextPromptRequest' - CreateChatPromptRequest: - title: CreateChatPromptRequest - type: object - properties: - name: - type: string - prompt: - type: array - items: - $ref: '#/components/schemas/ChatMessageWithPlaceholders' - config: - nullable: true - type: - $ref: '#/components/schemas/CreateChatPromptType' - labels: - type: array - items: - type: string - nullable: true - description: List of deployment labels of this prompt version. - tags: - type: array - items: - type: string - nullable: true - description: List of tags to apply to all versions of this prompt. - commitMessage: - type: string - nullable: true - description: Commit message for this prompt version. - required: - - name - - prompt - - type - CreateTextPromptRequest: - title: CreateTextPromptRequest - type: object - properties: - name: - type: string - prompt: - type: string - config: - nullable: true - type: - $ref: '#/components/schemas/CreateTextPromptType' - nullable: true - labels: - type: array - items: - type: string - nullable: true - description: List of deployment labels of this prompt version. - tags: - type: array - items: - type: string - nullable: true - description: List of tags to apply to all versions of this prompt. - commitMessage: - type: string - nullable: true - description: Commit message for this prompt version. - required: - - name - - prompt - Prompt: - title: Prompt - type: object - properties: - type: - type: string - enum: - - chat - - text - prompt: - type: string - required: - - type - - prompt - PromptType: - title: PromptType - type: string - enum: - - chat - - text - BasePrompt: - title: BasePrompt - type: object - properties: - name: - type: string - version: - type: integer - config: {} - labels: - type: array - items: - type: string - description: List of deployment labels of this prompt version. - tags: - type: array - items: - type: string - description: >- - List of tags. Used to filter via UI and API. The same across - versions of a prompt. - commitMessage: - type: string - nullable: true - description: Commit message for this prompt version. - resolutionGraph: - type: object - additionalProperties: true - nullable: true - description: >- - The dependency resolution graph for the current prompt. Null if the - prompt has no dependencies or if `resolve=false` was used. - required: - - name - - version - - config - - labels - - tags - ChatMessageWithPlaceholders: - title: ChatMessageWithPlaceholders - oneOf: - - $ref: '#/components/schemas/ChatMessage' - - $ref: '#/components/schemas/PlaceholderMessage' - ChatMessage: - title: ChatMessage - type: object - properties: - role: - type: string - content: - type: string - type: - $ref: '#/components/schemas/ChatMessageType' - nullable: true - required: - - role - - content - ChatMessageType: - title: ChatMessageType - type: string - enum: - - chatmessage - PlaceholderMessage: - title: PlaceholderMessage - type: object - properties: - name: - type: string - type: - $ref: '#/components/schemas/PlaceholderMessageType' - nullable: true - required: - - name - PlaceholderMessageType: - title: PlaceholderMessageType - type: string - enum: - - placeholder - TextPrompt: - title: TextPrompt - type: object - properties: - prompt: - type: string - required: - - prompt - allOf: - - $ref: '#/components/schemas/BasePrompt' - ChatPrompt: - title: ChatPrompt - type: object - properties: - prompt: - type: array - items: - $ref: '#/components/schemas/ChatMessageWithPlaceholders' - required: - - prompt - allOf: - - $ref: '#/components/schemas/BasePrompt' - CreateChatPromptType: - title: CreateChatPromptType - type: string - enum: - - chat - CreateTextPromptType: - title: CreateTextPromptType - type: string - enum: - - text - ServiceProviderConfig: - title: ServiceProviderConfig - type: object - properties: - schemas: - type: array - items: - type: string - documentationUri: - type: string - patch: - $ref: '#/components/schemas/ScimFeatureSupport' - bulk: - $ref: '#/components/schemas/BulkConfig' - filter: - $ref: '#/components/schemas/FilterConfig' - changePassword: - $ref: '#/components/schemas/ScimFeatureSupport' - sort: - $ref: '#/components/schemas/ScimFeatureSupport' - etag: - $ref: '#/components/schemas/ScimFeatureSupport' - authenticationSchemes: - type: array - items: - $ref: '#/components/schemas/AuthenticationScheme' - meta: - $ref: '#/components/schemas/ResourceMeta' - required: - - schemas - - documentationUri - - patch - - bulk - - filter - - changePassword - - sort - - etag - - authenticationSchemes - - meta - ScimFeatureSupport: - title: ScimFeatureSupport - type: object - properties: - supported: - type: boolean - required: - - supported - BulkConfig: - title: BulkConfig - type: object - properties: - supported: - type: boolean - maxOperations: - type: integer - maxPayloadSize: - type: integer - required: - - supported - - maxOperations - - maxPayloadSize - FilterConfig: - title: FilterConfig - type: object - properties: - supported: - type: boolean - maxResults: - type: integer - required: - - supported - - maxResults - ResourceMeta: - title: ResourceMeta - type: object - properties: - resourceType: - type: string - location: - type: string - required: - - resourceType - - location - AuthenticationScheme: - title: AuthenticationScheme - type: object - properties: - name: - type: string - description: - type: string - specUri: - type: string - type: - type: string - primary: - type: boolean - required: - - name - - description - - specUri - - type - - primary - ResourceTypesResponse: - title: ResourceTypesResponse - type: object - properties: - schemas: - type: array - items: - type: string - totalResults: - type: integer - Resources: - type: array - items: - $ref: '#/components/schemas/ResourceType' - required: - - schemas - - totalResults - - Resources - ResourceType: - title: ResourceType - type: object - properties: - schemas: - type: array - items: - type: string - nullable: true - id: - type: string - name: - type: string - endpoint: - type: string - description: - type: string - schema: - type: string - schemaExtensions: - type: array - items: - $ref: '#/components/schemas/SchemaExtension' - meta: - $ref: '#/components/schemas/ResourceMeta' - required: - - id - - name - - endpoint - - description - - schema - - schemaExtensions - - meta - SchemaExtension: - title: SchemaExtension - type: object - properties: - schema: - type: string - required: - type: boolean - required: - - schema - - required - SchemasResponse: - title: SchemasResponse - type: object - properties: - schemas: - type: array - items: - type: string - totalResults: - type: integer - Resources: - type: array - items: - $ref: '#/components/schemas/SchemaResource' - required: - - schemas - - totalResults - - Resources - SchemaResource: - title: SchemaResource - type: object - properties: - id: - type: string - name: - type: string - description: - type: string - attributes: - type: array - items: {} - meta: - $ref: '#/components/schemas/ResourceMeta' - required: - - id - - name - - description - - attributes - - meta - ScimUsersListResponse: - title: ScimUsersListResponse - type: object - properties: - schemas: - type: array - items: - type: string - totalResults: - type: integer - startIndex: - type: integer - itemsPerPage: - type: integer - Resources: - type: array - items: - $ref: '#/components/schemas/ScimUser' - required: - - schemas - - totalResults - - startIndex - - itemsPerPage - - Resources - ScimUser: - title: ScimUser - type: object - properties: - schemas: - type: array - items: - type: string - id: - type: string - userName: - type: string - name: - $ref: '#/components/schemas/ScimName' - emails: - type: array - items: - $ref: '#/components/schemas/ScimEmail' - meta: - $ref: '#/components/schemas/UserMeta' - required: - - schemas - - id - - userName - - name - - emails - - meta - UserMeta: - title: UserMeta - type: object - properties: - resourceType: - type: string - created: - type: string - nullable: true - lastModified: - type: string - nullable: true - required: - - resourceType - ScimName: - title: ScimName - type: object - properties: - formatted: - type: string - nullable: true - ScimEmail: - title: ScimEmail - type: object - properties: - primary: - type: boolean - value: - type: string - type: - type: string - required: - - primary - - value - - type - EmptyResponse: - title: EmptyResponse - type: object - description: Empty response for 204 No Content responses - properties: {} - ScoreConfigs: - title: ScoreConfigs - type: object - properties: - data: - type: array - items: - $ref: '#/components/schemas/ScoreConfig' - meta: - $ref: '#/components/schemas/utilsMetaResponse' - required: - - data - - meta - CreateScoreConfigRequest: - title: CreateScoreConfigRequest - type: object - properties: - name: - type: string - description: >- - Name of the score config. Max 35 characters. Only letters, numbers, - underscores, spaces, periods, parentheses, and hyphens are allowed. - dataType: - $ref: '#/components/schemas/ScoreConfigDataType' - categories: - type: array - items: - $ref: '#/components/schemas/ConfigCategory' - nullable: true - description: >- - Configure custom categories for categorical scores. Pass a list of - objects with `label` and `value` properties. Categories are - autogenerated for boolean configs and cannot be passed - minValue: - type: number - format: double - nullable: true - description: >- - Configure a minimum value for numerical scores. If not set, the - minimum value defaults to -∞ - maxValue: - type: number - format: double - nullable: true - description: >- - Configure a maximum value for numerical scores. If not set, the - maximum value defaults to +∞ - description: - type: string - nullable: true - description: >- - Description is shown across the Langfuse UI and can be used to e.g. - explain the config categories in detail, why a numeric range was - set, or provide additional context on config name or usage - required: - - name - - dataType - UpdateScoreConfigRequest: - title: UpdateScoreConfigRequest - type: object - properties: - isArchived: - type: boolean - nullable: true - description: The status of the score config showing if it is archived or not - name: - type: string - nullable: true - description: >- - Name of the score config. Max 35 characters. Only letters, numbers, - underscores, spaces, periods, parentheses, and hyphens are allowed. - categories: - type: array - items: - $ref: '#/components/schemas/ConfigCategory' - nullable: true - description: >- - Configure custom categories for categorical scores. Pass a list of - objects with `label` and `value` properties. Categories are - autogenerated for boolean configs and cannot be passed - minValue: - type: number - format: double - nullable: true - description: >- - Configure a minimum value for numerical scores. If not set, the - minimum value defaults to -∞ - maxValue: - type: number - format: double - nullable: true - description: >- - Configure a maximum value for numerical scores. If not set, the - maximum value defaults to +∞ - description: - type: string - nullable: true - description: >- - Description is shown across the Langfuse UI and can be used to e.g. - explain the config categories in detail, why a numeric range was - set, or provide additional context on config name or usage - ScoreSubjectTraceV3: - title: ScoreSubjectTraceV3 - type: object - properties: - id: - type: string - description: The trace ID. - required: - - id - ScoreSubjectObservationV3: - title: ScoreSubjectObservationV3 - type: object - properties: - id: - type: string - description: The observation ID. - traceId: - type: string - nullable: true - description: The parent trace ID, if available. - required: - - id - ScoreSubjectSessionV3: - title: ScoreSubjectSessionV3 - type: object - properties: - id: - type: string - description: The session ID. - required: - - id - ScoreSubjectExperimentV3: - title: ScoreSubjectExperimentV3 - type: object - properties: - id: - type: string - description: The dataset run ID (experiment ID). - required: - - id - ScoreSubjectV3: - title: ScoreSubjectV3 - type: object - properties: - kind: - type: string - enum: - - trace - - observation - - session - - experiment - id: - type: string - description: The trace ID. - traceId: - type: string - nullable: true - description: The parent trace ID, if available. - required: - - kind - - id - BaseScoreV3: - title: BaseScoreV3 - type: object - properties: - id: - type: string - projectId: - type: string - name: - type: string - source: - $ref: '#/components/schemas/ScoreSource' - timestamp: - type: string - format: date-time - environment: - type: string - description: The environment from which this score originated. - createdAt: - type: string - format: date-time - updatedAt: - type: string - format: date-time - comment: - type: string - nullable: true - description: >- - Optional comment attached to the score. Present when "details" is - included in the fields parameter. - configId: - type: string - nullable: true - description: >- - The score config ID, if this score was created from a config. - Present when "details" is included in the fields parameter. - metadata: - type: object - additionalProperties: true - nullable: true - description: >- - Arbitrary metadata attached to the score. Present when "details" is - included in the fields parameter. - authorUserId: - type: string - nullable: true - description: >- - The user who created this score, if available. Present when - "annotation" is included in the fields parameter. - queueId: - type: string - nullable: true - description: >- - The annotation queue this score belongs to, if any. Present when - "annotation" is included in the fields parameter. - subject: - $ref: '#/components/schemas/ScoreSubjectV3' - nullable: true - description: >- - The entity this score is attached to (trace, observation, session, - or experiment). Present when "subject" is included in the fields - parameter. - required: - - id - - projectId - - name - - source - - timestamp - - environment - - createdAt - - updatedAt - NumericScoreV3: - title: NumericScoreV3 - type: object - properties: - value: - type: number - format: double - description: The numeric value of the score. - required: - - value - allOf: - - $ref: '#/components/schemas/BaseScoreV3' - BooleanScoreV3: - title: BooleanScoreV3 - type: object - properties: - value: - type: boolean - description: The boolean value of the score. - required: - - value - allOf: - - $ref: '#/components/schemas/BaseScoreV3' - CategoricalScoreV3: - title: CategoricalScoreV3 - type: object - properties: - value: - type: string - description: The string category value of the score. - required: - - value - allOf: - - $ref: '#/components/schemas/BaseScoreV3' - TextScoreV3: - title: TextScoreV3 - type: object - properties: - value: - type: string - description: The text content of the score. - required: - - value - allOf: - - $ref: '#/components/schemas/BaseScoreV3' - CorrectionScoreV3: - title: CorrectionScoreV3 - type: object - properties: - value: - type: string - description: The correction content of the score. Empty string if not set. - required: - - value - allOf: - - $ref: '#/components/schemas/BaseScoreV3' - ScoreV3: - title: ScoreV3 - type: object - properties: - dataType: - type: string - enum: - - NUMERIC - - BOOLEAN - - CATEGORICAL - - TEXT - - CORRECTION - value: - type: string - description: The numeric value of the score. - required: - - dataType - - value - GetScoresV3Meta: - title: GetScoresV3Meta - type: object - properties: - limit: - type: integer - cursor: - type: string - nullable: true - description: >- - URL-safe base64 (base64url) cursor for the next page. Absent when - there are no more results. - required: - - limit - GetScoresV3Response: - title: GetScoresV3Response - type: object - properties: - data: - type: array - items: - $ref: '#/components/schemas/ScoreV3' - meta: - $ref: '#/components/schemas/GetScoresV3Meta' - required: - - data - - meta - GetScoresResponseTraceData: - title: GetScoresResponseTraceData - type: object - properties: - userId: - type: string - nullable: true - description: The user ID associated with the trace referenced by score - tags: - type: array - items: - type: string - nullable: true - description: A list of tags associated with the trace referenced by score - environment: - type: string - nullable: true - description: The environment of the trace referenced by score - sessionId: - type: string - nullable: true - description: The session ID associated with the trace referenced by score - GetScoresResponseDataNumeric: - title: GetScoresResponseDataNumeric - type: object - properties: - trace: - $ref: '#/components/schemas/GetScoresResponseTraceData' - nullable: true - allOf: - - $ref: '#/components/schemas/NumericScore' - GetScoresResponseDataCategorical: - title: GetScoresResponseDataCategorical - type: object - properties: - trace: - $ref: '#/components/schemas/GetScoresResponseTraceData' - nullable: true - allOf: - - $ref: '#/components/schemas/CategoricalScore' - GetScoresResponseDataBoolean: - title: GetScoresResponseDataBoolean - type: object - properties: - trace: - $ref: '#/components/schemas/GetScoresResponseTraceData' - nullable: true - allOf: - - $ref: '#/components/schemas/BooleanScore' - GetScoresResponseDataCorrection: - title: GetScoresResponseDataCorrection - type: object - properties: - trace: - $ref: '#/components/schemas/GetScoresResponseTraceData' - nullable: true - allOf: - - $ref: '#/components/schemas/CorrectionScore' - GetScoresResponseDataText: - title: GetScoresResponseDataText - type: object - properties: - trace: - $ref: '#/components/schemas/GetScoresResponseTraceData' - nullable: true - allOf: - - $ref: '#/components/schemas/TextScore' - GetScoresResponseData: - title: GetScoresResponseData - type: object - properties: - dataType: - type: string - enum: - - NUMERIC - - CATEGORICAL - - BOOLEAN - - CORRECTION - - TEXT - trace: - $ref: '#/components/schemas/GetScoresResponseTraceData' - required: - - dataType - GetScoresResponse: - title: GetScoresResponse - type: object - properties: - data: - type: array - items: - $ref: '#/components/schemas/GetScoresResponseData' - meta: - $ref: '#/components/schemas/utilsMetaResponse' - required: - - data - - meta - PaginatedSessions: - title: PaginatedSessions - type: object - properties: - data: - type: array - items: - $ref: '#/components/schemas/Session' - meta: - $ref: '#/components/schemas/utilsMetaResponse' - required: - - data - - meta - Traces: - title: Traces - type: object - properties: - data: - type: array - items: - $ref: '#/components/schemas/TraceWithDetails' - meta: - $ref: '#/components/schemas/utilsMetaResponse' - required: - - data - - meta - DeleteTraceResponse: - title: DeleteTraceResponse - type: object - properties: - message: - type: string - required: - - message - Sort: - title: Sort - type: object - properties: - id: - type: string - required: - - id - unstableEvaluatorType: - title: unstableEvaluatorType - type: string - enum: - - llm_as_judge - - code - description: |- - The evaluator engine type. - - The unstable public API supports LLM-as-a-judge and code evaluators. - unstableCodeEvaluatorSourceCodeLanguage: - title: unstableCodeEvaluatorSourceCodeLanguage - type: string - enum: - - PYTHON - - TYPESCRIPT - description: Code evaluator runtime language. - unstableEvaluatorScope: - title: unstableEvaluatorScope - type: string - enum: - - project - - managed - description: |- - Where an evaluator comes from. - - - `project`: created in your project - - `managed`: provided by Langfuse - unstableEvaluationRuleTarget: - title: unstableEvaluationRuleTarget - type: string - enum: - - observation - - experiment - description: >- - The ingestion object type that should trigger evaluation runs. - - - Choose the target first, because it changes both the valid filter - columns and the valid variable-mapping sources: - - - `observation` evaluates live-ingested observations such as - generations, spans, and events. - It supports mapping from `input`, `output`, and `metadata`. - - `experiment` evaluates live experiment executions and can additionally - map `expected_output` and `experiment_item_metadata`. - It currently supports filtering by `datasetId`. - Discover valid dataset IDs with `GET /api/public/v2/datasets`, then use the returned dataset `id` values in your filter. - unstableEvaluationRuleStatus: - title: unstableEvaluationRuleStatus - type: string - enum: - - active - - inactive - - paused - description: >- - Effective runtime status of the evaluation rule. - - - - `active`: enabled and currently runnable. - - - `inactive`: disabled by configuration. - - - `paused`: enabled, but Langfuse has blocked execution until the - underlying issue is resolved. - unstableEvaluationRuleMappingSource: - title: unstableEvaluationRuleMappingSource - type: string - enum: - - input - - output - - metadata - - expected_output - - experiment_item_metadata - description: >- - Source field used to populate a prompt variable. - - - Use these values when mapping evaluator prompt variables to live data. - - - Target-specific rules: - - - `target=observation` supports `input`, `output`, and `metadata` - - - `target=experiment` supports `input`, `output`, `metadata`, - `expected_output`, and `experiment_item_metadata` - - - Source semantics: - - - `input`: the observation or experiment input payload - - - `output`: the observation or experiment output payload - - - `metadata`: the metadata object for the target. Combine with - `jsonPath` when you need one nested field instead of the whole object. - - - `expected_output`: the experiment item's expected output. Only valid - for `target=experiment`. - - - `experiment_item_metadata`: the experiment item's metadata object. - Only valid for `target=experiment`. - unstableEvaluatorModelConfig: - title: unstableEvaluatorModelConfig - type: object - description: >- - Optional explicit model configuration for an evaluator. - - - If omitted, Langfuse uses the project's default evaluation model. - - If provided, the model must be available to the project when the - evaluator or evaluation rule is enabled. - - - To discover valid configured `provider` values for a project, call `GET - /api/public/llm-connections` and read the `provider` field from the - returned connections. - - Use a `provider` value that matches one of the connections already - configured in the same project. - - - Recovery guidance: - - - If evaluator creation returns `422` with - `code=evaluator_preflight_failed`, either provide a valid explicit - `modelConfig` here or configure the project's default evaluation model, - then retry the same request. - properties: - provider: - type: string - description: >- - Provider identifier to use for this evaluator, for example `openai` - or `anthropic`. - - - To discover valid values for the current project, call `GET - /api/public/llm-connections` and use one of the returned `provider` - values. - model: - type: string - description: >- - Model identifier exposed by the provider, for example `gpt-4.1-mini`. - required: - - provider - - model - unstableEvaluatorOutputDataType: - title: unstableEvaluatorOutputDataType - type: string - enum: - - NUMERIC - - BOOLEAN - - CATEGORICAL - description: >- - Structured score type returned by an evaluator. - - - This controls the type of score value Langfuse stores for evaluation - results: - - - `NUMERIC`: a numeric score such as `0.82` - - - `BOOLEAN`: a boolean score such as `true` - - - `CATEGORICAL`: one or more category labels from a fixed list - unstableEvaluatorOutputFieldDefinition: - title: unstableEvaluatorOutputFieldDefinition - type: object - properties: - description: - type: string - description: >- - Human-readable instructions for what the evaluator should return in - this field. - required: - - description - unstableEvaluatorOutputDefinition: - title: unstableEvaluatorOutputDefinition - type: object - properties: - dataType: - type: string - enum: - - NUMERIC - - BOOLEAN - - CATEGORICAL - reasoning: - $ref: '#/components/schemas/unstableEvaluatorOutputFieldDefinition' - score: - type: string - required: - - dataType - - reasoning - - score - unstablePublicNumericEvaluatorOutputDefinition: - title: unstablePublicNumericEvaluatorOutputDefinition - type: object - properties: - dataType: - $ref: '#/components/schemas/unstableEvaluatorOutputDataType' - description: Always `NUMERIC`. - reasoning: - $ref: '#/components/schemas/unstableEvaluatorOutputFieldDefinition' - score: - $ref: '#/components/schemas/unstableEvaluatorOutputFieldDefinition' - required: - - dataType - - reasoning - - score - unstablePublicBooleanEvaluatorOutputDefinition: - title: unstablePublicBooleanEvaluatorOutputDefinition - type: object - properties: - dataType: - $ref: '#/components/schemas/unstableEvaluatorOutputDataType' - description: Always `BOOLEAN`. - reasoning: - $ref: '#/components/schemas/unstableEvaluatorOutputFieldDefinition' - score: - $ref: '#/components/schemas/unstableEvaluatorOutputFieldDefinition' - required: - - dataType - - reasoning - - score - unstablePublicCategoricalEvaluatorOutputScoreDefinition: - title: unstablePublicCategoricalEvaluatorOutputScoreDefinition - type: object - properties: - description: - type: string - categories: - type: array - items: - type: string - shouldAllowMultipleMatches: - type: boolean - required: - - description - - categories - - shouldAllowMultipleMatches - unstablePublicCategoricalEvaluatorOutputDefinition: - title: unstablePublicCategoricalEvaluatorOutputDefinition - type: object - properties: - dataType: - $ref: '#/components/schemas/unstableEvaluatorOutputDataType' - description: Always `CATEGORICAL`. - reasoning: - $ref: '#/components/schemas/unstableEvaluatorOutputFieldDefinition' - score: - $ref: >- - #/components/schemas/unstablePublicCategoricalEvaluatorOutputScoreDefinition - required: - - dataType - - reasoning - - score - unstablePublicEvaluatorOutputDefinition: - title: unstablePublicEvaluatorOutputDefinition - type: object - properties: - dataType: - type: string - enum: - - NUMERIC - - BOOLEAN - - CATEGORICAL - reasoning: - $ref: '#/components/schemas/unstableEvaluatorOutputFieldDefinition' - score: - type: string - required: - - dataType - - reasoning - - score - unstableEvaluationRuleStringFilterOperator: - title: unstableEvaluationRuleStringFilterOperator - type: string - enum: - - '=' - - contains - - does not contain - - starts with - - ends with - unstableEvaluationRuleNumberFilterOperator: - title: unstableEvaluationRuleNumberFilterOperator - type: string - enum: - - '=' - - '>' - - < - - '>=' - - <= - unstableEvaluationRuleOptionsFilterOperator: - title: unstableEvaluationRuleOptionsFilterOperator - type: string - enum: - - any of - - none of - unstableEvaluationRuleArrayOptionsFilterOperator: - title: unstableEvaluationRuleArrayOptionsFilterOperator - type: string - enum: - - any of - - none of - - all of - unstableEvaluationRuleBooleanFilterOperator: - title: unstableEvaluationRuleBooleanFilterOperator - type: string - enum: - - '=' - - <> - unstableEvaluationRuleNullFilterOperator: - title: unstableEvaluationRuleNullFilterOperator - type: string - enum: - - is null - - is not null - unstableDateTimeEvaluationRuleFilter: - title: unstableDateTimeEvaluationRuleFilter - type: object - properties: - column: - type: string - description: Column to filter on. - operator: - $ref: '#/components/schemas/unstableEvaluationRuleNumberFilterOperator' - description: Comparison operator for datetime values. - value: - type: string - format: date-time - description: Datetime value to compare against. - required: - - column - - operator - - value - unstableStringEvaluationRuleFilter: - title: unstableStringEvaluationRuleFilter - type: object - properties: - column: - type: string - description: Column to filter on. - operator: - $ref: '#/components/schemas/unstableEvaluationRuleStringFilterOperator' - value: - type: string - required: - - column - - operator - - value - unstableNumberEvaluationRuleFilter: - title: unstableNumberEvaluationRuleFilter - type: object - properties: - column: - type: string - description: Column to filter on. - operator: - $ref: '#/components/schemas/unstableEvaluationRuleNumberFilterOperator' - value: - type: number - format: double - required: - - column - - operator - - value - unstableStringOptionsEvaluationRuleFilter: - title: unstableStringOptionsEvaluationRuleFilter - type: object - properties: - column: - type: string - description: Column to filter on. - operator: - $ref: '#/components/schemas/unstableEvaluationRuleOptionsFilterOperator' - value: - type: array - items: - type: string - description: One or more allowed string values. - required: - - column - - operator - - value - unstableArrayOptionsEvaluationRuleFilter: - title: unstableArrayOptionsEvaluationRuleFilter - type: object - properties: - column: - type: string - description: Column to filter on. - operator: - $ref: >- - #/components/schemas/unstableEvaluationRuleArrayOptionsFilterOperator - value: - type: array - items: - type: string - description: One or more array elements to match. - required: - - column - - operator - - value - unstableStringObjectEvaluationRuleFilter: - title: unstableStringObjectEvaluationRuleFilter - type: object - properties: - column: - type: string - description: >- - Object-valued column to filter on. In the unstable public API this - is currently `metadata`. - key: - type: string - description: Top-level key inside the object-valued column to filter on. - operator: - $ref: '#/components/schemas/unstableEvaluationRuleStringFilterOperator' - value: - type: string - required: - - column - - key - - operator - - value - unstableNumberObjectEvaluationRuleFilter: - title: unstableNumberObjectEvaluationRuleFilter - type: object - properties: - column: - type: string - description: Object-valued column to filter on. - key: - type: string - description: Key inside the object-valued column to filter on. - operator: - $ref: '#/components/schemas/unstableEvaluationRuleNumberFilterOperator' - value: - type: number - format: double - required: - - column - - key - - operator - - value - unstableCategoryOptionsEvaluationRuleFilter: - title: unstableCategoryOptionsEvaluationRuleFilter - type: object - properties: - column: - type: string - description: Object-valued column to filter on. - key: - type: string - description: Key inside the object-valued column to filter on. - operator: - $ref: '#/components/schemas/unstableEvaluationRuleOptionsFilterOperator' - value: - type: array - items: - type: string - required: - - column - - key - - operator - - value - unstableBooleanEvaluationRuleFilter: - title: unstableBooleanEvaluationRuleFilter - type: object - properties: - column: - type: string - description: Column to filter on. - operator: - $ref: '#/components/schemas/unstableEvaluationRuleBooleanFilterOperator' - value: - type: boolean - required: - - column - - operator - - value - unstableNullEvaluationRuleFilter: - title: unstableNullEvaluationRuleFilter - type: object - properties: - column: - type: string - description: >- - Column to filter on. In the unstable public API this is currently - `parentObservationId`. - operator: - $ref: '#/components/schemas/unstableEvaluationRuleNullFilterOperator' - value: - type: string - nullable: true - description: >- - Ignored placeholder value. Clients may omit it or send an empty - string. - required: - - column - - operator - unstableEvaluationRuleMapping: - title: unstableEvaluationRuleMapping - type: object - description: >- - Maps one evaluator variable to one source field from the target object. - - - Manual mappings are used for `llm_as_judge` evaluators. `code` - evaluators use a fixed runtime mapping managed by Langfuse. - - - How to build a valid mapping list: - - 1. Create the evaluator or fetch it with `GET /evaluators/{id}`. - - 2. Read the evaluator `variables` array. - - 3. Add exactly one mapping object for each variable in that array. - - 4. Use the variable name exactly as returned, without braces such as - `{{` or `}}`. - - 5. Choose a `source` that is valid for the selected `target`. - - - `jsonPath` is optional. Use it only when the selected source is a JSON - object and you want to extract one nested field before inserting it into - the evaluator prompt. - - - Recovery guidance: - - - `invalid_variable_mapping`: the variable name is unknown for this - evaluator, or the selected `source` is not valid for the chosen `target` - - - `missing_variable_mapping`: one or more LLM-as-judge evaluator - variables are not mapped yet - - - `duplicate_variable_mapping`: the same evaluator variable appears more - than once - - - `invalid_json_path`: the JSONPath expression is malformed. Remove it - or correct it. - properties: - variable: - type: string - description: >- - Prompt variable name without braces. - - - Example: for the prompt `Judge {{input}} against {{output}}`, use - `input` and `output`. - source: - $ref: '#/components/schemas/unstableEvaluationRuleMappingSource' - description: >- - Source field that should populate the prompt variable. - - - Quick reference: - - - `target=observation`: `input`, `output`, `metadata` - - - `target=experiment`: `input`, `output`, `metadata`, - `expected_output`, `experiment_item_metadata` - jsonPath: - type: string - nullable: true - description: >- - Optional JSONPath selector applied to the selected source before it - is passed to the evaluator prompt. - - - Requirements: - - - Must start with `$` - - - Must be a syntactically valid JSONPath expression - - - Most useful with `source=metadata` - required: - - variable - - source - unstableEvaluationRuleFilter: - title: unstableEvaluationRuleFilter - type: object - properties: - type: - type: string - enum: - - datetime - - string - - number - - stringOptions - - categoryOptions - - arrayOptions - - stringObject - - numberObject - - boolean - - 'null' - column: - type: string - description: Column to filter on. - operator: - type: string - description: Comparison operator for datetime values. - value: - type: string - description: Datetime value to compare against. - key: - type: string - description: Key inside the object-valued column to filter on. - required: - - type - - column - - operator - unstableDashboardWidgetView: - title: unstableDashboardWidgetView - type: string - enum: - - observations - - scores-numeric - - scores-categorical - unstableDashboardWidgetChartType: - title: unstableDashboardWidgetChartType - type: string - enum: - - LINE_TIME_SERIES - - AREA_TIME_SERIES - - BAR_TIME_SERIES - - HORIZONTAL_BAR - - VERTICAL_BAR - - PIE - - NUMBER - - HISTOGRAM - - PIVOT_TABLE - unstableDashboardWidgetMetricAggregation: - title: unstableDashboardWidgetMetricAggregation - type: string - enum: - - sum - - avg - - count - - max - - min - - p50 - - p75 - - p90 - - p95 - - p99 - - histogram - - uniq - unstableDashboardWidgetDimension: - title: unstableDashboardWidgetDimension - type: object - properties: - field: - type: string - required: - - field - unstableDashboardWidgetMetric: - title: unstableDashboardWidgetMetric - type: object - properties: - measure: - type: string - agg: - $ref: '#/components/schemas/unstableDashboardWidgetMetricAggregation' - required: - - measure - - agg - unstableDashboardWidgetFilter: - title: unstableDashboardWidgetFilter - type: object - description: >- - A dashboard widget filter in Langfuse filter-state shape. - - - Filter shapes depend on `type`, for example string filters use a string - `value`, - - option filters use a list of strings, and object filters include `key`. - properties: - column: - type: string - operator: - type: string - type: - type: string - value: - nullable: true - key: - type: string - nullable: true - required: - - column - - operator - - type - unstableDashboardWidgetChartConfig: - title: unstableDashboardWidgetChartConfig - type: object - description: |- - Chart-specific widget configuration. - - `type` must match the top-level `chartType`. - `row_limit` applies to total-value charts and pivot tables. - `bins` applies to histograms. - `defaultSort` applies to pivot tables. - properties: - type: - $ref: '#/components/schemas/unstableDashboardWidgetChartType' - row_limit: - type: integer - nullable: true - show_value_labels: - type: boolean - nullable: true - bins: - type: integer - nullable: true - defaultSort: - $ref: '#/components/schemas/unstableDashboardWidgetDefaultSort' - nullable: true - required: - - type - unstableDashboardWidgetDefaultSort: - title: unstableDashboardWidgetDefaultSort - type: object - properties: - column: - type: string - order: - $ref: '#/components/schemas/unstableDashboardWidgetSortOrder' - required: - - column - - order - unstableDashboardWidgetSortOrder: - title: unstableDashboardWidgetSortOrder - type: string - enum: - - ASC - - DESC - unstableCreateDashboardWidgetRequest: - title: unstableCreateDashboardWidgetRequest - type: object - properties: - name: - type: string - description: - type: string - view: - $ref: '#/components/schemas/unstableDashboardWidgetView' - dimensions: - type: array - items: - $ref: '#/components/schemas/unstableDashboardWidgetDimension' - metrics: - type: array - items: - $ref: '#/components/schemas/unstableDashboardWidgetMetric' - filters: - type: array - items: - $ref: '#/components/schemas/unstableDashboardWidgetFilter' - chartType: - $ref: '#/components/schemas/unstableDashboardWidgetChartType' - chartConfig: - $ref: '#/components/schemas/unstableDashboardWidgetChartConfig' - minVersion: - type: integer - nullable: true - required: - - name - - description - - view - - dimensions - - metrics - - filters - - chartType - - chartConfig - unstableDashboardWidget: - title: unstableDashboardWidget - type: object - properties: - id: - type: string - createdAt: - type: string - format: date-time - updatedAt: - type: string - format: date-time - name: - type: string - description: - type: string - view: - $ref: '#/components/schemas/unstableDashboardWidgetView' - dimensions: - type: array - items: - $ref: '#/components/schemas/unstableDashboardWidgetDimension' - metrics: - type: array - items: - $ref: '#/components/schemas/unstableDashboardWidgetMetric' - filters: - type: array - items: - $ref: '#/components/schemas/unstableDashboardWidgetFilter' - chartType: - $ref: '#/components/schemas/unstableDashboardWidgetChartType' - chartConfig: - $ref: '#/components/schemas/unstableDashboardWidgetChartConfig' - minVersion: - type: integer - required: - - id - - createdAt - - updatedAt - - name - - description - - view - - dimensions - - metrics - - filters - - chartType - - chartConfig - - minVersion - unstablePublicApiErrorCode: - title: unstablePublicApiErrorCode - type: string - enum: - - authentication_failed - - access_denied - - invalid_request - - invalid_query - - invalid_body - - invalid_filter_value - - invalid_json_path - - invalid_variable_mapping - - missing_variable_mapping - - duplicate_variable_mapping - - resource_not_found - - name_conflict - - evaluator_preflight_failed - - conflict - - unprocessable_content - - rate_limited - - method_not_allowed - - internal_error - description: >- - Machine-readable error code returned by the unstable evaluators API. - - - SDKs, CLIs, and agents should branch on `code` rather than parsing the - human-readable `message`. - - The HTTP status still indicates the broad error class, while `code` - gives the specific failure reason. - unstablePublicApiValidationIssue: - title: unstablePublicApiValidationIssue - type: object - description: >- - One validation issue returned for malformed request bodies or query - parameters. - - - This mirrors the most important parts of a Zod issue: a machine-readable - `code`, - - a human-readable `message`, and a structured `path`. - properties: - code: - type: string - description: >- - Machine-readable validation issue code emitted by the server - validator. - message: - type: string - description: Human-readable explanation of the validation failure. - path: - type: array - items: {} - description: Path to the invalid field, for example `["mapping", 0, "jsonPath"]`. - required: - - code - - message - - path - unstablePublicApiErrorDetails: - title: unstablePublicApiErrorDetails - type: object - description: >- - Optional structured context attached to an unstable-evals error. - - - The populated fields depend on the error `code`: - - - request parsing failures populate `issues` - - - filter validation failures populate `field`, `column`, - `invalidValues`, and `allowedValues` - - - variable mapping failures populate `field`, `variable`, or `variables` - - - JSONPath validation failures populate `field`, `variable`, and `value` - - - evaluator preflight failures populate `evaluatorName`, `provider`, and - `model` - - - rate limiting populates `retryAfterSeconds`, `limit`, `remaining`, and - `resetAt` - properties: - issues: - type: array - items: - $ref: '#/components/schemas/unstablePublicApiValidationIssue' - nullable: true - description: Validation issues for malformed request bodies or query parameters. - field: - type: string - nullable: true - description: >- - Path-like reference to the failing field, for example - `mapping[1].jsonPath`. - column: - type: string - nullable: true - description: Filter column that failed validation. - invalidValues: - type: array - items: - type: string - nullable: true - description: Unsupported values supplied by the caller. - allowedValues: - type: array - items: - type: string - nullable: true - description: Allowed values for the failing filter column. - variable: - type: string - nullable: true - description: Evaluator variable involved in the failure. - variables: - type: array - items: - type: string - nullable: true - description: >- - Multiple evaluator variables involved in the failure, for example - missing mappings. - value: - type: string - nullable: true - description: Raw invalid value supplied by the caller. - evaluatorName: - type: string - nullable: true - description: Evaluator name used during preflight validation. - provider: - type: string - nullable: true - description: Provider resolved during evaluator preflight, if any. - model: - type: string - nullable: true - description: Model resolved during evaluator preflight, if any. - retryAfterSeconds: - type: integer - nullable: true - description: Suggested retry delay for rate-limited requests. - limit: - type: integer - nullable: true - description: >- - Numeric limit associated with the failure, for example the active - evaluation-rule cap or the current rate-limit window. - remaining: - type: integer - nullable: true - description: Remaining requests in the current rate-limit window. - resetAt: - type: string - nullable: true - description: ISO-8601 timestamp when the current rate-limit window resets. - unstablePublicApiError: - title: unstablePublicApiError - type: object - description: >- - Standard error envelope for the unstable evaluators API. - - - Response handling guidance: - - - Use the HTTP status code for the broad class of failure. - - - Use `code` for precise branching in SDKs, CLIs, or agents. - - - Inspect `details` for field-level validation context such as invalid - filter values, malformed JSONPath expressions, or missing variable - mappings. - - - Retry only after fixing the specific issue described by `code` and - `details`. - properties: - message: - type: string - description: Human-readable description of the failure. - code: - $ref: '#/components/schemas/unstablePublicApiErrorCode' - description: Stable machine-readable error code. - details: - $ref: '#/components/schemas/unstablePublicApiErrorDetails' - nullable: true - description: >- - Optional structured error context. Inspect the populated fields - based on `code`. - required: - - message - - code - unstableEvaluationRule: - title: unstableEvaluationRule - type: object - description: >- - Live evaluation rule for incoming data. - - - An evaluation rule answers: - - - which evaluator should be used - - - which target objects should trigger scoring - - - how often scoring should run - - - which target fields should populate each evaluator variable - - - whether the deployment is active, inactive, or paused - - - Important status semantics: - - - `enabled` is the desired on/off setting from the client - - - `status` is the effective runtime state after Langfuse applies - validation and blocking rules - - - `enabled=true` with `status=paused` means the rule should run, but - Langfuse has paused it until the underlying problem is fixed - properties: - id: - type: string - description: Stable evaluation rule identifier. - name: - type: string - description: >- - Human-readable deployment name. This is independent from the - evaluator name. - evaluator: - $ref: '#/components/schemas/unstableEvaluationRuleEvaluator' - description: >- - Evaluator currently used by this rule. - - - `name` and `scope` identify the evaluator family conceptually. - - `id` is the currently active evaluator version in that family. - - If you create a newer project version with the same evaluator name - later, existing evaluation rules are moved to it automatically. - target: - $ref: '#/components/schemas/unstableEvaluationRuleTarget' - description: Target object type that should trigger scoring. - enabled: - type: boolean - description: Desired enabled state configured by the client. - status: - $ref: '#/components/schemas/unstableEvaluationRuleStatus' - description: >- - Effective runtime status after Langfuse applies validation and - blocking rules. - pausedReason: - type: string - nullable: true - description: Machine-readable reason when `status=paused`, otherwise `null`. - pausedMessage: - type: string - nullable: true - description: Human-readable explanation when `status=paused`, otherwise `null`. - sampling: - type: number - format: double - description: |- - Fraction of matching target objects that should be evaluated. - - Must be greater than `0` and less than or equal to `1`. - - `1` means evaluate every matching target. - - `0.25` means evaluate approximately 25% of matching targets. - filter: - type: array - items: - $ref: '#/components/schemas/unstableEvaluationRuleFilter' - description: >- - List of filter conditions used to decide whether a target should be - evaluated. - mapping: - type: array - items: - $ref: '#/components/schemas/unstableEvaluationRuleMapping' - description: >- - Variable mappings used to populate evaluator runtime variables from - the live target object. - createdAt: - type: string - format: date-time - description: Timestamp when the evaluation rule was created. - updatedAt: - type: string - format: date-time - description: Timestamp when the evaluation rule was last updated. - required: - - id - - name - - evaluator - - target - - enabled - - status - - pausedReason - - pausedMessage - - sampling - - filter - - mapping - - createdAt - - updatedAt - unstableEvaluationRules: - title: unstableEvaluationRules - type: object - description: Paginated list of evaluation rules. - properties: - data: - type: array - items: - $ref: '#/components/schemas/unstableEvaluationRule' - description: Evaluation rules in the current page. - meta: - $ref: '#/components/schemas/utilsMetaResponse' - description: Standard pagination metadata. - required: - - data - - meta - unstableCreateEvaluationRuleRequest: - title: unstableCreateEvaluationRuleRequest - oneOf: - - $ref: '#/components/schemas/unstableCreateLlmAsJudgeEvaluationRuleRequest' - - $ref: '#/components/schemas/unstableCreateCodeEvaluationRuleRequest' - description: >- - Request body for creating an evaluation rule. - - - Checklist for agents and SDK clients: - - - reference an existing evaluator family by `evaluator.name` and - `evaluator.scope` - - - choose `target=observation` or `target=experiment` - - - if `target=experiment` and you want a dataset filter, call `GET - /api/public/v2/datasets` first and use dataset `id` values in - `filter[].value` - - - for `llm_as_judge`, fetch or inspect the evaluator first and provide a - complete variable mapping for every evaluator variable - - - for `code`, do not send variables or mappings; Langfuse stores the - fixed code runtime mapping automatically - - - optionally narrow execution with `filter` - - - set `enabled=true` only when you want live execution immediately - unstableCreateLlmAsJudgeEvaluationRuleRequest: - title: unstableCreateLlmAsJudgeEvaluationRuleRequest - type: object - properties: - name: - type: string - description: Human-readable deployment name. - evaluator: - $ref: >- - #/components/schemas/unstableLlmAsJudgeEvaluationRuleEvaluatorReference - description: >- - LLM-as-judge evaluator family to use. - - - Use `name`, `scope`, and `type` from the evaluator endpoints. If - `type` is omitted, Langfuse defaults it to `llm_as_judge` for - backwards compatibility. - - Langfuse resolves that family to its latest version before saving - the rule. - target: - $ref: '#/components/schemas/unstableEvaluationRuleTarget' - description: Target object type to evaluate. - enabled: - type: boolean - description: Whether the deployment should be active immediately after creation. - sampling: - type: number - format: double - nullable: true - description: Optional sampling fraction. Defaults to `1`. - filter: - type: array - items: - $ref: '#/components/schemas/unstableEvaluationRuleFilter' - nullable: true - description: >- - Optional filter list. - - - Omit or pass an empty list to evaluate all matching targets for the - selected `target`. - - Each filter object must use a column that is valid for that - `target`. - - For `target=experiment`, `column=datasetId` expects dataset `id` - values from `GET /api/public/v2/datasets`, not dataset names. - mapping: - type: array - items: - $ref: '#/components/schemas/unstableEvaluationRuleMapping' - description: >- - LLM-as-judge variable mappings. - - - Every evaluator variable must appear exactly once. - - Build this list from the evaluator `variables` array returned by the - evaluator endpoints. - required: - - name - - evaluator - - target - - enabled - - mapping - unstableCreateCodeEvaluationRuleRequest: - title: unstableCreateCodeEvaluationRuleRequest - type: object - properties: - name: - type: string - description: Human-readable deployment name. - evaluator: - $ref: '#/components/schemas/unstableCodeEvaluationRuleEvaluatorReference' - description: >- - Code evaluator family to use. - - - Use `name`, `scope`, and `type` from the evaluator endpoints. - - Langfuse resolves that family to its latest version before saving - the rule. - target: - $ref: '#/components/schemas/unstableEvaluationRuleTarget' - description: Target object type to evaluate. - enabled: - type: boolean - description: Whether the deployment should be active immediately after creation. - sampling: - type: number - format: double - nullable: true - description: Optional sampling fraction. Defaults to `1`. - filter: - type: array - items: - $ref: '#/components/schemas/unstableEvaluationRuleFilter' - nullable: true - description: >- - Optional filter list. - - - Omit or pass an empty list to evaluate all matching targets for the - selected `target`. - - Each filter object must use a column that is valid for that - `target`. - - For `target=experiment`, `column=datasetId` expects dataset `id` - values from `GET /api/public/v2/datasets`, not dataset names. - required: - - name - - evaluator - - target - - enabled - unstableUpdateEvaluationRuleRequest: - title: unstableUpdateEvaluationRuleRequest - type: object - description: >- - Partial update body for an evaluation rule. - - - Provide only the fields you want to change. - - An empty body is rejected. - - - Practical guidance: - - - If you only want to rename the rule or change sampling, send just - those fields. - - - If you change to an LLM-as-judge `evaluator`, send a fresh `mapping` - unless you are certain the existing mapping still matches the evaluator - variables. - - - If you change `target` for an LLM-as-judge rule, usually send both - `filter` and `mapping` in the same request. - - - For code evaluator rules, omit `mapping`; Langfuse stores the fixed - code runtime mapping automatically. - - - If you change an experiment `datasetId` filter, call `GET - /api/public/v2/datasets` and use dataset `id` values from that response. - properties: - name: - type: string - nullable: true - description: Updated deployment name. - evaluator: - $ref: '#/components/schemas/unstableEvaluationRuleEvaluatorReference' - nullable: true - description: >- - Updated evaluator family. - - - Langfuse resolves the provided evaluator family to its latest - version before saving the rule. - - A rule's evaluator type cannot be changed: provide `name` and - `scope` for an evaluator family of the rule's current type. To use a - different evaluator type, create a new rule. - target: - $ref: '#/components/schemas/unstableEvaluationRuleTarget' - nullable: true - description: Updated target object type. - enabled: - type: boolean - nullable: true - description: Updated desired enabled state. - sampling: - type: number - format: double - nullable: true - description: Updated sampling fraction. - filter: - type: array - items: - $ref: '#/components/schemas/unstableEvaluationRuleFilter' - nullable: true - description: >- - Updated filter list. - - - For `target=experiment`, `column=datasetId` expects dataset `id` - values from `GET /api/public/v2/datasets`, not dataset names. - mapping: - type: array - items: - $ref: '#/components/schemas/unstableEvaluationRuleMapping' - nullable: true - description: >- - Updated LLM-as-judge variable mappings. - - - Do not send this field for code evaluator rules. Langfuse stores the - fixed code runtime mapping automatically and returns it in the - response. - unstableDeleteEvaluationRuleResponse: - title: unstableDeleteEvaluationRuleResponse - type: object - description: Confirmation response returned after successful deletion. - properties: - message: - type: string - description: Always `Evaluation rule successfully deleted`. - required: - - message - unstableEvaluationRuleEvaluatorReference: - title: unstableEvaluationRuleEvaluatorReference - type: object - description: >- - Evaluator family reference used when updating an evaluation rule. - - - `name` and `scope` identify the evaluator family in the authenticated - project context. - - A rule's evaluator type cannot be changed, so this reference does not - accept a `type`; the family must match the rule's current evaluator - type. - properties: - name: - type: string - description: Evaluator family name. - scope: - $ref: '#/components/schemas/unstableEvaluatorScope' - description: Whether the evaluator family is project-owned or Langfuse-managed. - required: - - name - - scope - unstableLlmAsJudgeEvaluationRuleEvaluatorReference: - title: unstableLlmAsJudgeEvaluationRuleEvaluatorReference - type: object - description: >- - LLM-as-judge evaluator family reference used when creating an evaluation - rule. - properties: - name: - type: string - description: Evaluator family name. - scope: - $ref: '#/components/schemas/unstableEvaluatorScope' - description: Whether the evaluator family is project-owned or Langfuse-managed. - type: - $ref: '#/components/schemas/unstableLlmAsJudgeEvaluatorType' - nullable: true - description: Evaluator engine type. Defaults to `llm_as_judge` when omitted. - required: - - name - - scope - unstableCodeEvaluationRuleEvaluatorReference: - title: unstableCodeEvaluationRuleEvaluatorReference - type: object - description: Code evaluator family reference used when creating an evaluation rule. - properties: - name: - type: string - description: Evaluator family name. - scope: - $ref: '#/components/schemas/unstableEvaluatorScope' - description: Whether the evaluator family is project-owned or Langfuse-managed. - type: - type: string - const: code - description: Must be `code`. - required: - - name - - scope - - type - unstableLlmAsJudgeEvaluatorType: - title: unstableLlmAsJudgeEvaluatorType - type: string - enum: - - llm_as_judge - unstableEvaluationRuleEvaluator: - title: unstableEvaluationRuleEvaluator - type: object - description: |- - Resolved evaluator currently used by the evaluation rule. - - `id` is the exact active evaluator version. - `name`, `scope`, and `type` identify the evaluator family conceptually. - properties: - id: - type: string - description: >- - Identifier of the exact evaluator version currently used by the rule. - name: - type: string - description: Evaluator family name. - scope: - $ref: '#/components/schemas/unstableEvaluatorScope' - description: Whether the evaluator family is project-owned or Langfuse-managed. - type: - $ref: '#/components/schemas/unstableEvaluatorType' - description: Evaluator engine type. - required: - - id - - name - - scope - - type - unstableDeleteEvaluatorResponse: - title: unstableDeleteEvaluatorResponse - type: object - description: Confirmation response returned after successful deletion. - properties: - message: - type: string - description: Always `Evaluator successfully deleted`. - required: - - message - unstableEvaluator: - title: unstableEvaluator - type: object - properties: - type: - type: string - enum: - - llm_as_judge - - code - prompt: - type: string - description: Prompt template used during evaluation. - outputDefinition: - $ref: '#/components/schemas/unstablePublicEvaluatorOutputDefinition' - description: >- - Structured output schema returned by this evaluator. - - - Responses always include `dataType` and omit the internal - output-definition `version`. - - Use `dataType` to decide how future scores should be interpreted. - modelConfig: - $ref: '#/components/schemas/unstableEvaluatorModelConfig' - description: Explicit model configuration, or `null` when the project default - evaluation model is used. - sourceCode: - type: string - description: Source code executed for each matched observation. - sourceCodeLanguage: - $ref: '#/components/schemas/unstableCodeEvaluatorSourceCodeLanguage' - description: Runtime language for `sourceCode`. - required: - - type - unstableEvaluatorBase: - title: unstableEvaluatorBase - type: object - properties: - id: - type: string - description: Identifier of this evaluator. - name: - type: string - description: Evaluator name. - version: - type: integer - description: Version number of this evaluator. - scope: - $ref: '#/components/schemas/unstableEvaluatorScope' - description: >- - Where this evaluator comes from: your project or Langfuse-managed - defaults. - variables: - type: array - items: - type: string - description: >- - Variables that can be mapped when creating an evaluation rule. - - - LLM evaluators require every variable to be mapped exactly once. - Code evaluators always expose the fixed runtime payload fields and - Langfuse maps them automatically. - evaluationRuleCount: - type: integer - description: >- - Number of evaluation rules in the project that currently use this - evaluator version. - createdAt: - type: string - format: date-time - description: Timestamp when this evaluator was created. - updatedAt: - type: string - format: date-time - description: Timestamp when this evaluator was last updated. - required: - - id - - name - - version - - scope - - variables - - evaluationRuleCount - - createdAt - - updatedAt - unstableLlmAsJudgeEvaluator: - title: unstableLlmAsJudgeEvaluator - type: object - properties: - prompt: - type: string - description: Prompt template used during evaluation. - outputDefinition: - $ref: '#/components/schemas/unstablePublicEvaluatorOutputDefinition' - description: >- - Structured output schema returned by this evaluator. - - - Responses always include `dataType` and omit the internal - output-definition `version`. - - Use `dataType` to decide how future scores should be interpreted. - modelConfig: - $ref: '#/components/schemas/unstableEvaluatorModelConfig' - nullable: true - description: >- - Explicit model configuration, or `null` when the project default - evaluation model is used. - required: - - prompt - - outputDefinition - - modelConfig - allOf: - - $ref: '#/components/schemas/unstableEvaluatorBase' - unstableCodeEvaluator: - title: unstableCodeEvaluator - type: object - properties: - sourceCode: - type: string - description: Source code executed for each matched observation. - sourceCodeLanguage: - $ref: '#/components/schemas/unstableCodeEvaluatorSourceCodeLanguage' - description: Runtime language for `sourceCode`. - required: - - sourceCode - - sourceCodeLanguage - allOf: - - $ref: '#/components/schemas/unstableEvaluatorBase' - unstableEvaluators: - title: unstableEvaluators - type: object - properties: - data: - type: array - items: - $ref: '#/components/schemas/unstableEvaluator' - meta: - $ref: '#/components/schemas/utilsMetaResponse' - required: - - data - - meta - unstableCreateEvaluatorRequest: - title: unstableCreateEvaluatorRequest - type: object - properties: - type: - type: string - enum: - - llm_as_judge - - code - name: - type: string - description: Evaluator name within the authenticated project. - prompt: - type: string - description: Prompt template used by the evaluator. - outputDefinition: - $ref: '#/components/schemas/unstableEvaluatorOutputDefinition' - description: >- - Structured output schema the evaluator must return. - - - Always send `dataType`. - - Do not send `version`; it is an internal storage detail and not part - of the public request contract. - modelConfig: - $ref: '#/components/schemas/unstableEvaluatorModelConfig' - description: Optional explicit model configuration. Omit or set to `null` to use - the project default evaluation model. - sourceCode: - type: string - description: Code executed for each matched observation. - sourceCodeLanguage: - $ref: '#/components/schemas/unstableCodeEvaluatorSourceCodeLanguage' - description: Runtime language for `sourceCode`. - required: - - type - - name - unstableCreateLlmAsJudgeEvaluatorRequest: - title: unstableCreateLlmAsJudgeEvaluatorRequest - type: object - properties: - name: - type: string - description: Evaluator name within the authenticated project. - prompt: - type: string - description: Prompt template used by the evaluator. - outputDefinition: - $ref: '#/components/schemas/unstableEvaluatorOutputDefinition' - description: >- - Structured output schema the evaluator must return. - - - Always send `dataType`. - - Do not send `version`; it is an internal storage detail and not part - of the public request contract. - modelConfig: - $ref: '#/components/schemas/unstableEvaluatorModelConfig' - nullable: true - description: >- - Optional explicit model configuration. Omit or set to `null` to use - the project default evaluation model. - required: - - name - - prompt - - outputDefinition - unstableCreateCodeEvaluatorRequest: - title: unstableCreateCodeEvaluatorRequest - type: object - properties: - name: - type: string - description: Evaluator name within the authenticated project. - sourceCode: - type: string - description: Code executed for each matched observation. - sourceCodeLanguage: - $ref: '#/components/schemas/unstableCodeEvaluatorSourceCodeLanguage' - description: Runtime language for `sourceCode`. - required: - - name - - sourceCode - - sourceCodeLanguage - utilsMetaResponse: - title: utilsMetaResponse - type: object - properties: - page: - type: integer - description: current page number - limit: - type: integer - description: number of items per page - totalItems: - type: integer - description: number of total items given the current filters/selection (if any) - totalPages: - type: integer - description: number of total pages given the current limit - required: - - page - - limit - - totalItems - - totalPages - securitySchemes: - BasicAuth: - type: http - scheme: basic diff --git a/package.json b/package.json index fba01be..e7d161b 100644 --- a/package.json +++ b/package.json @@ -23,30 +23,28 @@ "files": [ "bin", "dist", - "openapi.yml", "README.md" ], "scripts": { "test": "bun test", + "typecheck": "tsc --noEmit", "conformance:sync": "bun conformance/src/cli.ts sync", "conformance:run": "bun conformance/src/cli.ts run", - "patch-openapi": "bun scripts/patch-openapi.ts", - "refetch-openapi": "bun scripts/patch-openapi.ts --refetch", - "build": "bun run refetch-openapi && bun build src/cli.ts --outdir dist --target node --format esm", + "conformance:all": "bun conformance/src/all.ts", + "build": "bun scripts/build.ts", "release": "bun scripts/release.ts", - "prepublishOnly": "rm -rf dist && bun run build" - }, - "dependencies": { - "specli": "^0.0.39" + "prepublishOnly": "bun run build" }, + "dependencies": {}, "devDependencies": { "@apidevtools/swagger-parser": "^12.1.0", "@types/bun": "^1.3.14", "ajv": "^8.17.1", "ajv-formats": "^3.0.1", + "typescript": "^7.0.2", "yaml": "^2.8.2" }, "engines": { - "node": ">=20" + "bun": ">=1.3.0" } } diff --git a/scripts/build.ts b/scripts/build.ts new file mode 100644 index 0000000..604a715 --- /dev/null +++ b/scripts/build.ts @@ -0,0 +1,54 @@ +import { mkdir, rm } from "node:fs/promises"; +import { resolve } from "node:path"; + +import { loadCatalog, readVerifiedSpec } from "../conformance/src/catalog"; +import { compileApiContract } from "../src/contracts/compiler"; +import type { ApiContractCatalog } from "../src/contracts/types"; + +const root = resolve(import.meta.dirname, ".."); +const dist = resolve(root, "dist"); +const contractsDirectory = resolve(dist, "contracts"); + +await rm(dist, { recursive: true, force: true }); +await mkdir(contractsDirectory, { recursive: true }); + +const sourceCatalog = await loadCatalog(); +const contractCatalog: ApiContractCatalog = { + schemaVersion: 1, + latest: sourceCatalog.versions.at(-1)!.version, + versions: sourceCatalog.versions.map((entry) => ({ + version: entry.version, + sourceSha256: entry.sha256, + })), +}; + +let totalOperations = 0; +for (const entry of sourceCatalog.versions) { + const raw = await readVerifiedSpec(entry); + const contract = compileApiContract(entry, raw); + totalOperations += contract.operations.length; + await Bun.write( + resolve(contractsDirectory, `${entry.version}.json`), + `${JSON.stringify(contract)}\n`, + ); +} +await Bun.write( + resolve(contractsDirectory, "catalog.json"), + `${JSON.stringify(contractCatalog)}\n`, +); + +const result = await Bun.build({ + entrypoints: [resolve(root, "src/cli.ts")], + outdir: dist, + target: "bun", + format: "esm", + minify: true, +}); +if (!result.success) { + for (const log of result.logs) process.stderr.write(`${log}\n`); + process.exit(1); +} + +process.stdout.write( + `Built native Bun CLI with ${sourceCatalog.versions.length} contracts and ${totalOperations} operations\n`, +); diff --git a/scripts/patch-openapi.ts b/scripts/patch-openapi.ts deleted file mode 100644 index ebe1774..0000000 --- a/scripts/patch-openapi.ts +++ /dev/null @@ -1,277 +0,0 @@ -/** - * Flattens discriminated unions (oneOf) in the OpenAPI spec into flat objects. - * - * specli generates CLI flags from request body schemas but can't handle oneOf/allOf yet. - * This script detects discriminated unions in components.schemas and merges their - * branches into a single flat object with unioned properties and intersected required. - * - * Uses parseDocument to preserve original YAML formatting of untouched nodes. - */ - -import { readFileSync, writeFileSync } from "fs"; -import { parseDocument, type Document } from "yaml"; -import { resolve } from "path"; -import { parseArgs } from "util"; - -const DEFAULT_OPENAPI_URL = "https://cloud.langfuse.com/generated/api/openapi.yml"; - -const { values: args } = parseArgs({ - args: process.argv.slice(2), - options: { - refetch: { type: "boolean", default: false }, - openapi_url: { type: "string", default: DEFAULT_OPENAPI_URL }, - }, -}); - -const specPath = resolve(import.meta.dirname!, "../openapi.yml"); - -if (args.refetch) { - const url = args.openapi_url!; - console.log(`Fetching spec from ${url}...`); - const res = await fetch(url); - if (!res.ok) { - console.error(`Failed to fetch: ${res.status} ${res.statusText}`); - process.exit(1); - } - writeFileSync(specPath, await res.text()); - console.log(`Wrote fresh spec to ${specPath}`); -} - -const raw = readFileSync(specPath, "utf-8"); -const doc: Document = parseDocument(raw); - -const schemas = doc.getIn(["components", "schemas"], true) as any; -if (!schemas || !schemas.items) { - console.log("No components.schemas found, nothing to patch."); - process.exit(0); -} - -// Convert to JS for analysis (easier to work with) -const schemasJS = schemas.toJSON() as Record; -let patchCount = 0; - -for (const [name, schema] of Object.entries(schemasJS)) { - if (!schema.oneOf || !Array.isArray(schema.oneOf)) continue; - - // Check if every branch matches the discriminated union pattern: - // { allOf: [{ properties: { : { enum: [val] } } }, { $ref }], required: [] } - const branches: Array<{ - discriminatorKey: string; - discriminatorValue: string; - refSchemaName: string; - }> = []; - - let isDiscriminatedUnion = true; - for (const branch of schema.oneOf) { - if (!branch.allOf || branch.allOf.length !== 2) { - isDiscriminatedUnion = false; - break; - } - - const [inline, ref] = branch.allOf; - const props = inline?.properties; - if (!props || !ref?.$ref) { - isDiscriminatedUnion = false; - break; - } - - // Find the discriminator: a property with a single-value enum - const discEntries = Object.entries(props).filter( - ([, v]) => v.type === "string" && Array.isArray(v.enum) && v.enum.length === 1, - ); - if (discEntries.length !== 1) { - isDiscriminatedUnion = false; - break; - } - - const [discKey, discSchema] = discEntries[0]; - const refName = ref.$ref.replace("#/components/schemas/", ""); - - branches.push({ - discriminatorKey: discKey, - discriminatorValue: discSchema.enum[0], - refSchemaName: refName, - }); - } - - if (!isDiscriminatedUnion || branches.length === 0) continue; - - // all branches should use the same discriminator key - const discKey = branches[0].discriminatorKey; - if (!branches.every((b) => b.discriminatorKey === discKey)) continue; - - const mergedProperties: Record = {}; - const requiredSets: Set[] = []; - - // property to discriminate - mergedProperties[discKey] = { - type: "string", - enum: branches.map((b) => b.discriminatorValue), - }; - - for (const branch of branches) { - const branchSchema = schemasJS[branch.refSchemaName]; - if (!branchSchema?.properties) continue; - - const branchRequired = new Set(branchSchema.required ?? []); - requiredSets.push(branchRequired); - - for (const [propName, propSchema] of Object.entries(branchSchema.properties)) { - if (propName === discKey) continue; // already handled - - if (!(propName in mergedProperties)) { - mergedProperties[propName] = structuredClone(propSchema); - } else { - // property exists in multiple branches — check for type conflict - const existing = mergedProperties[propName]; - if (JSON.stringify(existing) !== JSON.stringify(propSchema)) { - // conflict: fall back to string so specli still exposes the flag - mergedProperties[propName] = { - type: "string", - ...(existing.description ? { description: existing.description } : {}), - ...(existing.nullable ? { nullable: true } : {}), - }; - } - } - } - } - - // Required = intersection of all branches' required fields + discriminator - const intersectedRequired = - requiredSets.length > 0 - ? [...requiredSets[0]].filter((r) => requiredSets.every((s) => s.has(r))) - : []; - const required = [discKey, ...intersectedRequired.filter((r) => r !== discKey)]; - - // Strip nullable from properties that have no type (specli errors on these) - for (const [propName, propSchema] of Object.entries(mergedProperties)) { - if (propSchema.nullable && !propSchema.type) { - delete propSchema.nullable; - } - } - - const patched: any = { - title: schema.title ?? name, - type: "object", - properties: mergedProperties, - }; - if (required.length > 0) { - patched.required = required; - } - - // Replace the schema node in the document (preserves rest of doc formatting) - doc.setIn(["components", "schemas", name], doc.createNode(patched)); - patchCount++; - console.log( - `Patched ${name}: merged ${branches.length} branches, ${required.length} required fields`, - ); -} - -// Remove paths that shouldn't be exposed to CLI users -// const hiddenPaths = ["/api/public/traces", "/api/public/traces/{traceId}"]; -const hiddenPaths: string[] = []; - -const paths = doc.getIn(["paths"], true) as any; -if (paths?.items) { - paths.items = paths.items.filter((pair: any) => { - const pathStr = pair.key?.value; - if (hiddenPaths.includes(pathStr)) { - console.log(`Removed path: ${pathStr}`); - return false; - } - return true; - }); -} - -// Patch operation descriptions with examples -const examples: Record = { - prompts_create: [ - "Create a new version for the prompt with the given `name`", - "", - "Example:", - " langfuse api prompts create --type text --name my-prompt --prompt 'Hello {{name}}'", - ].join("\n"), -}; - -if (paths?.items) { - for (const pathPair of paths.items) { - const methods = pathPair.value; - if (!methods?.items) continue; - for (const methodPair of methods.items) { - const op = methodPair.value; - if (!op?.items) continue; - for (const field of op.items) { - if (field.key?.value === "operationId" && examples[field.value?.value]) { - for (const descField of op.items) { - if (descField.key?.value === "description") { - descField.value = doc.createNode(examples[field.value.value]); - break; - } - } - } - } - } - } -} - -// Rename query parameters that collide with specli's global flags. -// specli (via commander.js) reserves "--version" for CLI version display, -// so any OpenAPI query parameter named "version" becomes unusable. -const paramRenames: Record> = { - prompts_get: { version: "prompt-version" }, -}; - -let renameCount = 0; -if (paths?.items) { - for (const pathPair of paths.items) { - const methods = pathPair.value; - if (!methods?.items) continue; - for (const methodPair of methods.items) { - const op = methodPair.value; - if (!op?.items) continue; - - let operationId = ""; - for (const field of op.items) { - if (field.key?.value === "operationId") { - operationId = field.value?.value ?? ""; - break; - } - } - - const renames = paramRenames[operationId]; - if (!renames) continue; - - for (const field of op.items) { - if (field.key?.value !== "parameters") continue; - const params = field.value; - if (!params?.items) continue; - - for (const param of params.items) { - if (!param?.items) continue; - let nameField: any = null; - let inValue = ""; - for (const pf of param.items) { - if (pf.key?.value === "name") nameField = pf; - if (pf.key?.value === "in") inValue = pf.value?.value ?? ""; - } - const oldName = nameField?.value?.value; - if (oldName && inValue === "query" && renames[oldName]) { - nameField.value = doc.createNode(renames[oldName]); - renameCount++; - console.log( - `Renamed ${operationId} query param '${oldName}' → '${renames[oldName]}'`, - ); - } - } - } - } - } -} - -const dirty = patchCount > 0 || renameCount > 0; -if (dirty) { - writeFileSync(specPath, doc.toString({ singleQuote: true })); - console.log(`\nWrote patched spec to ${specPath} (${patchCount} schema(s), ${renameCount} param rename(s))`); -} else { - console.log("No patches needed."); -} diff --git a/scripts/release.ts b/scripts/release.ts index d8fb059..a2d5699 100644 --- a/scripts/release.ts +++ b/scripts/release.ts @@ -15,10 +15,9 @@ const exactReleaseFiles = new Set([ "LICENSE", "README.md", "bun.lock", - "openapi.yml", "package.json", ]); -const releasePathPrefixes = ["bin/", "scripts/", "src/"]; +const releasePathPrefixes = ["bin/", "conformance/", "scripts/", "src/"]; const rawArgs = process.argv.slice(2); const isDryRun = rawArgs.includes("--dry-run"); const allowDirty = rawArgs.includes("--allow-dirty"); @@ -119,8 +118,14 @@ async function runCommand( stdout: options.capture ? "pipe" : "inherit", stderr: options.capture ? "pipe" : "inherit", }); - const stdoutPromise = options.capture ? proc.stdout.text() : Promise.resolve(""); - const stderrPromise = options.capture ? proc.stderr.text() : Promise.resolve(""); + const stdoutPromise = + proc.stdout instanceof ReadableStream + ? new Response(proc.stdout).text() + : Promise.resolve(""); + const stderrPromise = + proc.stderr instanceof ReadableStream + ? new Response(proc.stderr).text() + : Promise.resolve(""); exitCode = await proc.exited; stdout = await stdoutPromise; stderr = await stderrPromise; @@ -252,7 +257,7 @@ async function printPostBuildReview(): Promise { "LICENSE", "README.md", "bin", - "openapi.yml", + "conformance", "package.json", "scripts", "src", @@ -379,8 +384,9 @@ async function main(): Promise { await writePackageJson(pkg); console.log(`Updated package.json to ${pkg.name}@${nextVersion}`); + await runCommand("bun", ["run", "typecheck"]); await runCommand("bun", ["test"]); - await runCommand("bun", ["run", "prepublishOnly"]); + await runCommand("bun", ["run", "conformance:all"]); await runCommand("npm", ["pack", "--dry-run"]); await printPostBuildReview(); @@ -401,7 +407,7 @@ async function main(): Promise { return; } - // prepublishOnly already ran above, and npm pack --dry-run showed the package + // conformance:all already built above, and npm pack --dry-run showed the package // contents. Avoid a second lifecycle run producing a different publish. publishStarted = true; await runCommand("npm", ["publish", "--ignore-scripts"], { diff --git a/src/cli.ts b/src/cli.ts index 2bad7f1..ce3237a 100644 --- a/src/cli.ts +++ b/src/cli.ts @@ -1,91 +1,163 @@ -import { readFileSync } from "node:fs"; +import packageJson from "../package.json"; + +import { createApiClient, renderCurl } from "./client"; +import { + loadApiContract, + loadContractCatalog, + resolveContractVersion, +} from "./contracts/loader"; +import type { + ApiBodyField, + ApiCallInput, + ApiContract, + ApiOperation, + ApiParameter, + ApiResult, + JsonValue, + ValueKind, +} from "./contracts/types"; const DEFAULT_HOST = "https://cloud.langfuse.com"; -const OPENAPI_FILE_URL = new URL("../openapi.yml", import.meta.url); +const DEFAULT_TIMEOUT_MS = 30_000; const LANGFUSE_SKILL_URL = "https://raw.githubusercontent.com/langfuse/skills/main/skills/langfuse/SKILL.md"; -const GET_SKILL_FETCH_TIMEOUT_MS = 5000; -const LANGFUSE_FLAGS = new Set([ +const GET_SKILL_FETCH_TIMEOUT_MS = 5_000; +const VALUE_FLAGS = new Set([ "--public-key", "--secret-key", "--host", "--env", + "--api-version", + "--timeout", + "--output", ]); -const LANGFUSE_BOOL_FLAGS = new Set(["--refetch-api-spec"]); +const BOOLEAN_FLAGS = new Set(["--json", "--curl", "--show-secrets"]); -function loadEnvFile(filePath: string): void { - const content = readFileSync(filePath, "utf-8"); - for (const line of content.split("\n")) { +interface ParsedGlobals { + values: Record; + booleans: Set; + args: string[]; +} + +interface RuntimeConfig { + publicKey?: string; + secretKey?: string; + host: string; + apiVersion?: string; + timeoutMs: number; + json: boolean; + curl: boolean; + showSecrets: boolean; + output?: string; +} + +class CliError extends Error { + constructor(message: string, readonly exitCode = 2) { + super(message); + } +} + +function flagKey(flag: string): string { + return flag.replace(/^--/, ""); +} + +function extractGlobals(args: string[]): ParsedGlobals { + const values: Record = {}; + const booleans = new Set(); + const remaining: string[] = []; + for (let index = 0; index < args.length; index++) { + const token = args[index]; + const equals = token.indexOf("="); + const name = equals === -1 ? token : token.slice(0, equals); + if (VALUE_FLAGS.has(name)) { + const value = equals === -1 ? args[index + 1] : token.slice(equals + 1); + if (value === undefined || (equals === -1 && value.startsWith("--"))) { + throw new CliError(`${name} requires a value`); + } + values[flagKey(name)] = value; + if (equals === -1) index++; + continue; + } + if (BOOLEAN_FLAGS.has(name)) { + booleans.add(flagKey(name)); + continue; + } + remaining.push(token); + } + return { values, booleans, args: remaining }; +} + +function parseEnv(content: string): Record { + const result: Record = {}; + for (const line of content.split(/\r?\n/)) { const trimmed = line.trim(); if (!trimmed || trimmed.startsWith("#")) continue; - const eqIdx = trimmed.indexOf("="); - if (eqIdx === -1) continue; - const key = trimmed.slice(0, eqIdx).trim(); - let val = trimmed.slice(eqIdx + 1).trim(); + const separator = trimmed.indexOf("="); + if (separator === -1) continue; + const key = trimmed.slice(0, separator).trim(); + let value = trimmed.slice(separator + 1).trim(); if ( - (val.startsWith('"') && val.endsWith('"')) || - (val.startsWith("'") && val.endsWith("'")) + (value.startsWith('"') && value.endsWith('"')) || + (value.startsWith("'") && value.endsWith("'")) ) { - val = val.slice(1, -1); + value = value.slice(1, -1); } - process.env[key] = val; + result[key] = value; } + return result; } -type MainFn = ( - argv: string[], - options?: { cliName?: string; auth?: string; embeddedSpecText?: string }, -) => Promise; - -async function loadMain(): Promise { - const specliEntry = import.meta.resolve("specli"); - const cliMainUrl = new URL("cli/main.js", specliEntry); - const mod = await import(cliMainUrl.href); - return mod.main; -} - -async function getSpecText(params: { - refetch: boolean; - host: string; -}): Promise { - if (params.refetch) { - const specUrl = `${params.host}/generated/api/openapi.yml`; - return fetchText(specUrl, "spec"); +async function runtimeConfig(globals: ParsedGlobals): Promise { + const fileEnv = globals.values.env + ? parseEnv(await Bun.file(globals.values.env).text()) + : {}; + const env = { ...process.env, ...fileEnv }; + const timeoutMs = Number(globals.values.timeout ?? DEFAULT_TIMEOUT_MS); + if (!Number.isFinite(timeoutMs) || timeoutMs <= 0) { + throw new CliError("--timeout must be a positive number of milliseconds"); } - - // Use bundled spec - return readFileSync(OPENAPI_FILE_URL, "utf-8"); + return { + publicKey: globals.values["public-key"] ?? env.LANGFUSE_PUBLIC_KEY, + secretKey: globals.values["secret-key"] ?? env.LANGFUSE_SECRET_KEY, + host: ( + globals.values.host ?? + env.LANGFUSE_BASE_URL ?? + env.LANGFUSE_HOST ?? + DEFAULT_HOST + ).replace(/\/+$/, ""), + apiVersion: globals.values["api-version"] ?? env.LANGFUSE_API_VERSION, + timeoutMs, + json: globals.booleans.has("json"), + curl: globals.booleans.has("curl"), + showSecrets: globals.booleans.has("show-secrets"), + output: globals.values.output, + }; } async function fetchText( url: string, label: string, - options?: { timeoutMs?: number }, + timeoutMs?: number, ): Promise { - const resp = await fetch(url, { - signal: - typeof options?.timeoutMs === "number" - ? AbortSignal.timeout(options.timeoutMs) - : undefined, + const response = await fetch(url, { + signal: timeoutMs ? AbortSignal.timeout(timeoutMs) : undefined, }); - if (!resp.ok) { + if (!response.ok) { throw new Error( - `Failed to fetch ${label} from ${url}: ${resp.status} ${resp.statusText}`, + `Failed to fetch ${label} from ${url}: ${response.status} ${response.statusText}`, ); } - return resp.text(); + return response.text(); } -async function getSkillText(): Promise { - return fetchText(LANGFUSE_SKILL_URL, "skill", { - timeoutMs: GET_SKILL_FETCH_TIMEOUT_MS, - }); -} - -function printGetSkillFetchError(err: unknown): void { - const reason = err instanceof Error ? err.message : String(err); - - process.stderr.write(`Failed to fetch the latest Langfuse skill from GitHub. +async function getSkill(): Promise { + try { + process.stdout.write( + await fetchText(LANGFUSE_SKILL_URL, "skill", GET_SKILL_FETCH_TIMEOUT_MS), + ); + } catch (error) { + const reason = error instanceof Error ? error.message : String(error); + process.stderr.write(`Failed to fetch the latest Langfuse skill from GitHub. This environment may block direct GitHub access. Download the skill manually from: @@ -95,68 +167,12 @@ Then add the downloaded SKILL.md to your agent context manually. Original error: ${reason} `); -} - -export async function run(argv: string[]): Promise { - const extracted: Record = {}; - const boolFlags: Record = {}; - const passthrough: string[] = [argv[0], argv[1]]; - - let i = 2; - while (i < argv.length) { - if (LANGFUSE_FLAGS.has(argv[i]) && i + 1 < argv.length) { - const key = argv[i].replace(/^--/, ""); - extracted[key] = argv[i + 1]; - i += 2; - } else if (LANGFUSE_BOOL_FLAGS.has(argv[i])) { - const key = argv[i].replace(/^--/, ""); - boolFlags[key] = true; - i++; - } else { - passthrough.push(argv[i]); - i++; - } - } - - if (extracted["env"]) { - loadEnvFile(extracted["env"]); - } - - const publicKey = - extracted["public-key"] ?? process.env.LANGFUSE_PUBLIC_KEY; - const secretKey = - extracted["secret-key"] ?? process.env.LANGFUSE_SECRET_KEY; - const host = ( - extracted["host"] ?? - process.env.LANGFUSE_BASE_URL ?? - process.env.LANGFUSE_HOST ?? - DEFAULT_HOST - ).replace(/\/$/, ""); - - // First positional arg determines the subcommand - const subcommand = passthrough[2]; - - if (subcommand === "api") { - passthrough.splice(2, 1); - return runApi({ passthrough, boolFlags, publicKey, secretKey, host }); - } - - if (subcommand === "get-skill") { - try { - process.stdout.write(await getSkillText()); - } catch (err) { - printGetSkillFetchError(err); - process.exitCode = 1; - } - return; + process.exitCode = 1; } - - // Show help for anything else (no args, --help, -h, unknown command) - printHelp(); } function printHelp(): void { - console.log(`langfuse-cli — Interact with Langfuse from the command line + process.stdout.write(`langfuse-cli — Interact with Langfuse from the command line Usage: langfuse [options] @@ -167,100 +183,531 @@ Commands: Options: --public-key Langfuse public key (or LANGFUSE_PUBLIC_KEY) --secret-key Langfuse secret key (or LANGFUSE_SECRET_KEY) - --host Langfuse host (or LANGFUSE_HOST/LANGFUSE_BASE_URL, default: ${DEFAULT_HOST}) - --env Load env vars from file - --refetch-api-spec Fetch latest API spec instead of bundled + --host Langfuse host (default: ${DEFAULT_HOST}) + --env Load env vars from a file + --api-version Select a bundled historical API contract + --timeout Request timeout (default: ${DEFAULT_TIMEOUT_MS}) + -h, --help Show help + --version Show CLI version Examples: - langfuse api __schema List all available resources - langfuse api --help Show actions for a resource - langfuse api traces list --limit 10 List traces - langfuse api prompts list List prompts - langfuse api scores create --name quality \\ - --traceId --value 0.9 Create a score - langfuse api datasets create --name my-dataset Create a dataset`); + langfuse api help + langfuse api prompts list + langfuse api prompts create --body-json '{"name":"my-prompt","type":"text","prompt":"Hello"}' + langfuse --api-version 3.150.0 api traces list +`); } -function printApiHelp(resources: string[]): void { - const sorted = [...resources].sort(); - console.log(`Usage: langfuse api [options] +function resourceMap(contract: ApiContract): Map { + const resources = new Map(); + for (const operation of contract.operations) { + const existing = resources.get(operation.command.resource) ?? []; + existing.push(operation); + resources.set(operation.command.resource, existing); + } + for (const operations of resources.values()) { + operations.sort((left, right) => + left.command.action.localeCompare(right.command.action), + ); + } + return resources; +} -Langfuse API Resources: -${sorted.map((r) => ` ${r}`).join("\n")} +function printApiHelp(contract: ApiContract): void { + const resources = [...resourceMap(contract).keys()].sort(); + process.stdout.write(`Usage: langfuse api [options] -Commands: - __schema Show API spec metadata - --help Show actions for a resource - --help Show options for an action +API snapshot: ${contract.apiVersion} + +Resources: +${resources.map((resource) => ` ${resource}`).join("\n")} + +Discovery: + api help [resource] [action] + api schema --json Machine-readable command schema + api __schema --json Backward-compatible alias + api versions list Bundled historical snapshots + +Action options: + --body-json Lossless JSON request body + --body-file Read JSON body from file or stdin + --json Stable JSON response envelope + --curl Print curl without executing +`); +} + +function printResourceHelp(contract: ApiContract, resource: string): void { + const operations = resourceMap(contract).get(resource); + if (!operations) throw new CliError(`Unknown API resource: ${resource}`); + process.stdout.write(`Usage: langfuse api ${resource} [options] + +Actions: +${operations + .map( + (operation) => + ` ${operation.command.action.padEnd(30)} ${operation.summary ?? operation.operationId}`, + ) + .join("\n")} +`); +} + +function kindLabel(kind: ValueKind): string { + return kind === "array" ? "value (repeatable)" : kind; +} +function printOperationHelp(operation: ApiOperation): void { + const positionals = operation.pathParameterOrder + .map((name) => `<${name}>`) + .join(" "); + const lines: string[] = []; + for (const parameter of operation.parameters) { + if (parameter.location === "path") continue; + lines.push( + ` --${parameter.cliName} <${kindLabel(parameter.kind)}>${parameter.required ? " (required)" : ""}`, + ); + } + if (operation.requestBody?.legacyFieldFlags) { + for (const field of operation.requestBody.fields) { + lines.push( + ` --${field.name} <${kindLabel(field.kind)}>${field.required ? " (required)" : ""}`, + ); + } + } + if (operation.requestBody) { + lines.push(" --body-json Lossless JSON body"); + lines.push(" --body-file JSON body from file or stdin"); + } + process.stdout.write(`Usage: langfuse api ${operation.command.resource} ${operation.command.action}${positionals ? ` ${positionals}` : ""} [options] + +${operation.summary ?? operation.operationId} +${operation.description ? `\n${operation.description}\n` : ""} Options: - --json Output as JSON - --curl Preview curl command without executing - -h, --help Show help +${lines.length ? lines.join("\n") : " (no operation-specific options)"} + --json JSON response envelope + --curl Print curl without executing +`); +} + +function operationByCommand( + contract: ApiContract, + resource: string, + action: string, +): ApiOperation { + const operation = contract.operations.find( + (candidate) => + candidate.command.resource === resource && candidate.command.action === action, + ); + if (!operation) { + if (!resourceMap(contract).has(resource)) { + throw new CliError(`Unknown API resource: ${resource}`); + } + throw new CliError(`Unknown action ${resource} ${action}`); + } + return operation; +} + +function parseJsonValue(value: string, kind?: ValueKind): JsonValue { + if (kind === "string") return value; + if (kind === "boolean") { + if (value === "true") return true; + if (value === "false") return false; + throw new CliError(`Expected boolean, got ${value}`); + } + if (kind === "number") { + const number = Number(value); + if (!Number.isFinite(number)) throw new CliError(`Expected number, got ${value}`); + return number; + } + if (kind === "object" || kind === "array" || kind === "null") { + let parsed: JsonValue; + try { + parsed = JSON.parse(value) as JsonValue; + } catch { + throw new CliError(`Expected ${kind} as JSON, got ${value}`); + } + if ( + (kind === "object" && + (parsed === null || typeof parsed !== "object" || Array.isArray(parsed))) || + (kind === "array" && !Array.isArray(parsed)) || + (kind === "null" && parsed !== null) + ) { + throw new CliError(`Expected ${kind} as JSON, got ${value}`); + } + return parsed; + } + try { + return JSON.parse(value) as JsonValue; + } catch { + return value; + } +} -Workflow: - 1) langfuse api __schema - 2) langfuse api --help - 3) langfuse api --help - 4) langfuse api [options]`); +function addParameterValue( + input: ApiCallInput, + parameter: ApiParameter, + raw: string | undefined, +): void { + const target = + parameter.location === "path" + ? input.path + : parameter.location === "query" + ? input.query + : parameter.location === "header" + ? input.headers + : input.cookies; + if (raw === undefined && parameter.kind !== "boolean") { + throw new CliError(`--${parameter.cliName} requires a value`); + } + const parsed = parseJsonValue(raw ?? "true", parameter.itemKind ?? parameter.kind); + if (parameter.kind === "array") { + const existing = target[parameter.name]; + if (Array.isArray(existing)) existing.push(parsed); + else target[parameter.name] = [parsed]; + } else { + target[parameter.name] = parsed; + } +} + +function setBodyValue( + body: Record, + path: string[], + raw: string | undefined, + field?: ApiBodyField, +): void { + let target = body; + for (const segment of path.slice(0, -1)) { + const existing = target[segment]; + if (!existing || typeof existing !== "object" || Array.isArray(existing)) { + target[segment] = {}; + } + target = target[segment] as Record; + } + const name = path.at(-1)!; + const kind = path.length > 1 || field?.kind === "array" ? undefined : field?.kind; + const parsed = parseJsonValue(raw ?? "true", kind); + const existing = target[name]; + if (field?.kind === "array") { + if (Array.isArray(parsed)) target[name] = parsed; + else if (Array.isArray(existing)) existing.push(parsed); + else target[name] = [parsed]; + } else if (existing !== undefined) { + target[name] = Array.isArray(existing) ? [...existing, parsed] : [existing, parsed]; + } else { + target[name] = parsed; + } } -async function getResources(specText: string): Promise { - // Run specli's __schema --json to get the canonical resource list - const main = await loadMain(); - const chunks: string[] = []; - const origWrite = process.stdout.write.bind(process.stdout); - process.stdout.write = (chunk: any) => { - chunks.push(String(chunk)); - return true; +function splitOption(token: string): { name: string; inline?: string; negated: boolean } { + const separator = token.indexOf("="); + const rawName = separator === -1 ? token.slice(2) : token.slice(2, separator); + return { + name: rawName.startsWith("no-") ? rawName.slice(3) : rawName, + ...(separator === -1 ? {} : { inline: token.slice(separator + 1) }), + negated: rawName.startsWith("no-"), }; +} + +async function readBodyFile(path: string): Promise { + const text = path === "-" ? await Bun.stdin.text() : await Bun.file(path).text(); try { - await main(["node", "langfuse", "__schema", "--json"], { - cliName: "langfuse api", - auth: "BasicAuth", - embeddedSpecText: specText, - }); - } finally { - process.stdout.write = origWrite; - } - const output = JSON.parse(chunks.join("")); - return (output.data?.resources ?? []).map((r: any) => r.name); + return JSON.parse(text) as JsonValue; + } catch (error) { + throw new CliError( + `Invalid JSON in ${path === "-" ? "stdin" : path}: ${error instanceof Error ? error.message : String(error)}`, + ); + } } -async function runApi(params: { - passthrough: string[]; - boolFlags: Record; - publicKey: string | undefined; - secretKey: string | undefined; - host: string; -}): Promise { - const { passthrough, boolFlags, publicKey, secretKey, host } = params; +async function parseOperationInput( + operation: ApiOperation, + tokens: string[], +): Promise { + const input: ApiCallInput = { + path: {}, + query: {}, + headers: {}, + cookies: {}, + }; + const parameterByFlag = new Map(); + for (const parameter of operation.parameters) { + if (parameter.location !== "path") { + parameterByFlag.set(parameter.cliName, parameter); + } + } + if (operation.operationId === "prompts_get") { + const version = operation.parameters.find( + (parameter) => parameter.location === "query" && parameter.name === "version", + ); + if (version) parameterByFlag.set("prompt-version", version); + } + const positionals: string[] = []; + let fieldBody: Record | undefined; + let completeBody: JsonValue | undefined; + for (let index = 0; index < tokens.length; index++) { + const token = tokens[index]; + if (!token.startsWith("--")) { + positionals.push(token); + continue; + } + const option = splitOption(token); + let raw = option.inline; + if ( + raw === undefined && + tokens[index + 1] !== undefined && + !tokens[index + 1].startsWith("--") + ) { + raw = tokens[++index]; + } + if (option.name === "body-json") { + if (raw === undefined) throw new CliError("--body-json requires a value"); + try { + completeBody = JSON.parse(raw) as JsonValue; + } catch (error) { + throw new CliError( + `Invalid --body-json: ${error instanceof Error ? error.message : String(error)}`, + ); + } + continue; + } + if (option.name === "body-file") { + if (raw === undefined) throw new CliError("--body-file requires a path or -"); + completeBody = await readBodyFile(raw); + continue; + } + const parameter = parameterByFlag.get(option.name); + if (parameter) { + if (option.negated && parameter.kind !== "boolean") { + throw new CliError(`--no-${option.name} is only valid for boolean options`); + } + addParameterValue(input, parameter, option.negated ? "false" : raw); + continue; + } + if (!operation.requestBody) { + throw new CliError(`Unknown option --${option.name}`); + } + if (!operation.requestBody.legacyFieldFlags) { + throw new CliError( + `${operation.operationId} requires --body-json or --body-file for request bodies`, + ); + } + const path = option.name.split(".").filter(Boolean); + const field = operation.requestBody.fields.find( + (candidate) => candidate.name === path[0], + ); + if (!field) throw new CliError(`Unknown option --${option.name}`); + if (option.negated && field.kind !== "boolean") { + throw new CliError(`--no-${option.name} is only valid for boolean options`); + } + if (raw === undefined && field.kind !== "boolean") { + throw new CliError(`--${option.name} requires a value`); + } + fieldBody ??= {}; + setBodyValue(fieldBody, path, option.negated ? "false" : raw, field); + } + if (completeBody !== undefined && fieldBody !== undefined) { + throw new CliError("Do not mix --body-json/--body-file with body field flags"); + } + if (positionals.length !== operation.pathParameterOrder.length) { + throw new CliError( + `${operation.operationId} expects ${operation.pathParameterOrder.length} path argument(s), got ${positionals.length}`, + ); + } + for (let index = 0; index < operation.pathParameterOrder.length; index++) { + const name = operation.pathParameterOrder[index]; + const parameter = operation.parameters.find( + (candidate) => candidate.location === "path" && candidate.name === name, + ); + if (!parameter) throw new CliError(`Missing path parameter contract: ${name}`); + input.path[name] = parseJsonValue(positionals[index], parameter.kind); + } + for (const parameter of operation.parameters) { + const target = + parameter.location === "path" + ? input.path + : parameter.location === "query" + ? input.query + : parameter.location === "header" + ? input.headers + : input.cookies; + if (parameter.required && target[parameter.name] === undefined) { + throw new CliError(`Missing required option --${parameter.cliName}`); + } + } + let body = completeBody ?? fieldBody; + if (completeBody === undefined && operation.requestBody?.legacyFieldFlags) { + const missing = operation.requestBody.fields + .filter((field) => field.required && fieldBody?.[field.name] === undefined) + .map((field) => `--${field.name}`); + if (missing.length > 0) { + throw new CliError(`Missing required body option(s): ${missing.join(", ")}`); + } + if (body === undefined && operation.requestBody.required) body = {}; + } + if (operation.requestBody?.required && body === undefined) { + throw new CliError(`${operation.operationId} requires a request body`); + } + if (body !== undefined) input.body = body; + return input; +} - const specText = await getSpecText({ - refetch: boolFlags["refetch-api-spec"] ?? false, - host, - }); +function schemaOutput(contract: ApiContract) { + return { + schemaVersion: 1, + apiVersion: contract.apiVersion, + sourceSha256: contract.sourceSha256, + resources: [...resourceMap(contract)].map(([name, operations]) => ({ + name, + actions: operations.map((operation) => ({ + name: operation.command.action, + canonicalName: operation.command.canonicalAction, + ...(operation.command.aliasOf + ? { aliasOf: operation.command.aliasOf } + : {}), + operationId: operation.operationId, + method: operation.method, + path: operation.path, + auth: operation.auth, + pathParameterOrder: operation.pathParameterOrder, + parameters: operation.parameters, + ...(operation.requestBody + ? { requestBody: operation.requestBody } + : {}), + ...(operation.summary ? { summary: operation.summary } : {}), + ...(operation.description + ? { description: operation.description } + : {}), + })), + })), + }; +} + +async function writeResult( + result: ApiResult, + config: RuntimeConfig, +): Promise { + if (config.output) { + const content = + typeof result.body === "string" + ? result.body + : JSON.stringify(result.body, null, 2); + await Bun.write(config.output, content ?? ""); + } else if (config.json) { + process.stdout.write( + `${JSON.stringify({ status: result.status, headers: result.headers, body: result.body })}\n`, + ); + } else if (typeof result.body === "string") { + process.stdout.write(result.body.endsWith("\n") ? result.body : `${result.body}\n`); + } else if (result.body !== null) { + process.stdout.write(`${JSON.stringify(result.body, null, 2)}\n`); + } + if (!result.ok) process.exitCode = 1; +} - // Intercept help: no args, --help, or -h - const args = passthrough.slice(2); +async function runApi(config: RuntimeConfig, args: string[]): Promise { + const catalog = await loadContractCatalog(); + if (args[0] === "versions") { + const action = args[1] ?? "list"; + if (action === "list") { + process.stdout.write( + `${catalog.versions.map((entry) => entry.version).join("\n")}\n`, + ); + return; + } + if (action === "current") { + process.stdout.write(`${config.apiVersion ?? catalog.latest}\n`); + return; + } + if (action === "detect") { + const resolved = await resolveContractVersion({ + requested: "auto", + host: config.host, + timeoutMs: config.timeoutMs, + catalog, + }); + process.stdout.write( + `${resolved.detected} -> ${resolved.version}\n`, + ); + return; + } + throw new CliError(`Unknown versions action: ${action}`); + } + const resolved = await resolveContractVersion({ + requested: config.apiVersion, + host: config.host, + timeoutMs: config.timeoutMs, + catalog, + }); + const contract = await loadApiContract(resolved.version); if ( args.length === 0 || - (args.length === 1 && (args[0] === "--help" || args[0] === "-h")) + (args[0] === "help" && args.length === 1) || + args[0] === "--help" || + args[0] === "-h" ) { - printApiHelp(await getResources(specText)); + printApiHelp(contract); return; } - - const specliArgv = [...passthrough]; - const inject: string[] = ["--server", host]; - if (publicKey) inject.push("--username", publicKey); - if (secretKey) inject.push("--password", secretKey); - specliArgv.splice(2, 0, ...inject); - - const main = await loadMain(); - await main(specliArgv, { - cliName: "langfuse api", - auth: "BasicAuth", - embeddedSpecText: specText, + if (["schema", "__schema", "__spec"].includes(args[0])) { + const schema = schemaOutput(contract); + if (config.json) process.stdout.write(`${JSON.stringify(schema)}\n`); + else printApiHelp(contract); + return; + } + if (args[0] === "help") { + if (!args[1]) printApiHelp(contract); + else if (!args[2]) printResourceHelp(contract, args[1]); + else printOperationHelp(operationByCommand(contract, args[1], args[2])); + return; + } + const resource = args[0]; + if (!args[1] || args[1] === "help" || args[1] === "--help" || args[1] === "-h") { + printResourceHelp(contract, resource); + return; + } + const operation = operationByCommand(contract, resource, args[1]); + if (args[2] === "help" || args[2] === "--help" || args[2] === "-h") { + printOperationHelp(operation); + return; + } + const input = await parseOperationInput(operation, args.slice(2)); + const client = createApiClient({ + host: config.host, + publicKey: config.publicKey, + secretKey: config.secretKey, + timeoutMs: config.timeoutMs, }); + if (config.curl) { + process.stdout.write( + `${renderCurl(client.prepare(operation, input), { showSecrets: config.showSecrets })}\n`, + ); + return; + } + await writeResult(await client.call(operation, input), config); +} + +export async function run(argv: string[]): Promise { + try { + const globals = extractGlobals(argv.slice(2)); + const [command, ...args] = globals.args; + if (command === "--version") { + process.stdout.write(`${packageJson.version}\n`); + return; + } + if (!command || command === "--help" || command === "-h") { + printHelp(); + return; + } + if (command === "get-skill") { + await getSkill(); + return; + } + if (command !== "api") { + throw new CliError(`Unknown command: ${command}`); + } + await runApi(await runtimeConfig(globals), args); + } catch (error) { + const message = error instanceof Error ? error.message : String(error); + process.stderr.write(`${message}\n`); + process.exitCode = error instanceof CliError ? error.exitCode : 1; + } } diff --git a/src/client.ts b/src/client.ts new file mode 100644 index 0000000..43afc74 --- /dev/null +++ b/src/client.ts @@ -0,0 +1,191 @@ +import type { + ApiCallInput, + ApiClientConfig, + ApiOperation, + ApiResult, + JsonValue, +} from "./contracts/types"; + +export interface PreparedRequest { + url: URL; + method: string; + headers: Headers; + body?: string; +} + +function primitive(value: JsonValue): string { + if (value === null) return ""; + if (typeof value === "object") return JSON.stringify(value); + return String(value); +} + +function pathValue(value: JsonValue, style: string, explode: boolean): string { + if (style !== "simple") throw new Error(`Unsupported path style: ${style}`); + if (Array.isArray(value)) return value.map(primitive).join(","); + if (value && typeof value === "object") { + const entries = Object.entries(value); + return explode + ? entries.map(([key, item]) => `${key}=${primitive(item)}`).join(",") + : entries.flatMap(([key, item]) => [key, primitive(item)]).join(","); + } + return primitive(value); +} + +function queryValues( + name: string, + value: JsonValue, + style: string, + explode: boolean, +): Array<[string, string]> { + if (style !== "form") throw new Error(`Unsupported query style: ${style}`); + if (Array.isArray(value)) { + return explode + ? value.map((item) => [name, primitive(item)]) + : [[name, value.map(primitive).join(",")]]; + } + if (value && typeof value === "object") { + const entries = Object.entries(value); + return explode + ? entries.map(([key, item]) => [key, primitive(item)]) + : [[name, entries.flatMap(([key, item]) => [key, primitive(item)]).join(",")]]; + } + return [[name, primitive(value)]]; +} + +function encodePathComponent(value: string): string { + return encodeURIComponent(value); +} + +export function prepareRequest( + config: ApiClientConfig, + operation: ApiOperation, + input: ApiCallInput, +): PreparedRequest { + let pathname = operation.path; + const headers = new Headers(); + const cookies: string[] = []; + const query: Array<[string, string]> = []; + for (const parameter of operation.parameters) { + const source = + parameter.location === "path" + ? input.path + : parameter.location === "query" + ? input.query + : parameter.location === "header" + ? input.headers + : input.cookies; + const value = source[parameter.name]; + if (value === undefined) continue; + if (parameter.location === "path") { + pathname = pathname.replace( + `{${parameter.name}}`, + encodePathComponent(pathValue(value, parameter.style, parameter.explode)), + ); + } else if (parameter.location === "query") { + query.push( + ...queryValues( + parameter.name, + value, + parameter.style, + parameter.explode, + ), + ); + } else if (parameter.location === "header") { + headers.set(parameter.name, primitive(value)); + } else { + cookies.push(`${parameter.name}=${primitive(value)}`); + } + } + if (cookies.length > 0) headers.set("cookie", cookies.join("; ")); + if (operation.auth.required && operation.auth.schemes.includes("BasicAuth")) { + if (!config.publicKey || !config.secretKey) { + throw new Error( + "This operation requires LANGFUSE_PUBLIC_KEY and LANGFUSE_SECRET_KEY", + ); + } + headers.set( + "authorization", + `Basic ${Buffer.from(`${config.publicKey}:${config.secretKey}`).toString("base64")}`, + ); + } + let body: string | undefined; + if (input.body !== undefined) { + if (!operation.requestBody) { + throw new Error(`${operation.operationId} does not accept a request body`); + } + headers.set("content-type", operation.requestBody.contentType); + body = JSON.stringify(input.body); + } + headers.set("accept", "application/json"); + const host = config.host.endsWith("/") ? config.host : `${config.host}/`; + const url = new URL(pathname.replace(/^\//, ""), host); + for (const [name, value] of query) url.searchParams.append(name, value); + return { + url, + method: operation.method, + headers, + ...(body !== undefined ? { body } : {}), + }; +} + +async function responseBody(response: Response): Promise { + if (response.status === 204 || response.status === 205) return null; + const text = await response.text(); + if (!text) return null; + const contentType = response.headers.get("content-type") ?? ""; + if (contentType.includes("json") || contentType.includes("+json")) { + try { + return JSON.parse(text) as JsonValue; + } catch { + return text; + } + } + return text; +} + +export function createApiClient(config: ApiClientConfig) { + return { + prepare(operation: ApiOperation, input: ApiCallInput): PreparedRequest { + return prepareRequest(config, operation, input); + }, + + async call(operation: ApiOperation, input: ApiCallInput): Promise { + const prepared = prepareRequest(config, operation, input); + const response = await fetch(prepared.url, { + method: prepared.method, + headers: prepared.headers, + body: prepared.body, + signal: AbortSignal.timeout(config.timeoutMs), + }); + return { + status: response.status, + headers: Object.fromEntries(response.headers.entries()), + body: await responseBody(response), + ok: response.ok, + }; + }, + }; +} + +function shellQuote(value: string): string { + if (/^[A-Za-z0-9_./:@%+=,-]+$/.test(value)) return value; + return `'${value.replaceAll("'", `'"'"'`)}'`; +} + +export function renderCurl( + prepared: PreparedRequest, + options: { showSecrets: boolean }, +): string { + const parts = ["curl", "--request", prepared.method, shellQuote(prepared.url.href)]; + for (const [name, value] of prepared.headers.entries()) { + const rendered = + name.toLowerCase() === "authorization" && !options.showSecrets + ? "Basic " + : value; + parts.push("--header", shellQuote(`${name}: ${rendered}`)); + } + if (prepared.body !== undefined) { + parts.push("--data", shellQuote(prepared.body)); + } + return parts.join(" "); +} diff --git a/src/contracts/compiler.ts b/src/contracts/compiler.ts new file mode 100644 index 0000000..7794ebe --- /dev/null +++ b/src/contracts/compiler.ts @@ -0,0 +1,337 @@ +import { parse } from "yaml"; + +import { planCommandNames } from "../../conformance/src/naming"; +import type { + ApiBodyField, + ApiContract, + ApiOperation, + ApiParameter, + HttpMethod, + ValueKind, +} from "./types"; + +interface ContractSource { + version: string; + ref: string; + sha256: string; +} + +const HTTP_METHODS = [ + "get", + "post", + "put", + "patch", + "delete", + "options", + "head", + "trace", +] as const; + +const LEGACY_FIELD_FLAGS_UNSUPPORTED = new Set([ + "annotationQueues_createQueue", + "datasetItems_create", + "datasetRunItems_create", + "datasets_create", + "ingestion_batch", + "legacy_scoreV1_create", + "models_create", + "opentelemetry_exportTraces", + "promptVersion_update", + "prompts_create", + "scim_createUser", + "score_create", + "trace_deleteMultiple", + "unstable_dashboardWidgets_create", + "unstable_dashboards_addPlacement", + "unstable_dashboards_create", + "unstable_evaluationRules_create", + "unstable_evaluators_create", +]); + +function kebabCase(input: string): string { + return input + .trim() + .replace(/([a-z0-9])([A-Z])/g, "$1-$2") + .replace(/[\s_.:/]+/g, "-") + .replace(/[^a-zA-Z0-9-]/g, "-") + .replace(/-+/g, "-") + .replace(/^-|-$/g, "") + .toLowerCase(); +} + +function resolveLocalRef( + document: Record, + value: Record, +): Record { + let current = value; + const seen = new Set(); + while (current?.$ref) { + const ref = String(current.$ref); + if (!ref.startsWith("#/")) { + throw new Error(`External OpenAPI references are unsupported: ${ref}`); + } + if (seen.has(ref)) throw new Error(`Circular OpenAPI reference: ${ref}`); + seen.add(ref); + current = ref + .slice(2) + .split("/") + .map((segment) => segment.replaceAll("~1", "/").replaceAll("~0", "~")) + .reduce((node, segment) => node?.[segment], document); + if (!current) throw new Error(`Unresolved OpenAPI reference: ${ref}`); + } + return current; +} + +function schemaKind( + document: Record, + rawSchema: Record = {}, +): ValueKind { + const schema = resolveLocalRef(document, rawSchema); + const type = Array.isArray(schema.type) + ? schema.type.find((candidate: string) => candidate !== "null") + : schema.type; + if (type === "integer" || type === "number") return "number"; + if (type === "boolean") return "boolean"; + if (type === "array") return "array"; + if (type === "object" || schema.properties || schema.additionalProperties) { + return "object"; + } + if (type === "null") return "null"; + if (schema.allOf || schema.oneOf || schema.anyOf) { + const branches = schema.allOf ?? schema.oneOf ?? schema.anyOf; + const kinds = new Set( + branches.map((branch: Record) => schemaKind(document, branch)), + ); + return kinds.size === 1 ? [...kinds][0] : "object"; + } + return "string"; +} + +function parameterDefaults(location: string): { style: string; explode: boolean } { + if (location === "query" || location === "cookie") { + return { style: "form", explode: true }; + } + return { style: "simple", explode: false }; +} + +function mergeParameters( + document: Record, + pathParameters: any[] = [], + operationParameters: any[] = [], +): ApiParameter[] { + const merged = new Map(); + for (const raw of [...pathParameters, ...operationParameters]) { + const parameter = resolveLocalRef(document, raw); + const location = parameter.in; + if (!parameter.name || !["path", "query", "header", "cookie"].includes(location)) { + continue; + } + const schema = resolveLocalRef(document, parameter.schema ?? { type: "string" }); + const defaults = parameterDefaults(location); + const kind = schemaKind(document, schema); + merged.set(`${location}:${parameter.name}`, { + location, + name: String(parameter.name), + cliName: kebabCase(String(parameter.name)), + required: location === "path" || Boolean(parameter.required), + style: parameter.style ?? defaults.style, + explode: parameter.explode ?? defaults.explode, + kind, + ...(kind === "array" + ? { itemKind: schemaKind(document, schema.items ?? { type: "string" }) } + : {}), + }); + } + return [...merged.values()].sort((left, right) => { + if (left.location !== right.location) { + return left.location.localeCompare(right.location); + } + return left.name.localeCompare(right.name); + }); +} + +function collectBodyFields( + document: Record, + rawSchema: Record, +): ApiBodyField[] { + const fields = new Map(); + const visit = (raw: Record, inheritedRequired = new Set()) => { + const schema = resolveLocalRef(document, raw); + const required = new Set([ + ...inheritedRequired, + ...(schema.required ?? []), + ]); + for (const branch of [ + ...(schema.allOf ?? []), + ...(schema.oneOf ?? []), + ...(schema.anyOf ?? []), + ]) { + visit(branch, required); + } + for (const [name, rawProperty] of Object.entries>( + schema.properties ?? {}, + )) { + const property = resolveLocalRef(document, rawProperty); + const existing = fields.get(name); + fields.set(name, { + name, + required: Boolean(existing?.required || required.has(name)), + kind: existing?.kind ?? schemaKind(document, property), + ...(property.description + ? { description: String(property.description) } + : existing?.description + ? { description: existing.description } + : {}), + }); + } + }; + visit(rawSchema); + return [...fields.values()].sort((left, right) => + left.name.localeCompare(right.name), + ); +} + +function normalizeAuth( + document: Record, + operation: Record, +): ApiOperation["auth"] { + const requirements = operation.security ?? document.security ?? []; + const schemes = [ + ...new Set( + requirements.flatMap((requirement: Record) => + Object.keys(requirement ?? {}), + ), + ), + ].sort(); + for (const name of schemes) { + const scheme = resolveLocalRef( + document, + document.components?.securitySchemes?.[name] ?? {}, + ); + if (scheme.type !== "http" || scheme.scheme !== "basic") { + throw new Error(`Unsupported authentication scheme: ${name}`); + } + } + return { + required: + requirements.length > 0 && + !requirements.some( + (requirement: Record) => + Object.keys(requirement ?? {}).length === 0, + ), + schemes, + }; +} + +function normalizeRequestBody( + document: Record, + operationId: string, + raw: Record | undefined, +): ApiOperation["requestBody"] { + if (!raw) return undefined; + const body = resolveLocalRef(document, raw); + const contentTypes = Object.keys(body.content ?? {}); + const contentType = + contentTypes.find((candidate) => candidate === "application/json") ?? + contentTypes.find((candidate) => candidate.includes("+json")); + if (!contentType) { + throw new Error( + `${operationId}: only JSON request bodies are supported (${contentTypes.join(", ")})`, + ); + } + const rawSchema = body.content?.[contentType]?.schema; + if (!rawSchema) throw new Error(`${operationId}: request body has no schema`); + return { + required: Boolean(body.required), + contentType, + legacyFieldFlags: !LEGACY_FIELD_FLAGS_UNSUPPORTED.has(operationId), + fields: collectBodyFields(document, rawSchema), + }; +} + +export function compileApiContract( + source: ContractSource, + raw: string, +): ApiContract { + const document = parse(raw, { + maxAliasCount: 100_000, + uniqueKeys: true, + }) as Record; + if (!String(document.openapi ?? "").startsWith("3.0.")) { + throw new Error(`${source.ref}: expected OpenAPI 3.0.x`); + } + const pending: Array<{ + key: string; + operationId: string; + method: HttpMethod; + path: string; + tags: string[]; + auth: ApiOperation["auth"]; + pathParameterOrder: string[]; + parameters: ApiParameter[]; + requestBody?: ApiOperation["requestBody"]; + summary?: string; + description?: string; + }> = []; + for (const [path, rawPathItem] of Object.entries>( + document.paths ?? {}, + )) { + const pathItem = resolveLocalRef(document, rawPathItem); + for (const method of HTTP_METHODS) { + if (!pathItem[method]) continue; + const operation = resolveLocalRef(document, pathItem[method]); + if (operation.callbacks) { + throw new Error(`${method.toUpperCase()} ${path}: callbacks are unsupported`); + } + const operationId = String( + operation.operationId ?? `${method.toUpperCase()}:${path}`, + ); + pending.push({ + key: `${method.toUpperCase()} ${path}`, + operationId, + method: method.toUpperCase() as HttpMethod, + path, + tags: (operation.tags ?? []).map(String), + auth: normalizeAuth(document, operation), + pathParameterOrder: [...path.matchAll(/\{([^}]+)\}/g)].map( + (match) => match[1], + ), + parameters: mergeParameters( + document, + pathItem.parameters, + operation.parameters, + ), + requestBody: normalizeRequestBody( + document, + operationId, + operation.requestBody, + ), + ...(operation.summary ? { summary: String(operation.summary) } : {}), + ...(operation.description + ? { description: String(operation.description) } + : {}), + }); + } + } + pending.sort((left, right) => { + if (left.path !== right.path) return left.path.localeCompare(right.path); + return left.method.localeCompare(right.method); + }); + const names = planCommandNames(pending); + const operations: ApiOperation[] = pending.map( + ({ tags: _tags, ...operation }, index) => ({ + ...operation, + command: names[index], + }), + ); + const operationIds = new Set(operations.map((operation) => operation.operationId)); + if (operationIds.size !== operations.length) { + throw new Error(`${source.ref}: duplicate operationId`); + } + return { + schemaVersion: 1, + apiVersion: source.version, + sourceSha256: source.sha256, + operations, + }; +} diff --git a/src/contracts/loader.ts b/src/contracts/loader.ts new file mode 100644 index 0000000..c08ba7c --- /dev/null +++ b/src/contracts/loader.ts @@ -0,0 +1,101 @@ +import type { + ApiContract, + ApiContractCatalog, + ApiContractCatalogEntry, +} from "./types"; + +const CATALOG_URL = new URL("./contracts/catalog.json", import.meta.url); + +function parseVersion(version: string): [number, number, number] | undefined { + const match = /^v?(\d+)\.(\d+)\.(\d+)(?:[-+].*)?$/.exec(version); + if (!match) return undefined; + return [Number(match[1]), Number(match[2]), Number(match[3])]; +} + +function compareVersion(left: string, right: string): number { + const a = parseVersion(left); + const b = parseVersion(right); + if (!a || !b) return left.localeCompare(right); + for (let index = 0; index < 3; index++) { + if (a[index] !== b[index]) return a[index] - b[index]; + } + return 0; +} + +export async function loadContractCatalog(): Promise { + const catalog = (await Bun.file(CATALOG_URL).json()) as ApiContractCatalog; + if (catalog.schemaVersion !== 1 || !Array.isArray(catalog.versions)) { + throw new Error("Invalid bundled API contract catalog"); + } + return catalog; +} + +async function detectServerVersion(host: string, timeoutMs: number): Promise { + const response = await fetch(`${host}/api/public/health`, { + signal: AbortSignal.timeout(timeoutMs), + }); + if (!response.ok) { + throw new Error(`API version detection failed: HTTP ${response.status}`); + } + const body = (await response.json()) as { version?: unknown }; + if (typeof body.version !== "string" || !parseVersion(body.version)) { + throw new Error("API version detection returned no semantic version"); + } + return body.version.replace(/^v/, ""); +} + +function compatibleEntry( + entries: ApiContractCatalogEntry[], + serverVersion: string, +): ApiContractCatalogEntry | undefined { + const target = parseVersion(serverVersion); + if (!target) return undefined; + return [...entries] + .filter((entry) => { + const version = parseVersion(entry.version); + return version?.[0] === target[0] && compareVersion(entry.version, serverVersion) <= 0; + }) + .sort((left, right) => compareVersion(right.version, left.version))[0]; +} + +export async function resolveContractVersion(params: { + requested?: string; + host: string; + timeoutMs: number; + catalog?: ApiContractCatalog; +}): Promise<{ catalog: ApiContractCatalog; version: string; detected?: string }> { + const catalog = params.catalog ?? (await loadContractCatalog()); + const requested = params.requested ?? "latest"; + if (requested === "latest") { + return { catalog, version: catalog.latest }; + } + if (requested === "auto") { + const detected = await detectServerVersion(params.host, params.timeoutMs); + const exact = catalog.versions.find((entry) => entry.version === detected); + const compatible = exact ?? compatibleEntry(catalog.versions, detected); + if (!compatible) { + throw new Error( + `No bundled API contract is compatible with detected server ${detected}`, + ); + } + return { catalog, version: compatible.version, detected }; + } + const exact = catalog.versions.find((entry) => entry.version === requested); + if (!exact) { + throw new Error( + `Unknown API version ${requested}. Available: ${catalog.versions + .map((entry) => entry.version) + .join(", ")}`, + ); + } + return { catalog, version: exact.version }; +} + +export async function loadApiContract(version: string): Promise { + const url = new URL(`./contracts/${encodeURIComponent(version)}.json`, import.meta.url); + const contract = (await Bun.file(url).json()) as ApiContract; + if (contract.schemaVersion !== 1 || contract.apiVersion !== version) { + throw new Error(`Invalid bundled API contract for ${version}`); + } + return contract; +} diff --git a/src/contracts/types.ts b/src/contracts/types.ts new file mode 100644 index 0000000..9f39db7 --- /dev/null +++ b/src/contracts/types.ts @@ -0,0 +1,112 @@ +export type JsonPrimitive = string | number | boolean | null; +export type JsonValue = + | JsonPrimitive + | JsonValue[] + | { [key: string]: JsonValue }; + +export type HttpMethod = + | "GET" + | "POST" + | "PUT" + | "PATCH" + | "DELETE" + | "OPTIONS" + | "HEAD" + | "TRACE"; + +export type ValueKind = + | "string" + | "number" + | "boolean" + | "array" + | "object" + | "null"; + +export interface CommandName { + resource: string; + action: string; + canonicalAction: string; + aliasOf?: string; +} + +export interface ApiParameter { + location: "path" | "query" | "header" | "cookie"; + name: string; + cliName: string; + required: boolean; + style: string; + explode: boolean; + kind: ValueKind; + itemKind?: ValueKind; +} + +export interface ApiBodyField { + name: string; + required: boolean; + kind: ValueKind; + description?: string; +} + +export interface ApiRequestBody { + required: boolean; + contentType: string; + legacyFieldFlags: boolean; + fields: ApiBodyField[]; +} + +export interface ApiOperation { + key: string; + operationId: string; + method: HttpMethod; + path: string; + auth: { + required: boolean; + schemes: string[]; + }; + command: CommandName; + pathParameterOrder: string[]; + parameters: ApiParameter[]; + requestBody?: ApiRequestBody; + summary?: string; + description?: string; +} + +export interface ApiContract { + schemaVersion: 1; + apiVersion: string; + sourceSha256: string; + operations: ApiOperation[]; +} + +export interface ApiContractCatalogEntry { + version: string; + sourceSha256: string; +} + +export interface ApiContractCatalog { + schemaVersion: 1; + latest: string; + versions: ApiContractCatalogEntry[]; +} + +export interface ApiCallInput { + path: Record; + query: Record; + headers: Record; + cookies: Record; + body?: JsonValue; +} + +export interface ApiClientConfig { + host: string; + publicKey?: string; + secretKey?: string; + timeoutMs: number; +} + +export interface ApiResult { + status: number; + headers: Record; + body: JsonValue | string | null; + ok: boolean; +} diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..0d2a1ef --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,18 @@ +{ + "compilerOptions": { + "target": "ES2022", + "module": "Preserve", + "moduleResolution": "Bundler", + "resolveJsonModule": true, + "strict": true, + "noEmit": true, + "types": ["bun"], + "skipLibCheck": true + }, + "include": [ + "src/**/*.ts", + "scripts/**/*.ts", + "conformance/src/**/*.ts" + ], + "exclude": ["**/*.test.ts"] +}