Compare commits

...
334 Commits
Author SHA1 Message Date
Nimar 942f70105a chore: release v3.117.2
Codespell / Check for spelling errors (push) Waiting to run
CI/CD / pre-job (push) Waiting to run
CI/CD / lint (push) Blocked by required conditions
CI/CD / prettier-check (push) Blocked by required conditions
CI/CD / test-docker-build (push) Blocked by required conditions
CI/CD / tests-web-sync (node24, pg12) (push) Blocked by required conditions
CI/CD / tests-web-sync (node24, pg15) (push) Blocked by required conditions
CI/CD / tests-web-async (node24, pg12, mode) (push) Blocked by required conditions
CI/CD / tests-web-async (node24, pg15, mode) (push) Blocked by required conditions
CI/CD / tests-web-async (node24, pg12, mode-azure) (push) Blocked by required conditions
CI/CD / tests-web-async (node24, pg15, mode-azure) (push) Blocked by required conditions
CI/CD / tests-web-async (node24, pg12, mode-redis-cluster) (push) Blocked by required conditions
CI/CD / tests-web-async (node24, pg15, mode-redis-cluster) (push) Blocked by required conditions
CI/CD / tests-worker (node24, pg12, mode) (push) Blocked by required conditions
CI/CD / tests-worker (node24, pg15, mode) (push) Blocked by required conditions
CI/CD / tests-worker (node24, pg12, mode-azure) (push) Blocked by required conditions
CI/CD / tests-worker (node24, pg15, mode-azure) (push) Blocked by required conditions
CI/CD / tests-worker (node24, pg12, mode-redis-cluster) (push) Blocked by required conditions
CI/CD / tests-worker (node24, pg15, mode-redis-cluster) (push) Blocked by required conditions
CI/CD / test-worker-llm-connections (node24, pg15) (push) Blocked by required conditions
CI/CD / e2e-tests (push) Blocked by required conditions
CI/CD / e2e-server-tests (push) Blocked by required conditions
CI/CD / all-ci-passed (push) Blocked by required conditions
CI/CD / push-docker-image (push) Blocked by required conditions
release.yml / release (push) Waiting to run
2025-10-10 20:34:44 +02:00
NimarandGitHub b49c152d85 fix(jump-to-playground): fix openai mapping with tool calls (#9664)
* add obs name as mapping indicator

* fix mapper oai

* lint

* simplify
2025-10-10 18:20:30 +00:00
Max DeichmannandGitHub d07cb39ef6 chore: restrict time series lookback for tables (#9665) 2025-10-10 17:43:24 +00:00
froemic 0e59668ad9 chore: release v3.117.1
Codespell / Check for spelling errors (push) Waiting to run
CI/CD / pre-job (push) Waiting to run
CI/CD / lint (push) Blocked by required conditions
CI/CD / prettier-check (push) Blocked by required conditions
CI/CD / test-docker-build (push) Blocked by required conditions
CI/CD / tests-web-sync (node24, pg12) (push) Blocked by required conditions
CI/CD / tests-web-sync (node24, pg15) (push) Blocked by required conditions
CI/CD / tests-web-async (node24, pg12, mode) (push) Blocked by required conditions
CI/CD / tests-web-async (node24, pg15, mode) (push) Blocked by required conditions
CI/CD / tests-web-async (node24, pg12, mode-azure) (push) Blocked by required conditions
CI/CD / tests-web-async (node24, pg15, mode-azure) (push) Blocked by required conditions
CI/CD / tests-web-async (node24, pg12, mode-redis-cluster) (push) Blocked by required conditions
CI/CD / tests-web-async (node24, pg15, mode-redis-cluster) (push) Blocked by required conditions
CI/CD / tests-worker (node24, pg12, mode) (push) Blocked by required conditions
CI/CD / tests-worker (node24, pg15, mode) (push) Blocked by required conditions
CI/CD / tests-worker (node24, pg12, mode-azure) (push) Blocked by required conditions
CI/CD / tests-worker (node24, pg15, mode-azure) (push) Blocked by required conditions
CI/CD / tests-worker (node24, pg12, mode-redis-cluster) (push) Blocked by required conditions
CI/CD / tests-worker (node24, pg15, mode-redis-cluster) (push) Blocked by required conditions
CI/CD / test-worker-llm-connections (node24, pg15) (push) Blocked by required conditions
CI/CD / e2e-tests (push) Blocked by required conditions
CI/CD / e2e-server-tests (push) Blocked by required conditions
CI/CD / all-ci-passed (push) Blocked by required conditions
CI/CD / push-docker-image (push) Blocked by required conditions
release.yml / release (push) Waiting to run
2025-10-10 17:21:09 +02:00
75c7d0e448 fix(billing): Update schema validation for existing redis api keys (#9662)
* feat: Allow isIngestionSuspended to be nullish

Co-authored-by: michael <michael@langfuse.com>

* test: Use expect.anything() for isIngestionSuspended

Co-authored-by: michael <michael@langfuse.com>

---------

Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: michael <michael@langfuse.com>
2025-10-10 15:03:02 +00:00
NimarandGitHub 094599a424 fix(support): delimit user messages more (#9655) 2025-10-10 12:29:45 +00:00
froemic eca06ca08e chore: release v3.117.0
Codespell / Check for spelling errors (push) Waiting to run
CI/CD / pre-job (push) Waiting to run
CI/CD / lint (push) Blocked by required conditions
CI/CD / prettier-check (push) Blocked by required conditions
CI/CD / test-docker-build (push) Blocked by required conditions
CI/CD / tests-web-sync (node24, pg12) (push) Blocked by required conditions
CI/CD / tests-web-sync (node24, pg15) (push) Blocked by required conditions
CI/CD / tests-web-async (node24, pg12, mode) (push) Blocked by required conditions
CI/CD / tests-web-async (node24, pg15, mode) (push) Blocked by required conditions
CI/CD / tests-web-async (node24, pg12, mode-azure) (push) Blocked by required conditions
CI/CD / tests-web-async (node24, pg15, mode-azure) (push) Blocked by required conditions
CI/CD / tests-web-async (node24, pg12, mode-redis-cluster) (push) Blocked by required conditions
CI/CD / tests-web-async (node24, pg15, mode-redis-cluster) (push) Blocked by required conditions
CI/CD / tests-worker (node24, pg12, mode) (push) Blocked by required conditions
CI/CD / tests-worker (node24, pg15, mode) (push) Blocked by required conditions
CI/CD / tests-worker (node24, pg12, mode-azure) (push) Blocked by required conditions
CI/CD / tests-worker (node24, pg15, mode-azure) (push) Blocked by required conditions
CI/CD / tests-worker (node24, pg12, mode-redis-cluster) (push) Blocked by required conditions
CI/CD / tests-worker (node24, pg15, mode-redis-cluster) (push) Blocked by required conditions
CI/CD / test-worker-llm-connections (node24, pg15) (push) Blocked by required conditions
CI/CD / e2e-tests (push) Blocked by required conditions
CI/CD / e2e-server-tests (push) Blocked by required conditions
CI/CD / all-ci-passed (push) Blocked by required conditions
CI/CD / push-docker-image (push) Blocked by required conditions
release.yml / release (push) Waiting to run
2025-10-10 14:16:37 +02:00
Michael FröhlichGitHubellipsis-dev[bot] <65095814+ellipsis-dev[bot]@users.noreply.github.com>
13295f997c chore(billing): add information banner about billing changes to billing settings (#9640)
* add banner to billing settings

* Update web/src/ee/features/billing/components/BillingTransitionInfoCard.tsx

Co-authored-by: ellipsis-dev[bot] <65095814+ellipsis-dev[bot]@users.noreply.github.com>

* update text

---------

Co-authored-by: ellipsis-dev[bot] <65095814+ellipsis-dev[bot]@users.noreply.github.com>
2025-10-10 08:45:42 +00:00
a9048a64a1 feat(auth): add account settings with self-serve account deletion and display name update (#9615)
* add implementation

* add cmd+k shortcut

* use transaction wrapper when deleting user

* Update web/src/server/api/routers/userAccount.ts

Co-authored-by: Steffen Schmitz <steffen@langfuse.com>

* add review comments

---------

Co-authored-by: Steffen Schmitz <steffen@langfuse.com>
2025-10-10 08:34:07 +00:00
AneeshandGitHub fff0734af3 feat: make more env variables in docker-compose files overwritable (#8705) 2025-10-10 10:36:52 +02:00
Valery MeleshkinandGitHub 7dd6f31edb chore: renaming a helper function to match time units (#9652) 2025-10-10 08:28:05 +00:00
Valery MeleshkinandGitHub c32da046de chore: remove Experimental Traces AMT Table Configuration. (#9607)
I am leaving measureAndReturn in place because it emits a lot of useful
tracing metadata and it remains a good place to conduct experiments in
the future.
2025-10-10 06:44:57 +00:00
bd704157ed fix(ui): set default row height to medium for dataset items table (#9642)
Refactor: Set default row height to medium

Co-authored-by: Cursor Agent <cursoragent@cursor.com>
2025-10-09 18:16:03 +00:00
+3 2eca294a67 chore: refactor chatml mapper v1 (#9304)
* chore: extract chatmlmappers

* fix da lint

* extract more

* init mappers with registry

* simplify

* chore: enable browser logs in terminal (#8935)

* fix(billing): Fix invoice table decimal display (#9399)

Fix: Ensure consistent USD formatting in billing table

Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: michael <michael@langfuse.com>

* fix(prompts): don't throw on prompt deletion message (#9387)

* vibe

* cleanup

* chore: filter browser extensions from sentry errors (#9405)

* chore: do not upgrade to postgres 18 (#9407)

* chore: do not upgrade to postgrs 18

* chore: do not upgrade to postgrs 18

* chore: do not upgrade to postgrs 18

* feat(playground): add support for claude sonnet 4.5 (#9410)

* feat(playground): add support for claude sonnet 4.5

* fix id

* cuid2

* upgrade langchain

* chore: release v3.113.0

* style(dataset-item): detail view to show item IO and past runs in two tabs (#9419)

* fix: skip JSON parsing for completion start time strings (#9418)

* fix(otel): preferential parsing of environment key from span attributes (#9432)

* feat(auth): add support for Keycloak scope and ID token configuration (#9359)

* feat(auth): add support for Keycloak scope and ID token configuration

* fix(auth): update ID token envvar handling to use default value

* feat(trace-ui): natural language filters (#9347)

* add fetchCompletion route

* use fetchLLMCompletion function

* chore(llm): add execution context to fetchLLMCompletion function

* add router

* chore: refactor to add resolveBedrockCredentials

* feat(env): add optional AWS Bedrock configuration

* chore: revert langfuse execution context for fetch llm completion

* frontend poc

* use bedrock keys

* chore(organization): add aiFeaturesEnabled column

* feat(organization): update organization update procedure to include optional aiFeaturesEnabled

* add route integrated

* add env vars

* update

* add proejct id

* fixup: ai feature toggle

* fully integrated

* fix: organization ai feature settings

* update

* fix ts

* chore: remove env variables from shared

* chore: rename environment variables

* chore: enhance LLM completion context

* chore: fix typing

* typing

* no more token delegate

* add project id

* fix: filter completion should never throw an error, fallback to empty json

* chore: set up bedrock credentials for development

* chore: remove TokenCountDelegate from types

* fix: integrate organization AI feature checks in filter builder

* fix: use project hook over org hook

* send traces to langfuse cloud

* fix build

* fix buuild

* validate filters

* chore: eslint

* add debug

* don't greedy match

* add tooltip

* fix parsing

* update AI feature text

* fix lockfile

* chore(router): gate setting/using ai features on self hosted deployments

* chore: replace env variable usage with useLangfuseCloudRegion hook across components

* chore: integrate useLangfuseCloudRegion hook to conditionally render AI features in filter builder and AI feature switch components

* docs: update REVIEW.md with Langfuse Cloud environment usage guidelines

* link

* chore: add LANGFUSE_AWS_BEDROCK_MODEL to .env.dev.example and update its definition in env.mjs

* Update useFilterState.ts

---------

Co-authored-by: Marlies Mayerhofer <74332854+marliessophie@users.noreply.github.com>
Co-authored-by: Leo <5489276+leoweigand@users.noreply.github.com>

* chore: upgrade turborepo to 2.5.8 (#9337)

* chore: upgrade turborepo to 2.5.8

* add comments

* comments

* fix for turbo plugin default exports

* fix: Accept any 2xx status for webhooks (#9439)

feat: Accept 2xx status codes for webhook success

Co-authored-by: Cursor Agent <cursoragent@cursor.com>

* test(llm-connections): add tests for all adapters (#9423)

* test(llm-connections): add tests for all adapters

* test(llm-connections): add tests for all adapters

* fix

* fix

* push

* push

* fix: add current datetime to ai filter prompt (#9445)

* Add current datetime to AI prompt context

Co-authored-by: marc <marc@langfuse.com>

* Refactor datetime formatting for AI prompt

Co-authored-by: marc <marc@langfuse.com>

---------

Co-authored-by: Cursor Agent <cursoragent@cursor.com>

* chore: upgrade golang migrate 4.19.0 (#9444)

* chore: do not upgrade to postgrs 18

* chore: do not upgrade to postgrs 18

* chore: do not upgrade to postgrs 18

* chore: upgrade golang-migrate

* fix(trace-ui): make row label sticky for long cells (#9454)

* feat(trace-ui): make row label sticky for long cells

* simplify

* fix(otel): completion start time parsing to handle JSON stringified values (#9455)

* chore: release v3.114.0

* fix: change tRPC error code for ClickHouse errors (#9459)

* feat(prompt-experiments): allow for structured output (#9437)

* feat(prompt-experiments): allow for structured output

* push

* remove propagation from submit in createoreditllmschema

* push

* add output_schema to trace metadata

* push

* push

* push

* push

* perf: add output score index to job_executions table (#9434)

* fix(llm-schema-dialog): remove unnecessary event handler (#9463)

* feat(prompt-experiments): allow for structured output

* push

* remove propagation from submit in createoreditllmschema

* push

* add output_schema to trace metadata

* push

* push

* push

* push

* fix(create-llm-schema-dialog): remove unnecessary event handling

* push

* chore: clear up unused entities in clickhouse (#9151)

* chore: clear up unused entities in clickhouse

* chore: patch

* chore: patches

* chore: remove AMT documentation and background migration (#9465)

* fix(natural-language-filters): show only on traces table (#9466)

* fix: show input/output/total columns in traces table correctly (#9468)

* fix: show input/output/total columns in traces table correctly

* fix: show input/output/total columns in traces table correctly

* fix: show input/output/total columns in traces table correctly

* chore(llm-connections): move test suite to separate run (#9464)

* chore(llm-connections): move test suite to separate run

* push

* push

* push

* fix: set postgres timezone to UTC across docker compose files (dev and prod) (#9472)

* test(otel): add test for completion start time parsing (#9456)

* test(otel): add test for completion start time parsing

* push

* chore(billing): require credit card only on non-zero checkout sessions (#9467)

* handle upgrades w/o credit card

* fix flickering of error message

* fix flag

* style(dataset-compare-ui): move compare view charts to tabs (#9475)

* refactor: extract ChatML Mapper from IOPreview (#9202)

* chore: extract chatmlmappers

* fix da lint

* extract more

* fix(filters): trace with local export (#9471)

* fix(filters): trace with local export

* fix link

* add real seeder

* track prompt stats

* fix(billing): fix react rule of hooks violation (#9477)

* Refactor conditional rendering and hook logic

Co-authored-by: michael <michael@langfuse.com>

* fix ordering

---------

Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: michael <michael@langfuse.com>

* chore: release v3.115.0

* switch to scoring

* switch to score

* add langchain

* playground integration

* reset instrumentation-client

* use metadata and zod

* add pydantic

* fix build

* more fix

* simplify

* cleanup

* cleanup

* remove previous functions

* fix types

* simplify

* add llamaindex

* memorize

* fix: don't try to render content parts as chatml

* fix: don't show item badges unless activated

* add seeder stuff

* shhhh

---------

Co-authored-by: Michael Fröhlich <15179255+FroeMic@users.noreply.github.com>
Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: michael <michael@langfuse.com>
Co-authored-by: Max Deichmann <m.deichmann@tum.de>
Co-authored-by: marliessophie <74332854+marliessophie@users.noreply.github.com>
Co-authored-by: Steffen Schmitz <steffen@langfuse.com>
Co-authored-by: Hassieb Pakzad <68423100+hassiebp@users.noreply.github.com>
Co-authored-by: Fabian Reinold <32450519+freinold@users.noreply.github.com>
Co-authored-by: Leo <5489276+leoweigand@users.noreply.github.com>
Co-authored-by: Marc Klingen <git@marcklingen.com>
Co-authored-by: Valery Meleshkin <valeriy@meleshk.in>
2025-10-09 16:32:55 +00:00
Michael FröhlichandGitHub e8efbb5e8f chore(billing): remove usage-tracker component (#9641)
remove usage-tracker component
2025-10-09 16:05:29 +00:00
Michael FröhlichandGitHub 3f2e65ce61 fix(billing): exclude orgs with plan override from usage notifications (#9637)
fix erroneous warning emails
2025-10-09 14:05:12 +00:00
Max DeichmannandGitHub 218db7a5d7 chore: add offset for sessionid (#9634) 2025-10-09 12:34:47 +00:00
NimarandGitHub 69c6a74110 fix(log-view): increase limit to 150 obs (#9633) 2025-10-09 14:12:38 +02:00
marliessophieandGitHub 66f55e94fe docs(evals): add note to hisotric data eval execution tooltip (#9632) 2025-10-09 11:23:55 +00:00
0becc56477 chore: bump next (15.5.4), react (19.2), sentry (10.18) (#9612)
* chore: bump next, react, sentry

* add lock

* fix(playground): replace console warnings with toast notifications in usePersistedWindowIds hook (#9611)

* feat(onboarding): add onboarding for dataset item creation management (#9609)

* feat(onboarding): add DatasetItemsOnboarding component for dataset item management

- Introduced a new onboarding component to facilitate the addition of items to datasets, including options for CSV uploads and manual entry.
- Updated SplashScreen to accept children for enhanced flexibility.
- Modified DatasetItemsTable and related components to conditionally render onboarding based on dataset item count.

* chore: references

* feat(playground): scroll horizontally as windows are added (#9613)

feat(playground): scroll horizontally as windows are added/removed

* chore(support): add delimiter line between boilerplate and user message (#9616)

Update plainRouter.ts

Update delimiter line

* chore(auth): improved org invite flow (nitpick) (#9617)

* add implementation

* sanitize targetpath query param

* chore: limit trace filter options (#9622)

* chore: limit trace filter options

* chore: limit trace filter options

* fix upgrade

* fix shared pkg

---------

Co-authored-by: marliessophie <74332854+marliessophie@users.noreply.github.com>
Co-authored-by: Michael Fröhlich <15179255+FroeMic@users.noreply.github.com>
Co-authored-by: Max Deichmann <m.deichmann@tum.de>
2025-10-09 09:11:02 +00:00
Michael FröhlichandGitHub 6a6bbf7524 chore(billing): disable ingestion suspension enforcement (#9623)
disable enforcement of blocking for now
2025-10-09 11:02:53 +02:00
Hassieb PakzadandGitHub a369cf0941 feat(prompt-experiments): name runs experiments (#9540)
* feat(prompt-experiments): name runs experiments

* push

* push

* push

* push

* push

* add multistep form

* push

* refactor to context

* push

* push

* push

* push

* push

* push

* push

* push

* push

* rename to experiment

* onevalutaortoggled

* move to groupedProps instead of context

* factor out StepHeader component

* rely on form state for runName

* push
2025-10-09 08:28:03 +00:00
froemic cf4ed6bb2e chore: release v3.116.1
Codespell / Check for spelling errors (push) Waiting to run
CI/CD / pre-job (push) Waiting to run
CI/CD / lint (push) Blocked by required conditions
CI/CD / prettier-check (push) Blocked by required conditions
CI/CD / test-docker-build (push) Blocked by required conditions
CI/CD / tests-web-sync (node24, pg12) (push) Blocked by required conditions
CI/CD / tests-web-sync (node24, pg15) (push) Blocked by required conditions
CI/CD / tests-web-async (node24, pg12, mode) (push) Blocked by required conditions
CI/CD / tests-web-async (node24, pg15, mode) (push) Blocked by required conditions
CI/CD / tests-web-async (node24, pg12, mode-azure) (push) Blocked by required conditions
CI/CD / tests-web-async (node24, pg15, mode-azure) (push) Blocked by required conditions
CI/CD / tests-web-async (node24, pg12, mode-redis-cluster) (push) Blocked by required conditions
CI/CD / tests-web-async (node24, pg15, mode-redis-cluster) (push) Blocked by required conditions
CI/CD / tests-worker (node24, pg12, mode) (push) Blocked by required conditions
CI/CD / tests-worker (node24, pg15, mode) (push) Blocked by required conditions
CI/CD / tests-worker (node24, pg12, mode-azure) (push) Blocked by required conditions
CI/CD / tests-worker (node24, pg15, mode-azure) (push) Blocked by required conditions
CI/CD / tests-worker (node24, pg12, mode-redis-cluster) (push) Blocked by required conditions
CI/CD / tests-worker (node24, pg15, mode-redis-cluster) (push) Blocked by required conditions
CI/CD / test-worker-llm-connections (node24, pg15) (push) Blocked by required conditions
CI/CD / e2e-tests (push) Blocked by required conditions
CI/CD / e2e-server-tests (push) Blocked by required conditions
CI/CD / all-ci-passed (push) Blocked by required conditions
CI/CD / push-docker-image (push) Blocked by required conditions
release.yml / release (push) Waiting to run
2025-10-09 10:39:32 +02:00
Michael FröhlichandGitHub be00006ae2 chore(billing): read cycle usage from db instead of clickhouse for hobby plan users (#9619)
* read usage in billing settings from db; align with email notifications

* change line
2025-10-09 08:05:07 +00:00
Max DeichmannandGitHub b88a41443a chore: limit trace filter options (#9622)
* chore: limit trace filter options

* chore: limit trace filter options
2025-10-09 07:35:54 +00:00
Michael FröhlichandGitHub 9843c79f57 chore(auth): improved org invite flow (nitpick) (#9617)
* add implementation

* sanitize targetpath query param
2025-10-08 20:46:58 +00:00
Michael FröhlichandGitHub 8fcac23083 chore(support): add delimiter line between boilerplate and user message (#9616)
Update plainRouter.ts

Update delimiter line
2025-10-08 19:56:36 +00:00
marliessophieandGitHub b171db153e feat(playground): scroll horizontally as windows are added (#9613)
feat(playground): scroll horizontally as windows are added/removed
2025-10-08 18:05:54 +00:00
marliessophieandGitHub fba07bdff0 feat(onboarding): add onboarding for dataset item creation management (#9609)
* feat(onboarding): add DatasetItemsOnboarding component for dataset item management

- Introduced a new onboarding component to facilitate the addition of items to datasets, including options for CSV uploads and manual entry.
- Updated SplashScreen to accept children for enhanced flexibility.
- Modified DatasetItemsTable and related components to conditionally render onboarding based on dataset item count.

* chore: references
2025-10-08 17:55:17 +00:00
marliessophieandGitHub ee7473855c fix(playground): replace console warnings with toast notifications in usePersistedWindowIds hook (#9611) 2025-10-08 17:51:57 +00:00
NimarandGitHub 53b90fed02 fix(trace-log): show json / formatted switch (#9601)
* fix(trace-log): show json / formatted switch

* fix
2025-10-08 17:22:30 +00:00
Leo WeigandandGitHub 0ed2c53b6c chore(playground): make action icon buttons clearer (#9605) 2025-10-08 15:17:34 +00:00
Marc KlingenandGitHub 2b6420b78d feat(ui): add cloudregion to project/org information in settings (#9600)
* feat(ui): add cloudregion to project/org information in settings

* push
2025-10-08 14:16:35 +00:00
5e5f198aae Bug fix: Self-hosted hitting cloudBilling.getUsage (#9597)
fix: guard cloud billing usage tracker

Co-authored-by: Michael Fröhlich <15179255+FroeMic@users.noreply.github.com>
2025-10-08 16:07:56 +02:00
NimarandGitHub 01e4c57a55 fix(trace-ui): keep expand/collapse state across trace peeks (#9598) 2025-10-08 15:44:44 +02:00
Steffen SchmitzandGitHub 1d7c0ae9ab chore: disable write of eventRaw column in new events table (#9595) 2025-10-08 12:03:09 +00:00
marliessophieandGitHub 25d428dc93 feat(datasets): support DnD for CSV file uploads (#9593) 2025-10-08 10:10:06 +00:00
aedee34611 fix(billing): fix self-hosted billing error (#9578)
* fix(billing): fix self-hosted billing error

Fixes 500 errors for self-hosted users when cloudBilling routes are called without NEXT_PUBLIC_LANGFUSE_CLOUD_REGION configured.

## Changes

### Server-Side Protection
- Added `isCloudBillingEnabled()` utility to check cloud region configuration
- Updated all cloudBillingRouter endpoints to gracefully handle non-cloud environments:
  - Query endpoints return null/empty responses
  - Mutation endpoints throw PRECONDITION_FAILED errors
  - Removed DEV-specific check in getUsage, replaced with universal cloud check

### Client-Side Prevention
- Added `useIsCloudBillingAvailable()` hook for components
- Updated UsageTracker to check cloud availability before rendering/querying
- Updated BillingSettings to return null when cloud billing unavailable
- Updated BillingInvoiceTable to include cloud availability check
- Updated organization settings to hide billing tab when not in cloud region

### Defense in Depth
Implements three layers of protection:
1. Server router checks prevent 500 errors
2. Client components conditionally render
3. Settings UI hides billing features appropriately

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>

* fix lint errors

* conditionally render usage tracker

---------

Co-authored-by: Claude <noreply@anthropic.com>
2025-10-08 09:55:20 +00:00
Leo WeigandandGitHub 72d7a99ff2 revert: filter sidebar (#9592)
* Revert "fix: score filter UI issues (#9583)"

This reverts commit 09f709627a.

* Revert "fix: restore missing sessions view filters (#9577)"

This reverts commit 3290a3381e.

* Revert "fix: empty trace names breaking filter options (#9575)"

This reverts commit e0568b0c5e.

* Revert "feat: introduce sidebar filtering UI (#9561)"

This reverts commit 689dc16307.
2025-10-08 09:53:51 +00:00
8cb3d26e54 feat(billing): add configurable spend alerts for paid cloud plans (#9551)
* Checkpoint before follow-up message

Co-authored-by: michael <michael@langfuse.com>

* fix(spend-alerts): format code and fix toast imports

* feat(spend-alerts): add test script for spend alert emails

- Follows same pattern as send-test-threshold-emails.ts
- Includes 3 test scenarios with different thresholds
- Requires manual email configuration for safety

* feat(spend-alerts): add Prisma migration for CloudSpendAlert table

- Creates cloud_spend_alerts table with proper schema
- Adds foreign key constraint to organizations table
- Includes index on org_id for performance
- Supports decimal thresholds and trigger tracking

* add concurrently keyword to index

* fix linter error

* fix linter error

* remove old usage alerts

* update syling

* refatcor alerts jobs

* fix build errors

* update rate limit for stripe

* fix migration

* update email template and tracking

* update text

* add review comments

* add queue body deifniton

---------

Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: michael <michael@langfuse.com>
2025-10-08 08:55:15 +00:00
Valery MeleshkinandGitHub dee644d8cb feat: Enable advanced filtering in public API traces endpoint (LFE-3810) (#9492)
* feat: allow public traces API to use advanced filters

* chore: update API specs
2025-10-08 08:18:24 +00:00
Max DeichmannandGitHub d3388aac23 chore: add filter options for sessions and remove traces final (#9589)
* chore: add filter options for users

* chore: add filter options for users
2025-10-07 22:35:14 +00:00
Leo WeigandandGitHub 09f709627a fix: score filter UI issues (#9583) 2025-10-07 20:14:22 +02:00
69c730f1a0 feat(otel): map gen_ai.tool.call.arguments/result to input/output (#9430)
Map gen_ai.tool.call.arguments/result to input/output

Co-authored-by: Nimar <l.nimar.b@gmail.com>
2025-10-07 20:12:37 +02:00
NimarandGitHub 33cfd9b4f9 fix(json-view): expand to up to 100 rows on root (#9580) 2025-10-07 17:29:59 +00:00
Leo WeigandandGitHub 3290a3381e fix: restore missing sessions view filters (#9577)
* fix: env and trace tags filter missing in sessions view

* fix: restore missing sessions view filters

* fix: bookmarked filter in sessions view

* fix: trace tags filter

* fix: add back missing scores filters
2025-10-07 17:12:06 +00:00
Steffen SchmitzandGitHub cf40ce5093 chore: ingest otel events into new events table (dev) (#9548)
* chore: ingest otel events into new events table (dev)

* chore: propagate completionStartTime

* chore: add metadata processing

* chore: propagate metadata

* chore: add source attributes

* chore: simplify

* chore: adjust modelname prop
2025-10-07 16:53:03 +00:00
NimarandGitHub 9b53418780 fix(graph): cause freeze through infinite loop (#9576) 2025-10-07 18:42:00 +02:00
Leo WeigandandGitHub e0568b0c5e fix: empty trace names breaking filter options (#9575)
fix: empty trace name breaking filter options
2025-10-07 15:20:12 +00:00
Leo WeigandandGitHub d03ff3d6a9 fix: peek view params handling in sessions (#9570) 2025-10-07 15:47:50 +02:00
Leo WeigandandGitHub 689dc16307 feat: introduce sidebar filtering UI (#9561)
* shuffle toolbar

* controls sidebar

* setup for filter attributes

* test with environment filter

* new filter state management

* name filter

* add reset filter button

* add tags filter

* add bookmarked filter

* unify column id v display handling

* rename to bookmarked

* polish, fix bugs

* merge hooks, efficiency

* support dual-value in slider

* latency filter

* make filters generic

* add sidebar to observations, sessions, prompts, scores and evals

* simplify table-controls component

* allow resetting individual filters

* clean up layout

* show "all" when only selected item

* text facets

* add key-value filter

* add numerical key-value filter

* add metadata filter

* disable accordion animations

* add filtering for values

* fix observation filters not being applied

* add ai filters

* represent no range filter with empty inputs

* update look of reset button

* fix header shrinking

* tidy up vertical spacing

* clean up

* fix type and lint issues

* fix none-of operator not being used when it should

* fix missing env filter

* a few last fixes

* another type error

* oops
2025-10-07 13:28:37 +00:00
Steffen SchmitzandGitHub 32e6026f24 feat: process livekit semantic attributes in otel parsing (#9568) 2025-10-07 13:02:33 +00:00
NimarandGitHub d9460e684d chore: filter out http errors from sentry (#9569) 2025-10-07 12:58:56 +00:00
NimarandGitHub 30726d6150 feat(trace-ui): add log view with all observations concatenated (#9563)
* feat(trace-ui): add log view with all observations concatenated

* collapse nicer

* move up

* fix types
2025-10-07 10:18:07 +00:00
Hassieb PakzadandGitHub 28adf5b129 fix(jump-to-playground): allow for nullish tool_call_id and tool_calls (#9564) 2025-10-07 10:01:31 +00:00
Marc KlingenandGitHub dae07f5c93 fix(ui): "New Version" instead of "New" on main prompt mgmt CTA button (#9562) 2025-10-07 11:37:38 +02:00
Valery MeleshkinandGitHub 6a18e6615c chore: use simpler to install CH client + doc update (#9560)
chore: use simplier to install CH client + doc update.
2025-10-07 11:11:26 +02:00
Hassieb PakzadandGitHub 07c125bcc1 feat(model-prices): add gpt-5-pro (#9559) 2025-10-07 08:24:51 +00:00
446fbe484c fix(billing): Multiple queue job execution anomaly (#9553)
* Fix: Prevent duplicate job scheduling in queues

Co-authored-by: michael <michael@langfuse.com>

* Fix: Deduplicate queue jobs across multiple worker instances

Co-authored-by: michael <michael@langfuse.com>

* Checkpoint before follow-up message

Co-authored-by: michael <michael@langfuse.com>

* Checkpoint before follow-up message

Co-authored-by: michael <michael@langfuse.com>

* fix: prevent duplicate queue job scheduling across multiple containers

- Add unique jobIds to CloudFreeTierUsageThresholdQueue for deduplication
- Add comprehensive logging for job scheduling and execution tracking
- Apply best practices pattern with descriptive job data and comments
- Remove investigation documentation files

This resolves the issue where multiple worker containers were creating
duplicate recurring and bootstrap jobs, causing 10x more executions
than expected.

* add logging statements

* remove job id and bootstrap execution

---------

Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: michael <michael@langfuse.com>
2025-10-07 09:08:52 +02:00
Max Deichmann 77733d7774 chore: release v3.116.0
Codespell / Check for spelling errors (push) Waiting to run
CI/CD / pre-job (push) Waiting to run
CI/CD / lint (push) Blocked by required conditions
CI/CD / prettier-check (push) Blocked by required conditions
CI/CD / test-docker-build (push) Blocked by required conditions
CI/CD / tests-web-sync (node24, pg12) (push) Blocked by required conditions
CI/CD / tests-web-sync (node24, pg15) (push) Blocked by required conditions
CI/CD / tests-web-async (node24, pg12, mode) (push) Blocked by required conditions
CI/CD / tests-web-async (node24, pg15, mode) (push) Blocked by required conditions
CI/CD / tests-web-async (node24, pg12, mode-azure) (push) Blocked by required conditions
CI/CD / tests-web-async (node24, pg15, mode-azure) (push) Blocked by required conditions
CI/CD / tests-web-async (node24, pg12, mode-redis-cluster) (push) Blocked by required conditions
CI/CD / tests-web-async (node24, pg15, mode-redis-cluster) (push) Blocked by required conditions
CI/CD / tests-worker (node24, pg12, mode) (push) Blocked by required conditions
CI/CD / tests-worker (node24, pg15, mode) (push) Blocked by required conditions
CI/CD / tests-worker (node24, pg12, mode-azure) (push) Blocked by required conditions
CI/CD / tests-worker (node24, pg15, mode-azure) (push) Blocked by required conditions
CI/CD / tests-worker (node24, pg12, mode-redis-cluster) (push) Blocked by required conditions
CI/CD / tests-worker (node24, pg15, mode-redis-cluster) (push) Blocked by required conditions
CI/CD / test-worker-llm-connections (node24, pg15) (push) Blocked by required conditions
CI/CD / e2e-tests (push) Blocked by required conditions
CI/CD / e2e-server-tests (push) Blocked by required conditions
CI/CD / all-ci-passed (push) Blocked by required conditions
CI/CD / push-docker-image (push) Blocked by required conditions
release.yml / release (push) Waiting to run
2025-10-06 23:59:52 +02:00
Max DeichmannandGitHub 7119201dcf chore: add logs to csv exports (#9555) 2025-10-06 21:57:54 +00:00
Michael FröhlichGitHubClaudeellipsis-dev[bot] <65095814+ellipsis-dev[bot]@users.noreply.github.com>
1354e0937f fix(billing): implement chunked updates for free tier usage tracking (#9549)
* fix(billing): implement chunked updates for free tier usage tracking

Reduces DB load by 95% via transaction batching (50,000 → 50 chunks).
Each chunk processes 1,000 orgs with proper error handling.
Failed chunks reported to Datadog without killing the job.

Changes:
- Refactored processThresholds() to return update data instead of executing immediately
- Created bulkUpdates.ts with chunked transaction processing (1000 orgs per batch)
- Modified usageAggregation.ts to collect updates and execute in bulk
- Updated tests to verify returned data instead of mock calls
- Added error handling with traceException for failed chunks
- Structured for easy swap to raw SQL (Option 1) if needed

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>

* test: update cache invalidation tests to use bulkUpdateOrganizations

The tests now call bulkUpdateOrganizations() to complete the update flow,
including cache invalidation. This reflects the refactored architecture where
processThresholds() returns update data and bulkUpdateOrganizations() executes it.

* fix: reduce transaction timeout from 60s to 15s per chunk

60 seconds was excessive for 1000 orgs. Even at 10ms per update,
that's only 10 seconds. 15 seconds provides a reasonable buffer.

* refactor: use Promise.allSettled instead of transaction wrapper

Benefits over previous () approach:
- Better resilience: One failed org doesn't fail the entire 1000-org chunk
- Concurrent execution: Much faster than sequential transaction
- Granular error tracking: Track exactly which orgs failed
- Better error handling: Each org failure reported to Datadog individually

Trade-off: No atomicity per chunk, but we don't need it for this use case.
Each org update is independent and idempotent.

* fix: remove unused chunkOrgIds variable

* remove unused code

* refactor transaction update and add rawsql update

* Update worker/src/ee/usageThresholds/bulkUpdates.ts

Co-authored-by: ellipsis-dev[bot] <65095814+ellipsis-dev[bot]@users.noreply.github.com>

* Update worker/src/ee/usageThresholds/bulkUpdates.ts

Co-authored-by: ellipsis-dev[bot] <65095814+ellipsis-dev[bot]@users.noreply.github.com>

* make rawsql query default

---------

Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: ellipsis-dev[bot] <65095814+ellipsis-dev[bot]@users.noreply.github.com>
2025-10-06 17:46:46 +00:00
a2da9a51b3 fix(billing): enhance free tier usage emails with pricing, reset dates, and CRM integration (#9547)
* fix(billing): enhance free tier usage emails with pricing, reset dates, and CRM integration

- Add getBillingCycleEnd() helper to calculate when usage limits reset
- Add comprehensive tests for billing cycle end date calculations
- Add optional USAGE_THRESHOLD_EMAIL_BCC env variable for CRM integration (e.g., HubSpot)
- Include reset date in both warning and suspension emails
- Add Core plan pricing ($29/month) and key benefits to email templates
- Mention startup program (50% off for first year) with link to langfuse.com/startups
- Update email templates to include:
  - When usage limit resets
  - Pricing information from stripeCatalogue
  - Key upgrade benefits: unlimited users, 90-day retention, email/chat support
  - Startup program callout
- Update test script to include reset date and BCC configuration

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>

* refactor(billing): rename USAGE_THRESHOLD_EMAIL_BCC to CLOUD_CRM_EMAIL

Rename environment variable to better reflect its purpose as a general
cloud CRM integration endpoint rather than being specific to email BCC.

Changes:
- Renamed env variable in both .env.dev.example and .env.prod.example
- Updated email sending functions to use new variable name
- Updated test script with new variable name
- Regenerated TypeScript declarations

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>

* security(billing): add email validation for CLOUD_CRM_EMAIL

Add Zod email validation before using CLOUD_CRM_EMAIL in BCC field
to prevent potential email header injection attacks.

Changes:
- Import and use zod/v4 for email validation in both email functions
- Validate CLOUD_CRM_EMAIL format before assigning to BCC
- Log warning if invalid email format is detected
- Add CLOUD_CRM_EMAIL to worker env schema

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>

---------

Co-authored-by: Claude <noreply@anthropic.com>
2025-10-06 15:02:18 +00:00
3ce6c51009 fix(billing): correct metrics for paid vs free tier orgs when enforcement disabled (#9544)
When LANGFUSE_FREE_TIER_USAGE_THRESHOLD_ENFORCEMENT_ENABLED is false,
the paid plan check was never reached due to early return. This caused
ALL organizations (paid and free) to be incorrectly counted as free_tier_orgs.

Fix: Move paid plan check before enforcement check to ensure paid orgs
always return "PAID_PLAN" regardless of enforcement status.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-authored-by: Claude <noreply@anthropic.com>
2025-10-06 14:31:04 +00:00
Valery MeleshkinandGitHub f1564c3049 chore: add seeding to the events dev table (#9545) 2025-10-06 13:42:57 +00:00
marliessophieandGitHub f2c764c1d7 feat(dataset-compare-view-ui): make cell more digestable (#9491)
* chore: remove peek view from compare data table

* style(io-table-cell): support content expand on hover

* feat: make compare cell digestable

* feat: open trace peek view from compare view

* style: adjust cell for many scores, IO and metadata

* style: allow untoggling output

* chore: fix

* fix: enable score column query based on runIds length

* chore: rename dataset compare metrics to fields

* refactor: unify score aggregate types under AggregatedScoreData

* chore: extract score detail row component

* refactor: make openPeek optional in DataTablePeekViewProps and update related components
2025-10-06 11:47:35 +00:00
Steffen SchmitzandGitHub 71c7b30516 chore: create setup for dev-tables with new events table (#9535)
* chore: create setup for dev-tables with new events table

* chore: make tests independent of clickhouse version
2025-10-06 11:00:17 +00:00
Hassieb PakzadandGitHub e639e866e1 refactor(fetchLLMCompletion): simplify interface (#9493)
* refactor(fetchLLMCompletion): simplify interface

* fix llm connection tests

* fix lint

* fix ts errors

* fix

* fix
2025-10-06 10:57:25 +00:00
Michael FröhlichandGitHub 304f9999dd fix(billing): correctly name column in backfill-cycle-anchor background migration (#9538)
fix misnamed column in background script
2025-10-06 12:54:38 +02:00
Michael FröhlichGitHubMarc Klingenellipsis-dev[bot] <65095814+ellipsis-dev[bot]@users.noreply.github.com>
bee927e159 feat(billing): Enforce Usage Threshold on Hobby Plan (DRAFT) (#9426)
* feat(billing): Add migration for tracking usage on organization table (#9425)

Add db migration for tracking usage on hobby plan to organization table

* feat(billing): write org.billingAnchor on organization create and subscription create and delete (#9431)

* Add billingCycle helper functions with test cases

* update billingAnchor on organization create

* update billingAnchor subscription create / delete

* deduplicate startOfDayUTC definition

* feat(billing): Add clickhouse queries to retrieve usage (#9433)

add clickhouse queries

* feat(billing): add usage aggregation and threshold processing fns (#9438)

* export ParsedOrganization type

* add migration to use timestampz instead of datetime and add billingCycleUsageState column

* add endOfDayUTC helper

* update billingCycleHelpers tests

* add usageAggregation functionality

* add threshold processing scaffolding

* add tests for threshold processing

* fix formatting

* feat(billing): add usageThresholdQueue and handleUsageThresholdJob (#9441)

* add usaegThresholdQueue and handleUsageThresholdJob

* add usaegThresholdQueue and handleUsageThresholdJob

* fix build error

* feat(billing): add email templates for notifications  (#9447)

* add email templates

* trigger email notifications in threshold processing

* fix typo

* fix build error

* fix test

* feat(billing): add env variable to enable/disabled usage threshold enforcement (#9448)

add LANGFUSE_USAGE_THRESHOLD_ENFORCEMENT_ENABLED env variable

* feat(billing): enforce blocking of ingestion on isIngestionSuspended flag (#9450)

* block api ingestion on isIngestionSuspended

* fix failing tests

* add tests for api key invalidation

* extract invalidateAPIKey fns into shared package

* invlaidate api key in stripe webhook handler

* fix linter error

* avoid closing redis connection during test

* Update packages/shared/src/server/services/email/usageThresholdSuspension/sendUsageThresholdSuspensionEmail.ts

Co-authored-by: ellipsis-dev[bot] <65095814+ellipsis-dev[bot]@users.noreply.github.com>

* rename queue

* add comment to prisma

* add reply to to email and remove explicit support email

* remove getOrgCreateDataWIthAnchor wrapper

* clean up migrations

* remove manual setting of billingAnchorDate (let db take care)

* set ms specific billingAnchor

* fix docstring

* add functionality to backfill billingAnchor

* add datadog metrics

* add free-tier orgs gauge

* add review comments

* fix failing tests

* update migration to avoid blocking backfills

* add review comments; env variable renaming, test fixes, function renaming

* add guard to only run Queue in cloud environment

* add new queue to test-utils

* remove comment

* rename job, queue, and columns to 'cloud...'

* throw if NEXTAUTH_URL is not set when sending email

* refactor recordGauges to recordIncrement calls

* add explanatory comment

* refactor recordGauges to recordIncrement in cloudUsageMeteringJob

* refactor backfill to background job

* simplify job logic

* check background migration validatity based on cloud environment

* remove active subscription filter

* fix prettier

* feat(billling): add script to send test emails  (#9500)

* add script to send test emails

* add check and error message for empty email array

* add 200k notification threshold and increase blocking threshold to 250k

* replace id with uuid in background migration

* remove date from background migration file name

* check for columns to avoid race conditions

* fix failing test after updating thresholds

* update test thresholds

---------

Co-authored-by: Marc Klingen <git@marcklingen.com>
Co-authored-by: ellipsis-dev[bot] <65095814+ellipsis-dev[bot]@users.noreply.github.com>
2025-10-06 09:35:55 +00:00
NimarandGitHub 288b6d4ff2 fix(sentry): don't throw trpcclienterror in sentry (#9534) 2025-10-06 07:54:40 +00:00
Max DeichmannandGitHub a021f99e99 chore: upgrade sentry (#9528) 2025-10-05 15:38:27 +00:00
2c3362de5c chore: remove redundant AI feature sentence (#9497)
* Refactor AI feature switch description text

Co-authored-by: marc <marc@langfuse.com>

* prettier

---------

Co-authored-by: Cursor Agent <cursoragent@cursor.com>
2025-10-02 20:32:53 +00:00
c6199bc931 chore(ui): Rename table view button to "saved views" (#9496)
Refactor: Update default button text to "Saved Views"

Co-authored-by: Cursor Agent <cursoragent@cursor.com>
2025-10-02 19:37:36 +00:00
NimarandGitHub 07352dbf94 feat(agent-graph): cycle through observations by clicking graph nodes… (#9139)
* feat(agent-graph): cycle through observations by clicking graph nodes multiple times

* fix naming

* fix cycle

* invert numbering

* use dataset
2025-10-02 19:32:19 +00:00
3dd795b095 feat(billing): add self-service enterprise plan (#9486)
* feat: Add Enterprise plan to billing and update layout

Co-authored-by: michael <michael@langfuse.com>

* update product id

---------

Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: michael <michael@langfuse.com>
2025-10-02 10:20:42 +00:00
marliessophieandGitHub c519fe1737 style(io-table-cell): support content expand on hover (#9487) 2025-10-02 09:22:56 +00:00
Nimar 506d670625 chore: release v3.115.0
Codespell / Check for spelling errors (push) Waiting to run
CI/CD / pre-job (push) Waiting to run
CI/CD / lint (push) Blocked by required conditions
CI/CD / prettier-check (push) Blocked by required conditions
CI/CD / test-docker-build (push) Blocked by required conditions
CI/CD / tests-web-sync (node24, pg12) (push) Blocked by required conditions
CI/CD / tests-web-sync (node24, pg15) (push) Blocked by required conditions
CI/CD / tests-web-async (node24, pg12, mode) (push) Blocked by required conditions
CI/CD / tests-web-async (node24, pg15, mode) (push) Blocked by required conditions
CI/CD / tests-web-async (node24, pg12, mode-azure) (push) Blocked by required conditions
CI/CD / tests-web-async (node24, pg15, mode-azure) (push) Blocked by required conditions
CI/CD / tests-web-async (node24, pg12, mode-redis-cluster) (push) Blocked by required conditions
CI/CD / tests-web-async (node24, pg15, mode-redis-cluster) (push) Blocked by required conditions
CI/CD / tests-worker (node24, pg12, mode) (push) Blocked by required conditions
CI/CD / tests-worker (node24, pg15, mode) (push) Blocked by required conditions
CI/CD / tests-worker (node24, pg12, mode-azure) (push) Blocked by required conditions
CI/CD / tests-worker (node24, pg15, mode-azure) (push) Blocked by required conditions
CI/CD / tests-worker (node24, pg12, mode-redis-cluster) (push) Blocked by required conditions
CI/CD / tests-worker (node24, pg15, mode-redis-cluster) (push) Blocked by required conditions
CI/CD / test-worker-llm-connections (node24, pg15) (push) Blocked by required conditions
CI/CD / e2e-tests (push) Blocked by required conditions
CI/CD / e2e-server-tests (push) Blocked by required conditions
CI/CD / all-ci-passed (push) Blocked by required conditions
CI/CD / push-docker-image (push) Blocked by required conditions
release.yml / release (push) Waiting to run
2025-10-02 11:01:03 +02:00
4ee723d9ed fix(billing): fix react rule of hooks violation (#9477)
* Refactor conditional rendering and hook logic

Co-authored-by: michael <michael@langfuse.com>

* fix ordering

---------

Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: michael <michael@langfuse.com>
2025-10-01 20:54:58 +02:00
NimarandGitHub d3f35d1f22 fix(filters): trace with local export (#9471)
* fix(filters): trace with local export

* fix link

* add real seeder

* track prompt stats
2025-10-01 18:17:12 +00:00
NimarandGitHub 1a0a2b1b53 refactor: extract ChatML Mapper from IOPreview (#9202)
* chore: extract chatmlmappers

* fix da lint

* extract more
2025-10-01 18:06:16 +00:00
marliessophieandGitHub 5bb5b105c4 style(dataset-compare-ui): move compare view charts to tabs (#9475) 2025-10-01 17:39:31 +00:00
Michael FröhlichandGitHub 7377a313e5 chore(billing): require credit card only on non-zero checkout sessions (#9467)
* handle upgrades w/o credit card

* fix flickering of error message

* fix flag
2025-10-01 16:11:53 +00:00
Hassieb PakzadandGitHub a1e7867a35 test(otel): add test for completion start time parsing (#9456)
* test(otel): add test for completion start time parsing

* push
2025-10-01 16:00:28 +00:00
Marc KlingenandGitHub 6081187466 fix: set postgres timezone to UTC across docker compose files (dev and prod) (#9472) 2025-10-01 18:07:09 +02:00
Hassieb PakzadandGitHub adb40d1bb1 chore(llm-connections): move test suite to separate run (#9464)
* chore(llm-connections): move test suite to separate run

* push

* push

* push
2025-10-01 15:30:40 +00:00
Max DeichmannandGitHub f1601bb38d fix: show input/output/total columns in traces table correctly (#9468)
* fix: show input/output/total columns in traces table correctly

* fix: show input/output/total columns in traces table correctly

* fix: show input/output/total columns in traces table correctly
2025-10-01 14:55:30 +00:00
marliessophieandGitHub 466d7e32c5 fix(natural-language-filters): show only on traces table (#9466) 2025-10-01 14:13:01 +00:00
Steffen SchmitzandGitHub 819fa50f2e chore: remove AMT documentation and background migration (#9465) 2025-10-01 16:00:08 +02:00
Steffen SchmitzandGitHub 2d000cb826 chore: clear up unused entities in clickhouse (#9151)
* chore: clear up unused entities in clickhouse

* chore: patch

* chore: patches
2025-10-01 13:54:29 +00:00
Hassieb PakzadandGitHub d953606e2f fix(llm-schema-dialog): remove unnecessary event handler (#9463)
* feat(prompt-experiments): allow for structured output

* push

* remove propagation from submit in createoreditllmschema

* push

* add output_schema to trace metadata

* push

* push

* push

* push

* fix(create-llm-schema-dialog): remove unnecessary event handling

* push
2025-10-01 13:17:06 +00:00
Steffen SchmitzandGitHub 3611fc3b4e perf: add output score index to job_executions table (#9434) 2025-10-01 12:09:49 +00:00
Hassieb PakzadandGitHub fedfe24ec4 feat(prompt-experiments): allow for structured output (#9437)
* feat(prompt-experiments): allow for structured output

* push

* remove propagation from submit in createoreditllmschema

* push

* add output_schema to trace metadata

* push

* push

* push

* push
2025-10-01 11:58:59 +00:00
Valery MeleshkinandGitHub 9842d842d8 fix: change tRPC error code for ClickHouse errors (#9459) 2025-10-01 11:39:37 +00:00
Hassieb Pakzad 6cd98d2cd8 chore: release v3.114.0
Codespell / Check for spelling errors (push) Waiting to run
CI/CD / pre-job (push) Waiting to run
CI/CD / lint (push) Blocked by required conditions
CI/CD / prettier-check (push) Blocked by required conditions
CI/CD / test-docker-build (push) Blocked by required conditions
CI/CD / tests-web-sync (node24, pg12) (push) Blocked by required conditions
CI/CD / tests-web-sync (node24, pg15) (push) Blocked by required conditions
CI/CD / tests-web-async (node24, pg12, mode) (push) Blocked by required conditions
CI/CD / tests-web-async (node24, pg15, mode) (push) Blocked by required conditions
CI/CD / tests-web-async (node24, pg12, mode-azure) (push) Blocked by required conditions
CI/CD / tests-web-async (node24, pg15, mode-azure) (push) Blocked by required conditions
CI/CD / tests-web-async (node24, pg12, mode-redis-cluster) (push) Blocked by required conditions
CI/CD / tests-web-async (node24, pg15, mode-redis-cluster) (push) Blocked by required conditions
CI/CD / tests-worker (node24, pg12, mode) (push) Blocked by required conditions
CI/CD / tests-worker (node24, pg15, mode) (push) Blocked by required conditions
CI/CD / tests-worker (node24, pg12, mode-azure) (push) Blocked by required conditions
CI/CD / tests-worker (node24, pg15, mode-azure) (push) Blocked by required conditions
CI/CD / tests-worker (node24, pg12, mode-redis-cluster) (push) Blocked by required conditions
CI/CD / tests-worker (node24, pg15, mode-redis-cluster) (push) Blocked by required conditions
CI/CD / e2e-tests (push) Blocked by required conditions
CI/CD / e2e-server-tests (push) Blocked by required conditions
CI/CD / all-ci-passed (push) Blocked by required conditions
CI/CD / push-docker-image (push) Blocked by required conditions
release.yml / release (push) Waiting to run
2025-10-01 11:24:25 +02:00
Hassieb PakzadandGitHub 23300f9db8 fix(otel): completion start time parsing to handle JSON stringified values (#9455) 2025-10-01 09:15:08 +00:00
NimarandGitHub 5427d3f74f fix(trace-ui): make row label sticky for long cells (#9454)
* feat(trace-ui): make row label sticky for long cells

* simplify
2025-10-01 08:19:13 +00:00
Max DeichmannandGitHub a43fbc8a74 chore: upgrade golang migrate 4.19.0 (#9444)
* chore: do not upgrade to postgrs 18

* chore: do not upgrade to postgrs 18

* chore: do not upgrade to postgrs 18

* chore: upgrade golang-migrate
2025-10-01 07:22:25 +00:00
8d9c059572 fix: add current datetime to ai filter prompt (#9445)
* Add current datetime to AI prompt context

Co-authored-by: marc <marc@langfuse.com>

* Refactor datetime formatting for AI prompt

Co-authored-by: marc <marc@langfuse.com>

---------

Co-authored-by: Cursor Agent <cursoragent@cursor.com>
2025-09-30 21:01:21 +00:00
Hassieb PakzadandGitHub 37002f39bd test(llm-connections): add tests for all adapters (#9423)
* test(llm-connections): add tests for all adapters

* test(llm-connections): add tests for all adapters

* fix

* fix

* push

* push
2025-09-30 17:13:39 +00:00
c7de984fb1 fix: Accept any 2xx status for webhooks (#9439)
feat: Accept 2xx status codes for webhook success

Co-authored-by: Cursor Agent <cursoragent@cursor.com>
2025-09-30 17:02:49 +00:00
NimarandGitHub 1d3ad16710 chore: upgrade turborepo to 2.5.8 (#9337)
* chore: upgrade turborepo to 2.5.8

* add comments

* comments

* fix for turbo plugin default exports
2025-09-30 16:58:15 +00:00
17a358a1c2 feat(trace-ui): natural language filters (#9347)
* add fetchCompletion route

* use fetchLLMCompletion function

* chore(llm): add execution context to fetchLLMCompletion function

* add router

* chore: refactor to add resolveBedrockCredentials

* feat(env): add optional AWS Bedrock configuration

* chore: revert langfuse execution context for fetch llm completion

* frontend poc

* use bedrock keys

* chore(organization): add aiFeaturesEnabled column

* feat(organization): update organization update procedure to include optional aiFeaturesEnabled

* add route integrated

* add env vars

* update

* add proejct id

* fixup: ai feature toggle

* fully integrated

* fix: organization ai feature settings

* update

* fix ts

* chore: remove env variables from shared

* chore: rename environment variables

* chore: enhance LLM completion context

* chore: fix typing

* typing

* no more token delegate

* add project id

* fix: filter completion should never throw an error, fallback to empty json

* chore: set up bedrock credentials for development

* chore: remove TokenCountDelegate from types

* fix: integrate organization AI feature checks in filter builder

* fix: use project hook over org hook

* send traces to langfuse cloud

* fix build

* fix buuild

* validate filters

* chore: eslint

* add debug

* don't greedy match

* add tooltip

* fix parsing

* update AI feature text

* fix lockfile

* chore(router): gate setting/using ai features on self hosted deployments

* chore: replace env variable usage with useLangfuseCloudRegion hook across components

* chore: integrate useLangfuseCloudRegion hook to conditionally render AI features in filter builder and AI feature switch components

* docs: update REVIEW.md with Langfuse Cloud environment usage guidelines

* link

* chore: add LANGFUSE_AWS_BEDROCK_MODEL to .env.dev.example and update its definition in env.mjs

* Update useFilterState.ts

---------

Co-authored-by: Marlies Mayerhofer <74332854+marliessophie@users.noreply.github.com>
Co-authored-by: Leo <5489276+leoweigand@users.noreply.github.com>
2025-09-30 15:19:04 +00:00
Fabian ReinoldandGitHub 7e76fbfcb4 feat(auth): add support for Keycloak scope and ID token configuration (#9359)
* feat(auth): add support for Keycloak scope and ID token configuration

* fix(auth): update ID token envvar handling to use default value
2025-09-30 14:16:40 +00:00
Hassieb PakzadandGitHub aa5b5fb8a9 fix(otel): preferential parsing of environment key from span attributes (#9432) 2025-09-30 13:12:27 +00:00
Steffen SchmitzandGitHub c084d19c2d fix: skip JSON parsing for completion start time strings (#9418) 2025-09-30 11:43:31 +00:00
marliessophieandGitHub 23e765fc35 style(dataset-item): detail view to show item IO and past runs in two tabs (#9419) 2025-09-30 09:03:02 +00:00
Nimar f60286f13d chore: release v3.113.0
Codespell / Check for spelling errors (push) Waiting to run
CI/CD / pre-job (push) Waiting to run
CI/CD / lint (push) Blocked by required conditions
CI/CD / prettier-check (push) Blocked by required conditions
CI/CD / test-docker-build (push) Blocked by required conditions
CI/CD / tests-web-sync (node24, pg12) (push) Blocked by required conditions
CI/CD / tests-web-sync (node24, pg15) (push) Blocked by required conditions
CI/CD / tests-web-async (node24, pg12, mode) (push) Blocked by required conditions
CI/CD / tests-web-async (node24, pg15, mode) (push) Blocked by required conditions
CI/CD / tests-web-async (node24, pg12, mode-azure) (push) Blocked by required conditions
CI/CD / tests-web-async (node24, pg15, mode-azure) (push) Blocked by required conditions
CI/CD / tests-web-async (node24, pg12, mode-redis-cluster) (push) Blocked by required conditions
CI/CD / tests-web-async (node24, pg15, mode-redis-cluster) (push) Blocked by required conditions
CI/CD / tests-worker (node24, pg12, mode) (push) Blocked by required conditions
CI/CD / tests-worker (node24, pg15, mode) (push) Blocked by required conditions
CI/CD / tests-worker (node24, pg12, mode-azure) (push) Blocked by required conditions
CI/CD / tests-worker (node24, pg15, mode-azure) (push) Blocked by required conditions
CI/CD / tests-worker (node24, pg12, mode-redis-cluster) (push) Blocked by required conditions
CI/CD / tests-worker (node24, pg15, mode-redis-cluster) (push) Blocked by required conditions
CI/CD / e2e-tests (push) Blocked by required conditions
CI/CD / e2e-server-tests (push) Blocked by required conditions
CI/CD / all-ci-passed (push) Blocked by required conditions
CI/CD / push-docker-image (push) Blocked by required conditions
release.yml / release (push) Waiting to run
2025-09-30 09:02:29 +02:00
NimarandGitHub 0f43240c35 feat(playground): add support for claude sonnet 4.5 (#9410)
* feat(playground): add support for claude sonnet 4.5

* fix id

* cuid2

* upgrade langchain
2025-09-30 06:59:51 +00:00
Max DeichmannandGitHub dc4e94a68b chore: do not upgrade to postgres 18 (#9407)
* chore: do not upgrade to postgrs 18

* chore: do not upgrade to postgrs 18

* chore: do not upgrade to postgrs 18
2025-09-29 20:00:33 +00:00
NimarandGitHub fd98cd4cd7 chore: filter browser extensions from sentry errors (#9405) 2025-09-29 17:59:58 +00:00
NimarandGitHub a1f09a2e30 fix(prompts): don't throw on prompt deletion message (#9387)
* vibe

* cleanup
2025-09-29 17:23:10 +00:00
decac0cd5a fix(billing): Fix invoice table decimal display (#9399)
Fix: Ensure consistent USD formatting in billing table

Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: michael <michael@langfuse.com>
2025-09-29 18:36:17 +02:00
NimarandGitHub 17e600f65e chore: enable browser logs in terminal (#8935) 2025-09-29 15:15:53 +00:00
Steffen SchmitzandGitHub 0540828e21 chore: use otel ingestion queue by default (#9397) 2025-09-29 13:46:06 +00:00
Max DeichmannandGitHub 4f9d7b0e60 chore: update clickhouse writer batch size (#9396) 2025-09-29 13:15:14 +00:00
Leo WeigandandGitHub a940fa40d9 fix: always respect to in date range filters (#9393) 2025-09-29 12:37:50 +00:00
Steffen SchmitzandGitHub ab2007a627 perf: avoid updates on session ingest (#9392) 2025-09-29 11:58:17 +00:00
marliessophieandGitHub 9b8868ac97 feat(score-configs): support mutability (#9218)
* chore(score-configs): extract score domain types and unify validation

chore: fix types

chore: add documentation

chore: types

chore: rename types

chore: rename types

chore: fix test type imports

chore: validation tweaks

chore: types

chore: types

* chore: remove dead code

* chore: handle custom errors for score categories

* chore: simplify

* feat(score-configs): support mutability (#9143)

* fix(scores): refactor validateConfigAgainstBody to support context-based validation

* fix: chore

* fix: chore

* chore(scores): display removed categorical score values as outdated and mark stale

* fix(scores): enrich categories with outdated status and streamline return logic

* fix(scores): handle empty categories in enrichCategories function

* fix(scores): handle empty categories in enrichCategories function
2025-09-29 11:11:27 +00:00
Marc KlingenandGitHub 4f6e917cd7 feat(ui): add search bar to members table (#9339)
* feat: add search bar to members table

* push
2025-09-25 14:56:24 +02:00
NimarandGitHub da51c01f35 fix(otel-mapper): null model props shouldn't cause map to generation (#9308)
* fix(otel-mapper): empty model properties shouldn't cause map to generation type

* fix test name

* fix the bug

* cleanup
2025-09-25 07:49:29 +00:00
Michael FröhlichandGitHub 4aeea98b56 fix(billing): Fix base-fee aggregation for legacy billing (#9328)
Fix base-fee aggregation for legacy billing
2025-09-24 18:18:00 +00:00
Steffen SchmitzandGitHub 917e35a1b3 perf: remove unused join for job exec status aggregation (#9230) 2025-09-23 10:29:01 +00:00
marliessophieandGitHub 2e0a548e6f feat(datasets): support filtering on per-run-basis in compare table (#9098)
* fixup(datasets-compare): support front-end column filtering

- Added column filtering capabilities to the DatasetCompareRunsTable and DatasetRunAggregateColumnHelpers.
- Introduced useColumnFilterState hook for managing filter state with URL persistence.
- Updated DatasetRunItemsByRunTable to accept multiple dataset run IDs.
- Enhanced PopoverFilterBuilder to support different button variants for filter actions.
- Refactored related components to integrate new filtering features.

* chore: remove fetching of run items data in compare view

* chore: move filter logic into useDatasetRunAggregateColumns

* chore: extend useColumnFilterState

* chore(scores): add ScoreDataType and ScoreDataTypeDomain

* chore: move state out of useDatasetRunAggregateColumns

* chore(dataset-run-items): enhance DatasetRunItem types and converters for optional IO support

* chore: refactor compare data fetching to support one API route

* feat: support filtering on compare view

* feat: add FilteredRunPills component for enhanced filtering display in compare view

* chore: eslint

* chore: simplify and refactor

* chore: push

* fix import

* chore: remove console log

* chore: drop first approach of pulling dataset item ids from postgres

* chore: rename trpc route

* tests: adjust non filter tests

* chore: rename dataset repository helper functions

* chore: add intersection query

* chore: push

* chore: debounce updateRunFilters

* chore: add filter synchronization for run IDs in DatasetCompareRunsTable
2025-09-23 08:56:47 +00:00
Hassieb PakzadandGitHub f9222c526f feat(otel): add llm.response.model parsing (#9261) 2025-09-23 06:56:07 +00:00
Hassieb PakzadandGitHub 5cb61d6a96 fix(llm-tools-and-schemas): allow periods in names (#9296) 2025-09-23 06:55:58 +00:00
Hassieb PakzadandGitHub 463ada23ca fix(models): drop preview from gemini-2.5-flash-lite (#9299) 2025-09-22 18:20:23 +00:00
b2d22a0720 fix(billing): Add delimiting line to support emails (#9292)
Fix typo in support chat auto-reply

Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: michael <michael@langfuse.com>
2025-09-22 13:39:02 +00:00
Marc KlingenandGitHub fd1f1786da chore(cloud): limit org members to 2 on free plan to be in line with pricing/packaging (#9291) 2025-09-22 15:17:31 +02:00
Max DeichmannandGitHub 56598597df chore: remove hosting of ..-in-the-middle packages (#9288)
push
2025-09-22 11:04:10 +00:00
ea71c40ecb fix(billing): Hide promotion code for hobby plan users (#9286)
feat: Hide discount view for Hobby plan users

Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: michael <michael@langfuse.com>
2025-09-22 12:05:27 +02:00
Max DeichmannandGitHub 20cc20f908 chore: enable turborepo (#9275)
* chore: enable turborepo

* chore: enable turborepo

* chore: enable turborepo
2025-09-22 09:08:49 +00:00
Michael FröhlichandGitHub 6326a6d788 fix(billing): Avoid 500s through uninitialized stripe client in dev environment (#9276)
make getUsage endpoint nullable to aoid 500s for developers in dev mode without stripe secret
2025-09-21 11:07:30 +00:00
Max DeichmannandGitHub 286f7ea29f chore: upgrade axios (#9265)
chore: upgrade zxios
2025-09-19 18:49:08 +00:00
Michael FröhlichandGitHub 700921f69f feat(billing): Add option to add discount code to active subscriptions from settings menu (#9235)
* Add dialog to add discounts to running subscriptions

* reset opId
2025-09-19 16:51:52 +02:00
Leo WeigandandGitHub 9e3379f05f feat: improved time range filtering (#9252)
* feat: better relative date range selects

* fix: date picker styles

fixes LFE-6702

* add TimeRangePicker

* use TimeRangePicker in traces view

* update dashboards and tables to use TimeRangePicker

* missed fixes due to updated time range utils

* simplify

* update remaining

* remove old timestamp filter

* address comment

* silence codespell false-positive

* fix badge heights
2025-09-19 14:05:55 +00:00
marliessophieandGitHub bc178010c1 fix(dataset-runs): during loading states render skeletons for charts (#9258) 2025-09-19 13:55:43 +00:00
marliessophieandGitHub 47a1cb24a3 style(experiments-compare): sort scores alphabetically in cell (#9253) 2025-09-19 12:43:38 +00:00
Valery MeleshkinandGitHub 312d97f509 fix: ensure cases when CH outputs an error as a row are handled (#9238)
ClickHouse has a quirk when it comes to handling exceptions mid response.
It will simply output a row with "exception" key inside, which is indistinguishable from
a query like `SELECT "my lovely string" AS exception;` may return.

This PR makes the best effort to convert such rows into errors and throws them.

See:
 - https://github.com/ClickHouse/clickhouse-js/issues/332
 - https://github.com/ClickHouse/ClickHouse/issues/75175

Ideally this should get fixed in the future versions of ClickHouse.
2025-09-19 12:12:04 +00:00
A. WilcoxandGitHub 7776462c9b style: Fix grammar of org invite email subject (#9239) 2025-09-19 11:34:34 +00:00
NimarandGitHub aa8eabbc19 fix(otel-ingest): don't overwrite trace metadata->attributes from new observation (#9247)
* fix(otel-ingest): don't overwrite trace metadata from new observation

* update test

* skip test, not good

* skip test because it's not getting the entire trace
2025-09-19 11:29:12 +00:00
Valery MeleshkinandGitHub f4caacabdb fix: improve error message for CH memory issues on public API (#9191)
* fix: improve error message for CH memory issues on public API

* fix(ui): surface ClickHouse error message providing a bit of advice to the user.
2025-09-18 22:49:39 +00:00
NimarandGitHub 7892ff521d fix(trpc): show toast on trpc error to user (#9231) 2025-09-18 17:14:44 +00:00
Michael FröhlichandGitHub 45215cb4ff feat(billing): Allow past subscribers see their invoices (#9232)
* Allow users on hobby plan to see past invoices

* Fix linter errors
2025-09-18 14:42:01 +00:00
Michael FröhlichandGitHub 5404119473 fix(billing): automatically cancel and invoice stripe subscriptions when an org is deleted (#9228)
* cancel stripe subscription on invoice delete

* Update organizationRouter.ts
2025-09-18 16:15:58 +02:00
Hassieb PakzadandGitHub 7769cb9d2a chore: run sdk-api-spec-bot on changes in fern dir only (#9229) 2025-09-18 15:59:34 +02:00
Hassieb PakzadandGitHub 0b68c410cf chore(sdk-api-spec-bot): auto add reviewer (#9227) 2025-09-18 15:46:55 +02:00
Hassieb PakzadandGitHub efa6fe7e6a chore: update sdk-api-spec bot for js sdk (#9226) 2025-09-18 15:37:11 +02:00
Michael FröhlichandGitHub 16f41f809a feat(billing): show active promotion codes in billing settings #9210 (#9221)
* add label for discounts

* remove logging statement
2025-09-18 13:16:57 +00:00
Hassieb PakzadandGitHub a7d59d7bb2 chore: update sdk-api-spec bot (#9222) 2025-09-18 14:57:59 +02:00
Michael FröhlichandGitHub 291abf3205 chore(billing): Update price label for core plan (#9216)
update core price to 29 USD
2025-09-18 13:22:34 +02:00
Leo WeigandandGitHub 5df5a7cd48 chore(single-trace): make tree a little more dense and allow click everywhere (#9183)
* fix(single-trace): tree slightly more compact, allow click anywhere

* simplify tree node layout and make more robust

* make lint pass
2025-09-18 10:14:55 +00:00
marliessophieandGitHub ac2936d316 fix(experiment-form-ui): prevent event bubbling from embedded form submission handlers (#9215) 2025-09-18 10:12:22 +00:00
Steffen SchmitzandGitHub 0335dcd18d fix: include session scores in posthog export (#9214) 2025-09-18 10:06:20 +00:00
27cfbb14a1 feat: show last authentication method on login (#9194)
* Refactor: Add last used auth method persistence

Co-authored-by: leo <leo@langfuse.com>

* Refactor: Use useLocalStorage hook for auth method persistence

Co-authored-by: leo <leo@langfuse.com>

* Refactor SSOButtons to use parent-managed last used method

Co-authored-by: leo <leo@langfuse.com>

* Refactor: Conditionally show "Last used" badge on sign-in

Co-authored-by: leo <leo@langfuse.com>

* refactor styles

* pass lint

---------

Co-authored-by: Cursor Agent <cursoragent@cursor.com>
2025-09-18 10:02:03 +00:00
marliessophieandGitHub 4c5828dade fix(experiments-ui): resolve composed prompts for prompt-experiment setup (#9211)
* fix(experiments): resolve composed prompts for prompt-experiment setup

* chore: guard
2025-09-18 09:49:10 +00:00
2c9afcc58b feat(billing): new billing setup (#9053)
* Michael/lfe 6645 (#9046)

* refactor billing settings and cancel button

* refactor billing components

* chore(stripe): bump stripe from 17.4.0 to 18.5.0.basil (#9050)

* bump stripe api version to 18.5.0.basil

* update stripeAPIHandler to 18.5.0

* update cloud billing router and add log statements

* Show stripe buttons only with active subscription

* remove logging statements

* feat(billing): show plan end in billing settings (#9052)

* update cloudConfigSchema with metadata field for scheduled cancellations

* add function to unpack cancellation metadata in subscription.update webhook

* store cancellation metadata in db

* add  and  aggegregation option to LocalIsoDate component

* add BillingCurrentPlanLabel component to indicate cancelled plans

* add title prop to StripeCustomerPortalButton for better accessibility

* remove text from label

* fix(billing): cancel pending cancellations on plan switches (#9076)

cancel pending cancellations on plan switches

* feat(billing): distinguish between new subscriptions and legacy subscriptions (#9078)

* add orderKeys to distinguish between upgrades and downgrades of subcriptions

* add optional activeUsageProductId to cloudConfig schema

* add legacy Plan flag on cloudConfig.stripe schema

* set usageProductId in webhook callback

* feat(billing): Add new billing logic and update stripe integration (#9096)

* update stripe usage product id to match sandbox

* update to cloudBillinRouter.createCheckoutSession to deal with legacy and new prices

* add orderKeys and replace them with monthly price to distinguish upgrade and downgrade paths

* update cloudBillingRouter.changePlan mutation to deal with different upgrade paths

* update stripeWebHookApiHandler to listen for subscriptionSchedule events

* update cloudConfig schema to store subscription schedule metadata

* extract formatLocalIsoDate into own function

* add useBillingInformation hook for common ui-billing operations

* Add BillingScheduleNotification component to indicate to user when their subscription is cancelled or scheduled to change

* refactor billing component to use new hook

* add managed cancellation button

* fix planSwitch info overrides on schedule.update callbacks

* remove logging statements

* add buttons to isolate strip cancelation, plan continuation, and switching

* refacor billingSwitchPlanDialog

* fix linter errors

* fix bug where released schedules where released

* fix Billing Portal ui issue

* add support for legacy plans to enable testing on staging

* clear cancellationInfo and planSwitchScheduleInfo on customer.subscription.deleted webhook

* extract condition into variable

* fix typo

* Add invoice table

* fix unstable ref causing rerenders

* fix uneccessary cast

* feat(billing): invoice table in billing settings (#9134)

* Add invoice table

* fix unstable ref causing rerenders

* fix uneccessary cast

* update webhooks to allow externally triggered subscription schedules

* nit

* feat(billing): Rewrite Billing Service to Reduce Complexity and Minimize Data Drift Risks (#9169)

* refactor code for more effective refetches

* Refactor cloudBillingRouter into BillingService

* Add Idempotency keys for unique stripe ops, add logging and auditLogs, Refactor for cleaner dx

* Add env checks to stripe webhook handler

* Add env checks to stripe webhook handler

* add review comments

* standardize logger statements

* Add new readme

* update plan switch/cance/reactivate explanation messages

* Add fallbacks in org resolution to webhook, to deal with susbcriptions created from the dashboard

* fix linter errors

* remove comment

* fix build errors

* implement review comments

* add discount column to invoice table

* update orderkey of core plan to reflect new price

* fix(billing): apply existing discounts to new subscription phase when updating via subscription schedule (#9199)

* safeguard against illegal invoice.createPreview call and apply discounts on subscription schedule change

* fix typo

* add docstrings to stripeIdempotencyKey.ts

* update productId for prod

---------

Co-authored-by: Marc Klingen <git@marcklingen.com>
2025-09-18 11:11:02 +02:00
Nimar d401d42377 chore: release v3.112.0
Codespell / Check for spelling errors (push) Waiting to run
CI/CD / pre-job (push) Waiting to run
CI/CD / lint (push) Blocked by required conditions
CI/CD / prettier-check (push) Blocked by required conditions
CI/CD / test-docker-build (push) Blocked by required conditions
CI/CD / tests-web-sync (node24, pg12) (push) Blocked by required conditions
CI/CD / tests-web-sync (node24, pg15) (push) Blocked by required conditions
CI/CD / tests-web-async (node24, pg12, mode) (push) Blocked by required conditions
CI/CD / tests-web-async (node24, pg15, mode) (push) Blocked by required conditions
CI/CD / tests-web-async (node24, pg12, mode-azure) (push) Blocked by required conditions
CI/CD / tests-web-async (node24, pg15, mode-azure) (push) Blocked by required conditions
CI/CD / tests-web-async (node24, pg12, mode-redis-cluster) (push) Blocked by required conditions
CI/CD / tests-web-async (node24, pg15, mode-redis-cluster) (push) Blocked by required conditions
CI/CD / tests-worker (node24, pg12, mode) (push) Blocked by required conditions
CI/CD / tests-worker (node24, pg15, mode) (push) Blocked by required conditions
CI/CD / tests-worker (node24, pg12, mode-azure) (push) Blocked by required conditions
CI/CD / tests-worker (node24, pg15, mode-azure) (push) Blocked by required conditions
CI/CD / tests-worker (node24, pg12, mode-redis-cluster) (push) Blocked by required conditions
CI/CD / tests-worker (node24, pg15, mode-redis-cluster) (push) Blocked by required conditions
CI/CD / e2e-tests (push) Blocked by required conditions
CI/CD / e2e-server-tests (push) Blocked by required conditions
CI/CD / all-ci-passed (push) Blocked by required conditions
CI/CD / push-docker-image (push) Blocked by required conditions
release.yml / release (push) Waiting to run
2025-09-18 10:51:49 +02:00
marliessophieandGitHub 322c767a33 fix(datasets): update score filters to handle empty run lists in DatasetRunsTable and DatasetCompare components (#9209) 2025-09-18 08:14:56 +00:00
Hassieb Pakzad 53726d58bf Revert "chore: update fern python generator (#9203)"
This reverts commit 63feb943a4.
2025-09-17 22:54:54 +02:00
Hassieb PakzadandGitHub 63feb943a4 chore: update fern python generator (#9203) 2025-09-17 22:18:27 +02:00
Hassieb PakzadandGitHub 6379c7c54b chore: update sdk-api-spec bot (#9197) 2025-09-17 18:39:48 +02:00
Hassieb PakzadandGitHub 69b3b81771 chore: update sdk-api-spec bot (#9195) 2025-09-17 18:30:59 +02:00
marliessophieandGitHub 668d1b6933 fix(llm): OpenAI llm completion reasoning model checks and token adjustments (#9174) 2025-09-17 15:48:42 +00:00
NimarandGitHub d7069df433 fix(trace-ui): don't render markdown if content too big (#9190)
* add key to json viewer

* don't render markdown if there are too many characters

* bump posthog

* cleanup

* restore
2025-09-17 17:55:08 +02:00
Hassieb PakzadandGitHub 83d3009331 chore: add sdk-api-spec bot (#9193) 2025-09-17 17:40:03 +02:00
Hassieb PakzadandGitHub 688659c597 chore: add sdk-api-spec bot (#9192) 2025-09-17 17:26:40 +02:00
marliessophieandGitHub 63ec00efe9 feat(api): GET /scores support for filtering by sessionId (#9176)
* feat(api): GET /scores support for filtering by sessionId

* chore: test
2025-09-17 15:14:42 +00:00
Steffen SchmitzandGitHub a11b5ffa92 chore: switch docker compose cluster to bitnami legacy (#9188) 2025-09-17 17:01:08 +02:00
Hassieb PakzadandGitHub 72bf6a75c1 chore: add sdk-api-spec bot (#9187) 2025-09-17 16:50:01 +02:00
Hassieb PakzadandGitHub 92b11374d9 chore: add sdk-api-spec bot (#9184)
* chore: add sdk-api-spec bot

* Update web/package.json
2025-09-17 16:42:34 +02:00
marliessophieandGitHub 828c3ff7be fix(llm-completion): add transformations for providers requiring a user message (#9162) 2025-09-16 16:27:52 +00:00
7b35cb0e99 docs: mention SLACK_STATE_SECRET env var as a required one (#9121)
* docs: mention SLACK_STATE_SECRET env var as a required one

Signed-off-by: ajatprabha <ajat.prabha.leo@gmail.com>

* Update README.md

---------

Signed-off-by: ajatprabha <ajat.prabha.leo@gmail.com>
Co-authored-by: Nimar <l.nimar.b@gmail.com>
2025-09-16 16:14:58 +00:00
Leo WeigandandGitHub 8d5c445dc5 fix(tables): cmd+click opens traces and observations in a new tab (#9161) 2025-09-16 16:00:26 +00:00
Marc KlingenandGitHub c752af2201 chore(cloud): remove eval count limit from hobby/free plan (#9158) 2025-09-16 16:38:05 +02:00
Steffen SchmitzandGitHub 03f6204797 chore: add debugging log for invalid start_time in batch exports (#9150) 2025-09-16 07:51:54 +00:00
Steffen SchmitzandGitHub c33708e5db fix: fetch environment options from raw data (#9141)
* fix: fetch environment options from raw data

* chore: drop stuff

* chore: patch

* chore: always add default

* chore: adjust mapping logic for correctness

* chore: drop materialized views to fill project_environments

* chore: remove migration files
2025-09-16 07:30:36 +00:00
Marc KlingenandGitHub 4c2c3d21c7 chore: move readme images to github assets (#9142) 2025-09-15 16:51:55 +02:00
Hassieb PakzadandGitHub 9a09fa2ad0 fix(ingestion-service): skip tokenization for obs level ERROR (#9133) 2025-09-15 13:11:05 +00:00
Steffen SchmitzandGitHub 43e0644c64 perf: drop final on check trace exists (#9117)
* perf: drop final on check trace exists

* chore: only apply final on eval check if non-id filter is used

* chore: skip full query if not needed

* chore: revert query changes

* chore: simplify condition

* chore: add metrics
2025-09-15 11:11:12 +00:00
Marc KlingenandGitHub c5be145a53 docs: add note re otel to ingestion endpoint api reference (#9135) 2025-09-15 10:29:09 +00:00
NimarandGitHub 3a002f03d5 chore: upgrade sentry v8 -> v10 (#8934)
* chore: upgrade sentry v8 -> v10

* fix source maps

* make pnpm-lockfile single quoted as is on main

* move sentry config to new file
2025-09-15 09:53:23 +00:00
NimarandGitHub c785156220 fix(datasets): graphs not readable in dark mode (#9132) 2025-09-15 09:05:47 +00:00
Max Deichmann 6f8627ed86 chore: release v3.111.0
Codespell / Check for spelling errors (push) Waiting to run
CI/CD / pre-job (push) Waiting to run
CI/CD / lint (push) Blocked by required conditions
CI/CD / prettier-check (push) Blocked by required conditions
CI/CD / test-docker-build (push) Blocked by required conditions
CI/CD / tests-web-sync (node24, pg12) (push) Blocked by required conditions
CI/CD / tests-web-sync (node24, pg15) (push) Blocked by required conditions
CI/CD / tests-web-async (node24, pg12, mode) (push) Blocked by required conditions
CI/CD / tests-web-async (node24, pg15, mode) (push) Blocked by required conditions
CI/CD / tests-web-async (node24, pg12, mode-azure) (push) Blocked by required conditions
CI/CD / tests-web-async (node24, pg15, mode-azure) (push) Blocked by required conditions
CI/CD / tests-web-async (node24, pg12, mode-redis-cluster) (push) Blocked by required conditions
CI/CD / tests-web-async (node24, pg15, mode-redis-cluster) (push) Blocked by required conditions
CI/CD / tests-worker (node24, pg12, mode) (push) Blocked by required conditions
CI/CD / tests-worker (node24, pg15, mode) (push) Blocked by required conditions
CI/CD / tests-worker (node24, pg12, mode-azure) (push) Blocked by required conditions
CI/CD / tests-worker (node24, pg15, mode-azure) (push) Blocked by required conditions
CI/CD / tests-worker (node24, pg12, mode-redis-cluster) (push) Blocked by required conditions
CI/CD / tests-worker (node24, pg15, mode-redis-cluster) (push) Blocked by required conditions
CI/CD / e2e-tests (push) Blocked by required conditions
CI/CD / e2e-server-tests (push) Blocked by required conditions
CI/CD / all-ci-passed (push) Blocked by required conditions
CI/CD / push-docker-image (push) Blocked by required conditions
release.yml / release (push) Waiting to run
2025-09-15 08:27:37 +02:00
6a7034a5ad chore: Check tracing configuration for data retention (#9045)
* Refactor: Rename hasAnyTrace to hasTracingConfigured

Co-authored-by: max <max@langfuse.com>

* chore: upgrade posthog repository

---------

Co-authored-by: Cursor Agent <cursoragent@cursor.com>
2025-09-14 16:16:12 +00:00
Max DeichmannandGitHub 29443bcdbd chore: upgrade posthog repository (#9126) 2025-09-14 16:11:51 +00:00
Steffen SchmitzandGitHub 9f066a285f feat: export non-trace scores via posthog integration (#9103) 2025-09-12 15:15:34 +00:00
Steffen SchmitzandGitHub 664bd3596e chore: increase default throughput on create eval queue (#9108) 2025-09-12 14:16:41 +00:00
Leo WeigandandGitHub bad27d2625 fix(table): tags wrapping and being cropped (#9107) 2025-09-12 13:52:48 +00:00
Leo WeigandandGitHub 4da02c1ebe fix(design-system): hover card layering (#9106) 2025-09-12 13:17:21 +00:00
Max DeichmannandGitHub 65bd0bf5c1 perf: move more endpoints to read only (#9105) 2025-09-12 14:38:05 +02:00
Marc KlingenandGitHub b1ee21fc34 docs: update dependency list in readme (#9102)
* docs: update readme

* push
2025-09-12 12:20:29 +02:00
Hassieb PakzadandGitHub b894e8a620 fix(quickstart): update langchain js import (#9093) 2025-09-11 16:48:32 +00:00
Max DeichmannandGitHub aa39af94fb chore: move reads to read replica (#9091)
* chore: mve reads to read replica

* chore: mve reads to read replica
2025-09-11 16:42:05 +00:00
b1880ddb82 feat(support):Include ticket id in email subject (#8991)
* feat: Add unique ID to support thread titles

Co-authored-by: marc <marc@langfuse.com>

* add nanoid package

---------

Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: froemic <m.froehlich1994@gmail.com>
Co-authored-by: Michael Fröhlich <15179255+FroeMic@users.noreply.github.com>
2025-09-11 15:12:22 +00:00
Valery MeleshkinandGitHub 19e9da214e fix(otel): Make sure we do not process empty otel events (#9082)
* fix: Make sure we do not process empty otel events

* chore: fix a typo

* chore: addressing review comments.
2025-09-11 13:36:17 +00:00
Hassieb PakzadandGitHub 2e698c8ec0 chore: bump langchain anthropic (#9087) 2025-09-11 15:14:34 +02:00
Marc KlingenandGitHub 46feb262ec chore(cloud): switch to in-app support for sso setup to replace email (#9086) 2025-09-11 12:41:17 +00:00
steffen911 23b830074a chore: release v3.110.0
Codespell / Check for spelling errors (push) Waiting to run
CI/CD / pre-job (push) Waiting to run
CI/CD / lint (push) Blocked by required conditions
CI/CD / prettier-check (push) Blocked by required conditions
CI/CD / test-docker-build (push) Blocked by required conditions
CI/CD / tests-web-sync (node24, pg12) (push) Blocked by required conditions
CI/CD / tests-web-sync (node24, pg15) (push) Blocked by required conditions
CI/CD / tests-web-async (node24, pg12, mode) (push) Blocked by required conditions
CI/CD / tests-web-async (node24, pg15, mode) (push) Blocked by required conditions
CI/CD / tests-web-async (node24, pg12, mode-azure) (push) Blocked by required conditions
CI/CD / tests-web-async (node24, pg15, mode-azure) (push) Blocked by required conditions
CI/CD / tests-web-async (node24, pg12, mode-redis-cluster) (push) Blocked by required conditions
CI/CD / tests-web-async (node24, pg15, mode-redis-cluster) (push) Blocked by required conditions
CI/CD / tests-worker (node24, pg12, mode) (push) Blocked by required conditions
CI/CD / tests-worker (node24, pg15, mode) (push) Blocked by required conditions
CI/CD / tests-worker (node24, pg12, mode-azure) (push) Blocked by required conditions
CI/CD / tests-worker (node24, pg15, mode-azure) (push) Blocked by required conditions
CI/CD / tests-worker (node24, pg12, mode-redis-cluster) (push) Blocked by required conditions
CI/CD / tests-worker (node24, pg15, mode-redis-cluster) (push) Blocked by required conditions
CI/CD / e2e-tests (push) Blocked by required conditions
CI/CD / e2e-server-tests (push) Blocked by required conditions
CI/CD / all-ci-passed (push) Blocked by required conditions
CI/CD / push-docker-image (push) Blocked by required conditions
release.yml / release (push) Waiting to run
2025-09-11 14:12:21 +02:00
Steffen SchmitzandGitHub fa5908a749 feat: add api endpoint to configure blob storage integration (#9047)
* feat: add api endpoint to configure blob storage integration

* chore: add test suite and generated API docs

* chore: add implementation

* chore: simplify delete endpoint

* chore: PUT route cleanups
2025-09-11 12:05:12 +00:00
Steffen SchmitzandGitHub 26045d089c perf: add a global concurrency limit to the createEvalQueue (#9077) 2025-09-11 12:09:28 +02:00
Steffen SchmitzandGitHub edfb15c010 feat: add gcp vertex tool inputs and outputs on otel spans (#9074) 2025-09-11 08:15:28 +00:00
marliessophieandGitHub 1f76ae650f fix(peek): safe parse runs url parameter in compare data hook (#9073) 2025-09-11 07:44:16 +00:00
36dc608b58 chore(worker): Move nodemon to tsx for reloads from packages/shared (#8801)
---------

Co-authored-by: Max Deichmann <m.deichmann@tum.de>
2025-09-11 00:36:02 +02:00
Jannik MaierhöferandGitHub bc0735729f fix(ui): update JS/TS SDK v4 notification (#9064) 2025-09-10 18:58:53 +00:00
Steffen SchmitzGitHubellipsis-dev[bot] <65095814+ellipsis-dev[bot]@users.noreply.github.com>
88e553734e chore: avoid otel alerts about public key mismatch on worker (#9063)
* chore: avoid otel alerts about public key mismatch on worker

* Update worker/src/queues/otelIngestionQueue.ts

Co-authored-by: ellipsis-dev[bot] <65095814+ellipsis-dev[bot]@users.noreply.github.com>

---------

Co-authored-by: ellipsis-dev[bot] <65095814+ellipsis-dev[bot]@users.noreply.github.com>
2025-09-10 18:58:10 +00:00
Jannik MaierhöferandGitHub bd5495321e fix(ui): remove slack support from pro tier (#9059) 2025-09-10 17:33:04 +00:00
e8ebfb8e3e feat(ui): add notification card for JS SDK v4 GA (#9022)
* feat(ui): add notification card for JS SDK v4 GA

* Update web/src/components/nav/sidebar-notifications.tsx

* Update web/src/components/nav/sidebar-notifications.tsx

* Update web/src/components/nav/sidebar-notifications.tsx

---------

Co-authored-by: Marc Klingen <git@marcklingen.com>
2025-09-10 16:47:34 +00:00
Leo WeigandandGitHub 8c076b6b50 fix(tooltip): portal content (#9055) 2025-09-10 16:24:13 +00:00
Hassieb PakzadandGitHub 2cdc3cab7f fix(otel): add root span metadata to trace (#9054) 2025-09-10 16:04:37 +00:00
Leo WeigandandGitHub 729c2132af feat(sessions): add trace detail peek view and clean up layout (#9042)
* chore: add richer seed data for sessions

* session styling improvements

* support peek view in session detail

* fix formatting switch padding

* add usePreserveRelativeScroll hook

* button improvements

* move buttons and trace detail link

* columns adjustment

* finalise card layout

* prettify aggr metrics header

* tidy up paddings

* refactor to new peek view hook

* a bit more tidying up

* fix types
2025-09-10 12:44:23 +00:00
marliessophieandGitHub d3627bf225 feat(dataset-run-items): pin dataset item id column in ui table (#9040) 2025-09-10 12:27:28 +00:00
Max Deichmann 05b87199ec chore: release v3.109.0
Codespell / Check for spelling errors (push) Waiting to run
CI/CD / pre-job (push) Waiting to run
CI/CD / lint (push) Blocked by required conditions
CI/CD / prettier-check (push) Blocked by required conditions
CI/CD / test-docker-build (push) Blocked by required conditions
CI/CD / tests-web-sync (node24, pg12) (push) Blocked by required conditions
CI/CD / tests-web-sync (node24, pg15) (push) Blocked by required conditions
CI/CD / tests-web-async (node24, pg12, mode) (push) Blocked by required conditions
CI/CD / tests-web-async (node24, pg15, mode) (push) Blocked by required conditions
CI/CD / tests-web-async (node24, pg12, mode-azure) (push) Blocked by required conditions
CI/CD / tests-web-async (node24, pg15, mode-azure) (push) Blocked by required conditions
CI/CD / tests-web-async (node24, pg12, mode-redis-cluster) (push) Blocked by required conditions
CI/CD / tests-web-async (node24, pg15, mode-redis-cluster) (push) Blocked by required conditions
CI/CD / tests-worker (node24, pg12, mode) (push) Blocked by required conditions
CI/CD / tests-worker (node24, pg15, mode) (push) Blocked by required conditions
CI/CD / tests-worker (node24, pg12, mode-azure) (push) Blocked by required conditions
CI/CD / tests-worker (node24, pg15, mode-azure) (push) Blocked by required conditions
CI/CD / tests-worker (node24, pg12, mode-redis-cluster) (push) Blocked by required conditions
CI/CD / tests-worker (node24, pg15, mode-redis-cluster) (push) Blocked by required conditions
CI/CD / e2e-tests (push) Blocked by required conditions
CI/CD / e2e-server-tests (push) Blocked by required conditions
CI/CD / all-ci-passed (push) Blocked by required conditions
CI/CD / push-docker-image (push) Blocked by required conditions
release.yml / release (push) Waiting to run
2025-09-10 13:52:40 +02:00
a618d53451 fix: require admin api key for background migration retries in OSS (#9028)
* feat: Add admin API key authentication for retrying migrations

Co-authored-by: max <max@langfuse.com>

* fix: deny background migrations on LF cloud

* fix: deny background migrations on LF cloud

* fix: deny background migrations on LF cloud

* fix: deny background migrations on LF cloud

* fix: deny background migrations on LF cloud

* fix: deny background migrations on LF cloud

* fix: deny background migrations on LF cloud

---------

Co-authored-by: Cursor Agent <cursoragent@cursor.com>
2025-09-10 09:09:55 +00:00
Matthew LapointeandGitHub 9907c39861 chore: ip allowlist for webhooks (#8977) 2025-09-10 08:49:22 +00:00
marliessophieandGitHub 4c8594e47a feat(dataset-run-items-ui): support score filters in UI table (#8993)
* feat(dataset-run-items-ui): support score filters in UI table

* chore: add dataset run item scores to seeder

* chore: make datasetid optional

* chore: drop dataset item id from scores CTE
2025-09-10 08:15:06 +00:00
marliessophieandGitHub a69609229a style: increase tooltip and sidebar toggle z-index (#9035) 2025-09-10 07:51:44 +00:00
Max DeichmannGitHubellipsis-dev[bot] <65095814+ellipsis-dev[bot]@users.noreply.github.com>
217ae648d6 chore: disallow background migrations on langfuse cloud (#9027)
* security: rename protectedprocedure

* fix: deny background migrations on LF cloud

* Update web/src/features/background-migrations/server/background-migrations-router.ts

Co-authored-by: ellipsis-dev[bot] <65095814+ellipsis-dev[bot]@users.noreply.github.com>

---------

Co-authored-by: ellipsis-dev[bot] <65095814+ellipsis-dev[bot]@users.noreply.github.com>
2025-09-09 21:53:55 +00:00
Max DeichmannandGitHub 72e5b51398 fix: rename protectedprocedure (#9026)
security: rename protectedprocedure
2025-09-09 21:37:44 +00:00
0cbbf94568 chore(cloud): allow selection of HIPAA cloud region on sign-in/sign-up page (#9024)
* feat: Add HIPAA region and improve region selection

Co-authored-by: marc <marc@langfuse.com>

* Refactor: Remove unused state and simplify region logic

Co-authored-by: marc <marc@langfuse.com>

* Fix: Update BAA link to HIPAA security page

Co-authored-by: marc <marc@langfuse.com>

* Refactor AuthCloudRegionSwitch component for clarity

Co-authored-by: marc <marc@langfuse.com>

* delete weird file and prettier

* replace contact support on sign in with mailto link

---------

Co-authored-by: Cursor Agent <cursoragent@cursor.com>
2025-09-09 22:28:08 +02:00
marliessophieandGitHub 61f4c3ac95 feat(tables-ui): pin columns in annotation and prompt metrics tables (#9021) 2025-09-09 17:50:59 +00:00
cc8f521741 fix(auth): on unsuccessful password-reset attempts (wrong OTP) delete verification tokens in db to reduce enumeration risk (#9009)
* Refactor: Use signIn with redirect: false for OTP verification

Co-authored-by: marc <marc@langfuse.com>

* nit

* revert button changes, not the focus of this pr

* push

---------

Co-authored-by: Cursor Agent <cursoragent@cursor.com>
2025-09-09 17:17:06 +00:00
Marc KlingenandGitHub 540f8622e3 chore: fix password reset setup docs link (#9020) 2025-09-09 18:06:48 +02:00
marliessophieandGitHub a7e3dec6f0 chore(peek): remove polling logic and dependency on table state from peek implementation (#8885)
* fixup(peek): isolate data fetching for peek view to depend on trpc and be independent of table state

* fix: rely only on url for peek state management

* fixup: drop complex peek logic

* refactor: replace createPeekHandler with usePeekNavigation for improved URL management in table components

* chore: all peek logic fixed except compare view and expand

* chore: refactor navigation logic

* chore: docs

* chore: clear url params also in row click navigation
2025-09-09 15:43:42 +00:00
Hassieb PakzadandGitHub 5760d51c08 fix(otel): handle event observation type (#9012) 2025-09-09 09:52:17 +00:00
LanceandGitHub 59a75ce36c fix: add explicit session_id alias in ClickHouse session_data CTE (#9008) 2025-09-09 09:38:23 +00:00
Max DeichmannandGitHub 9a6a79f458 chore: add email to auth span (#9006) 2025-09-09 08:45:40 +02:00
marliessophieandGitHub 2598f44036 fix(dataset-router): correct datasetItemIds value assignment in dataset filtering logic (#8997) 2025-09-08 14:23:02 +00:00
Steffen SchmitzandGitHub f7e7b132dd feat: allow custom select filters in dashboard widget flow (#8995) 2025-09-08 14:13:10 +00:00
Hassieb PakzadGitHubellipsis-dev[bot] <65095814+ellipsis-dev[bot]@users.noreply.github.com>
521be4e0b6 fix(quickstart): update js examples (#8918)
* fix(quickstart): update js examples

* Update web/src/features/prompts/components/prompt-detail.tsx

Co-authored-by: ellipsis-dev[bot] <65095814+ellipsis-dev[bot]@users.noreply.github.com>

* push

---------

Co-authored-by: ellipsis-dev[bot] <65095814+ellipsis-dev[bot]@users.noreply.github.com>
2025-09-08 12:29:07 +00:00
Steffen SchmitzandGitHub a13b5d0d32 fix: use set utility for seen traces to fix cluster slot detection (#8990)
* fix: use set utility for seen traces to fix cluster slot detection

* chore: add review instruction
2025-09-08 12:08:26 +00:00
marliessophieandGitHub ebe557881c style(dataset-table-ui): reposition columns for run items making IO more prominent; drop repetitive columns (#8983)
* style(dataset-table-ui): reposition columns for run items making IO more prominent; drop repetitive columns

* chore: avoid triple negation

* chore: types

* fix: seeder

* fix: seeder
2025-09-08 09:55:57 +00:00
marliessophieandGitHub 5e8a9e3e80 feat(dataset-run-metrics): add total cost column to dataset runs table (#8970) 2025-09-08 08:38:16 +00:00
marliessophieandGitHub ab115cdc19 feat(table-ui): pin all select columns for y-/x-overflow (#8981) 2025-09-08 07:50:14 +00:00
Henrique CadioliandGitHub 18deee6434 chore: decouple upload file from url signing in storage service (#8878) 2025-09-08 07:37:32 +00:00
marliessophieandGitHub 977724a801 fix(dataset-run-items): depend on stable trpc properties (#8973) 2025-09-05 15:54:32 +00:00
marliessophieandGitHub 434c9be8a6 feat(dataset-runs-ui): fix select and name columns on y-/x-scroll (#8967)
* chore(table): rename isPinned to isFixedPosition

* chore(table): do not allow re-ordering fixed position columns

* refactor(table): update column visibility and order keys for dataset runs

* feat(dataset-runs): fix first two columns on y-/x-scroll

* chore: fixup

* chore: add comment
2025-09-05 14:49:07 +00:00
Michael FröhlichandGitHub 06bb3bbf43 fix(support): improve context in initial support email (#8966)
update plain Router
2025-09-05 14:04:04 +00:00
marliessophieandGitHub 736f4762bc fix(table-column-visibilty): show fixed position columns as unmoveable in visibility drawer (#8968)
* chore(table): rename isPinned to isFixedPosition

* chore(table): do not allow re-ordering fixed position columns
2025-09-05 13:57:23 +00:00
Leo WeigandandGitHub 4341b37ef3 fix(trace-detail): nested observations not expanded in timeline (#8965) 2025-09-05 13:03:04 +00:00
Nimar 95e07f2eb0 chore: release v3.108.0
Codespell / Check for spelling errors (push) Waiting to run
CI/CD / pre-job (push) Waiting to run
CI/CD / lint (push) Blocked by required conditions
CI/CD / prettier-check (push) Blocked by required conditions
CI/CD / test-docker-build (push) Blocked by required conditions
CI/CD / tests-web-sync (node24, pg12) (push) Blocked by required conditions
CI/CD / tests-web-sync (node24, pg15) (push) Blocked by required conditions
CI/CD / tests-web-async (node24, pg12, mode) (push) Blocked by required conditions
CI/CD / tests-web-async (node24, pg15, mode) (push) Blocked by required conditions
CI/CD / tests-web-async (node24, pg12, mode-azure) (push) Blocked by required conditions
CI/CD / tests-web-async (node24, pg15, mode-azure) (push) Blocked by required conditions
CI/CD / tests-web-async (node24, pg12, mode-redis-cluster) (push) Blocked by required conditions
CI/CD / tests-web-async (node24, pg15, mode-redis-cluster) (push) Blocked by required conditions
CI/CD / tests-worker (node24, pg12, mode) (push) Blocked by required conditions
CI/CD / tests-worker (node24, pg15, mode) (push) Blocked by required conditions
CI/CD / tests-worker (node24, pg12, mode-azure) (push) Blocked by required conditions
CI/CD / tests-worker (node24, pg15, mode-azure) (push) Blocked by required conditions
CI/CD / tests-worker (node24, pg12, mode-redis-cluster) (push) Blocked by required conditions
CI/CD / tests-worker (node24, pg15, mode-redis-cluster) (push) Blocked by required conditions
CI/CD / e2e-tests (push) Blocked by required conditions
CI/CD / e2e-server-tests (push) Blocked by required conditions
CI/CD / all-ci-passed (push) Blocked by required conditions
CI/CD / push-docker-image (push) Blocked by required conditions
release.yml / release (push) Waiting to run
2025-09-05 15:06:20 +02:00
Steffen SchmitzandGitHub 242e33c2f6 chore: skip otel processing on empty batch (#8964)
* chore: skip otel processing on empty batch

* chore: actually return on empty
2025-09-05 13:00:46 +00:00
Marc KlingenandGitHub 01aa0b9ecd chore: Update support.yml 2025-09-05 14:16:07 +02:00
Jannik MaierhöferandGitHub 3c0a4c863a feat(github-discussions): add structured form (#8953)
* feat(github-discussions): add structured form

* fix
2025-09-05 14:14:27 +02:00
NimarandGitHub d9954d14b1 fix(home): dashboard dark mode score analysis graph (#8961) 2025-09-05 13:57:22 +02:00
marliessophieandGitHub 7a2c5188cd fix(playground): show user-friendly toast notification for JSON prettification; do not report to sentry (#8957) 2025-09-05 11:28:08 +00:00
Leo WeigandandGitHub c7f62805c4 fix(tracing): spacings on single user page (#8954) 2025-09-05 09:34:02 +00:00
NimarandGitHub c23d1521ab fix(home): chart axis in dark mode (#8955)
* fix(home): dashboard axis description dark mode

* fix tooltip

* more compact
2025-09-05 11:25:46 +02:00
Michael FröhlichandGitHub 53b44454f6 feat(support): Send acknowledgement email to user on thread create (#8950)
* add reply-to call

* Add auto acknowledgement message

* add email version

* add demo version

* add reply-to logic

* bump message length
2025-09-05 09:04:32 +00:00
NimarandGitHub 8bf6044b24 fix(home): home screen dashboard text in darkmode barely visible 2 (#8951)
fix(home): home screen dashboard text in darkmode barely visible user consumption chart
2025-09-05 10:31:51 +02:00
marliessophieandGitHub a28877b9d9 chore(dataset-run-items): clean up deprecated code (#8949)
* chore(dataset-run-items): clean up deprecated code

* chore: fix

* chore: push
2025-09-05 07:59:51 +00:00
marliessophieandGitHub 902d8fc2f9 style(dataset-run-items): enhance dataset run name display (#8948) 2025-09-05 07:15:36 +00:00
marliessophieandGitHub 869851b974 perf(dataset-runs-metrics): utilise trace_id bloom filter in filtered observations (#8947) 2025-09-05 07:01:42 +00:00
NimarandGitHub f4118e451e fix(home): home screen dashboard text in darkmode barely visible (#8946) 2025-09-05 01:13:54 +02:00
Michael FröhlichandGitHub a8471adcea fix(support): improve form error and add new topic category (#8941)
* Add 'Other' category to topic select in support form

* enable submit button on uncomplete data and add form error
2025-09-04 20:01:04 +00:00
Max DeichmannandGitHub 2922387793 chore: revert turbo cache (#8938)
push
2025-09-04 19:21:01 +00:00
NimarandGitHub 94124ac60c fix(trace-timeline): cannot select items other than the first 2 (#8939) 2025-09-04 21:17:32 +02:00
NimarandGitHub 69c91c5988 fix(trace-ui): make tracetree scrollable again (#8936) 2025-09-04 20:31:21 +02:00
dbb97ca511 chore: improve billing portal navigation, 2 buttons to make it more obvious (#8921)
* Add billing portal buttons with update and cancel options

Co-authored-by: marc <marc@langfuse.com>

* Apply suggestions from code review

* chore: empty commit

---------

Co-authored-by: Cursor Agent <cursoragent@cursor.com>
2025-09-04 17:20:43 +00:00
Steffen SchmitzandGitHub 3e23f0c58e perf: upload otel ingestion payloads as batch to blob storage (#8893) 2025-09-04 19:15:09 +02:00
NimarandGitHub e5d3659b64 fix(prompts): clicking dataset run crashes ui (#8926)
* fix(prompts): clicking dataset run crashes ui

* update hook form

* upgrade responsive

* fix bug!
2025-09-04 16:56:51 +00:00
Leo WeigandandGitHub 7b6b8b176b fix(prompts): variables broken when copying prompt content (#8931) 2025-09-04 16:27:56 +00:00
marliessophieandGitHub 98151fa706 fix(dataset-run-items): pagination for single dataset run detail view (#8927)
* fix(dataset-run-items): pagination for single dataset run detail view

* chore: push
2025-09-04 16:14:26 +00:00
Leo WeigandandGitHub 0840ac1a9f fix(traces): tag assignment opening trace detail (#8928) 2025-09-04 15:39:34 +00:00
NimarandGitHub 7ad3818008 feat(agent-graphs): styling improvements (#8917)
* make it resizable

* grey not colourful

* graph direction

* basic keep graph in trace

* simplify

* no more

* cleanup
2025-09-04 15:21:33 +00:00
Steffen SchmitzandGitHub 3cbe3bd1bc chore: terminate tokenizer workers before freeing them from memory (#8925) 2025-09-04 14:37:40 +00:00
NimarandGitHub 0781cfb194 fix(trace): jumptoplayground can cause infinite loop (#8922) 2025-09-04 13:53:37 +00:00
Steffen SchmitzandGitHub 8c40cfc97d fix: make redis recently-processed cache cluster-safe (#8915)
* fix: make redis recently-processed cache cluster-safe

* chore: add explanation on Promise.all for seen events cache
2025-09-04 12:55:02 +00:00
Hassieb PakzadandGitHub 6bc996806b fix(users-table): metrics to include full observation count (#8914) 2025-09-04 14:34:52 +02:00
b9e54d2a19 build: add caches to CI pipeline (#8634)
* build: add caches to CI pipeline

* chore: cleanup

---------

Co-authored-by: Nimar <l.nimar.b@gmail.com>
2025-09-04 12:32:03 +00:00
NimarandGitHub 99fd7ca6ab fix(ci): specify types tightly to not block merge queue (#8916) 2025-09-04 13:36:59 +02:00
NimarGitHubSteffen SchmitzLeo Weigandellipsis-dev[bot] <65095814+ellipsis-dev[bot]@users.noreply.github.com>Marc Klingen
3e083656fa chore: upgrade to nextjs 15 & react 19 (#8861)
* chore: upgrade to nextjs v15

* update mui x-tree-view

* upgrade next themese

* update react-day-picker react-resizable-panels

* update sonner

* update next-query-params

* update react-email

* upgrade types react in shared

* upgrade next in ee

* next config remove now stable hook

* fix next-themes not exporting prop types directly anymore

* fix mui-x-tree peer dependency

* fix markdown viewer (ugly) to get around stricter typing

* add config to oltp protobus for nextjs15

* HACK: temp circumvent ApiRouteConfig typing

* TEMP: try nextjs15 workaround for SCIM

* try more ram

* not experimental no more

* moar memory

* update memory

* over 8000

* fix turbopack issues

* fix dockerfile

* default to turbo in web

* accept warnings for now

* cleanup

* cleanup

* remove tiktoken stuff

* chore: move obsevation type setting override to mapper (#8714)

* chore: move obsevation type setting override to mapper

* simplify mapper

* map nicer

* simplify

* feat(single-trace): add copy button on value cell of json view (#8871)

* fix: use leftUTF8 for input/output truncation on clickhouse (#8869)

* chose: use leftUTF8 for input/output truncation on clickhouse

* chore: skip truncation linebreak

* fix(trace-detail): guard against zero latency in trace tree (#8874)

fix(trace-detail): guard against zero latency

* chore: patch flaky trace repository test (#8870)

* chore: bump dd-trace to 5.65.0 (#8882)

* fix(prompts): subfolders hiding prompts (#8876)

* fix(prompts): subfolders hiding prompts

* Update web/src/features/prompts/server/routers/promptRouter.ts

Co-authored-by: ellipsis-dev[bot] <65095814+ellipsis-dev[bot]@users.noreply.github.com>

* fix da lint!

* fix

---------

Co-authored-by: ellipsis-dev[bot] <65095814+ellipsis-dev[bot]@users.noreply.github.com>

* fix(trace-single-ui): fix unavailable ref (#8883)

* fix(trace-single-ui): fix unavailable ref

* really fix it

* chore: release v3.106.4

* chore: add env note to review.md (#8888)

* feat(table-ui): filter score columns to show score only if has value for given table (#8865)

* feat(table-ui): filter score columns to show score only if has value for given table

* chore: refactor analytics

* chore: include dataset id in filter condition for DRI scores

* chore: fix tests

* chore: await data

* fixup-remove: upgrade to nextjs v15 (#8795)

* chore: simplify query

* chore: remove getRunScoresGroupedByNameSourceType

* chore: push

* fix: stabilize hook dependency

* Revert "fixup-remove: upgrade to nextjs v15 (#8795)"

This reverts commit 50b742325a82057f5dbfbe3f548d0610d3343f13.

* chore: eslint

---------

Co-authored-by: Nimar <l.nimar.b@gmail.com>

* update grid layout

* fix toggle

* ugly hack to make resizable work in turbopack

---------

Co-authored-by: Steffen Schmitz <steffen@langfuse.com>
Co-authored-by: Leo Weigand <5489276+leoweigand@users.noreply.github.com>
Co-authored-by: ellipsis-dev[bot] <65095814+ellipsis-dev[bot]@users.noreply.github.com>
Co-authored-by: Marc Klingen <git@marcklingen.com>
2025-09-04 12:55:49 +02:00
Michael FröhlichandGitHub ee80b102bc fix(support): ensure correct assignment of tenant id (#8909)
add orgid to client playload and select correct context for current tenant
2025-09-04 12:08:51 +02:00
Marc KlingenandGitHub 7dcd2ad30f ci: remove pr title check from merge_queue (#8904)
remove pr title check from merge_queue
2025-09-04 10:34:24 +02:00
Michael FröhlichandGitHub 43a98141a8 feat(support): revamp in-app support experience (#8875)
* Add support form and drawer scaffolding

* style changes

* fix flickering

* style support drawer into section

* update form

* refactor to use useForm hook

* add initial trpc router for creating chats

* add plainRouter and remove legacy chat

* refactor plain trpc route to fire and forget non-critical updates

* update success section

* add mobile support view

* make interaction type conditional

* add community section

* replace support-menu-dropdown with supportmenu-button

* add entitlement in-app-support to all cloud plans

* make plain call synchronous

* add file upload

* fix lint error

* remove unused code

* add first implementation of threadEvents

* remove auto-open on project create

* remove auto-open on tracing api generation

* remove comments

* remove old trpc route

* remove unused function

* simplify function return

* reduce input surface of createThread procedure

* simplified layout

* make drawer props more explicit

* remove duplicate threadFields

* remove misleading button

* refactor trpc router and plain client

* add support for uiCustomization

* remove plain chat from csp

* strict equality

* replace radio buttons
2025-09-04 08:16:05 +00:00
Nimar cf3505805d chore: release v3.107.0
Codespell / Check for spelling errors (push) Waiting to run
CI/CD / pre-job (push) Waiting to run
CI/CD / lint (push) Blocked by required conditions
CI/CD / prettier-check (push) Blocked by required conditions
CI/CD / test-docker-build (push) Blocked by required conditions
CI/CD / tests-web-sync (node24, pg12) (push) Blocked by required conditions
CI/CD / tests-web-sync (node24, pg15) (push) Blocked by required conditions
CI/CD / tests-web-async (node24, pg12, mode) (push) Blocked by required conditions
CI/CD / tests-web-async (node24, pg15, mode) (push) Blocked by required conditions
CI/CD / tests-web-async (node24, pg12, mode-azure) (push) Blocked by required conditions
CI/CD / tests-web-async (node24, pg15, mode-azure) (push) Blocked by required conditions
CI/CD / tests-web-async (node24, pg12, mode-redis-cluster) (push) Blocked by required conditions
CI/CD / tests-web-async (node24, pg15, mode-redis-cluster) (push) Blocked by required conditions
CI/CD / tests-worker (node24, pg12, mode) (push) Blocked by required conditions
CI/CD / tests-worker (node24, pg15, mode) (push) Blocked by required conditions
CI/CD / tests-worker (node24, pg12, mode-azure) (push) Blocked by required conditions
CI/CD / tests-worker (node24, pg15, mode-azure) (push) Blocked by required conditions
CI/CD / tests-worker (node24, pg12, mode-redis-cluster) (push) Blocked by required conditions
CI/CD / tests-worker (node24, pg15, mode-redis-cluster) (push) Blocked by required conditions
CI/CD / e2e-tests (push) Blocked by required conditions
CI/CD / e2e-server-tests (push) Blocked by required conditions
CI/CD / all-ci-passed (push) Blocked by required conditions
CI/CD / push-docker-image (push) Blocked by required conditions
release.yml / release (push) Waiting to run
2025-09-03 18:08:56 +02:00
NimarandGitHub 63fa75cc61 fix(ci): attempt fix title checker prevented push to docker (#8892)
* fix(ci): remove pr title checker

* add to pipeline
2025-09-03 17:22:02 +02:00
Steffen SchmitzandGitHub a02ef20deb perf: move tokenization in worker to worker thread (#8554)
* perf: move tokenization in worker to worker thread

* chore: linting

* chore: test timeouts

* duplicate worker file

* chore: linting

* chore: make async tokenization rate configurable

* chore: add note in worker-thread files on keeping them same

* chore: make pool size dynamic and add error fallback

* chore: pass text as is to tokenization

* chore: patch tracing behaviour

* chore: bump to 4 workers

* chore: linting

* chore: update span prop names

* chore: start with 2 workers
2025-09-03 12:57:57 +00:00
2deb588a57 feat(table-ui): filter score columns to show score only if has value for given table (#8865)
* feat(table-ui): filter score columns to show score only if has value for given table

* chore: refactor analytics

* chore: include dataset id in filter condition for DRI scores

* chore: fix tests

* chore: await data

* fixup-remove: upgrade to nextjs v15 (#8795)

* chore: simplify query

* chore: remove getRunScoresGroupedByNameSourceType

* chore: push

* fix: stabilize hook dependency

* Revert "fixup-remove: upgrade to nextjs v15 (#8795)"

This reverts commit 50b742325a82057f5dbfbe3f548d0610d3343f13.

* chore: eslint

---------

Co-authored-by: Nimar <l.nimar.b@gmail.com>
2025-09-03 12:08:35 +00:00
Marc KlingenandGitHub f04fd76f6c chore: add env note to review.md (#8888) 2025-09-03 13:24:35 +02:00
Nimar d022a07263 chore: release v3.106.4
Codespell / Check for spelling errors (push) Waiting to run
CI/CD / pre-job (push) Waiting to run
CI/CD / lint (push) Blocked by required conditions
CI/CD / prettier-check (push) Blocked by required conditions
CI/CD / test-docker-build (push) Blocked by required conditions
CI/CD / tests-web-sync (node24, pg12) (push) Blocked by required conditions
CI/CD / tests-web-sync (node24, pg15) (push) Blocked by required conditions
CI/CD / tests-web-async (node24, pg12, mode) (push) Blocked by required conditions
CI/CD / tests-web-async (node24, pg15, mode) (push) Blocked by required conditions
CI/CD / tests-web-async (node24, pg12, mode-azure) (push) Blocked by required conditions
CI/CD / tests-web-async (node24, pg15, mode-azure) (push) Blocked by required conditions
CI/CD / tests-web-async (node24, pg12, mode-redis-cluster) (push) Blocked by required conditions
CI/CD / tests-web-async (node24, pg15, mode-redis-cluster) (push) Blocked by required conditions
CI/CD / tests-worker (node24, pg12, mode) (push) Blocked by required conditions
CI/CD / tests-worker (node24, pg15, mode) (push) Blocked by required conditions
CI/CD / tests-worker (node24, pg12, mode-azure) (push) Blocked by required conditions
CI/CD / tests-worker (node24, pg15, mode-azure) (push) Blocked by required conditions
CI/CD / tests-worker (node24, pg12, mode-redis-cluster) (push) Blocked by required conditions
CI/CD / tests-worker (node24, pg15, mode-redis-cluster) (push) Blocked by required conditions
CI/CD / e2e-tests (push) Blocked by required conditions
CI/CD / e2e-server-tests (push) Blocked by required conditions
CI/CD / validate-pr-title (push) Blocked by required conditions
CI/CD / all-ci-passed (push) Blocked by required conditions
CI/CD / push-docker-image (push) Blocked by required conditions
release.yml / release (push) Waiting to run
2025-09-03 12:02:56 +02:00
NimarandGitHub 94e8f0b592 fix(trace-single-ui): fix unavailable ref (#8883)
* fix(trace-single-ui): fix unavailable ref

* really fix it
2025-09-03 12:02:16 +02:00
NimarGitHubellipsis-dev[bot] <65095814+ellipsis-dev[bot]@users.noreply.github.com>
5c8e4d641a fix(prompts): subfolders hiding prompts (#8876)
* fix(prompts): subfolders hiding prompts

* Update web/src/features/prompts/server/routers/promptRouter.ts

Co-authored-by: ellipsis-dev[bot] <65095814+ellipsis-dev[bot]@users.noreply.github.com>

* fix da lint!

* fix

---------

Co-authored-by: ellipsis-dev[bot] <65095814+ellipsis-dev[bot]@users.noreply.github.com>
2025-09-03 09:12:41 +00:00
Steffen SchmitzandGitHub 2eebca4e42 chore: bump dd-trace to 5.65.0 (#8882) 2025-09-03 08:37:27 +00:00
Steffen SchmitzandGitHub 426d4b77b6 chore: patch flaky trace repository test (#8870) 2025-09-03 08:21:23 +00:00
Leo WeigandandGitHub 1e3f345f5b fix(trace-detail): guard against zero latency in trace tree (#8874)
fix(trace-detail): guard against zero latency
2025-09-03 07:37:22 +00:00
Steffen SchmitzandGitHub 2d6e58136d fix: use leftUTF8 for input/output truncation on clickhouse (#8869)
* chose: use leftUTF8 for input/output truncation on clickhouse

* chore: skip truncation linebreak
2025-09-02 16:41:40 +00:00
NimarandGitHub cf15ee5066 feat(single-trace): add copy button on value cell of json view (#8871) 2025-09-02 16:26:20 +00:00
NimarandGitHub 816abfb59e chore: move obsevation type setting override to mapper (#8714)
* chore: move obsevation type setting override to mapper

* simplify mapper

* map nicer

* simplify
2025-09-02 13:20:22 +00:00
Marc KlingenandGitHub 702dd6ac27 ci: check pr title for conventional commit (#8862) 2025-09-02 14:18:18 +02:00
GrégoireandGitHub 14fbeb1788 Map gen_ai.input/output.messages to input/output (#8813) 2025-09-02 06:35:58 +00:00
Nimar fbf6110acf chore: release v3.106.3
Codespell / Check for spelling errors (push) Waiting to run
CI/CD / pre-job (push) Waiting to run
CI/CD / lint (push) Blocked by required conditions
CI/CD / prettier-check (push) Blocked by required conditions
CI/CD / test-docker-build (push) Blocked by required conditions
CI/CD / tests-web-sync (node24, pg12) (push) Blocked by required conditions
CI/CD / tests-web-sync (node24, pg15) (push) Blocked by required conditions
CI/CD / tests-web-async (node24, pg12, mode) (push) Blocked by required conditions
CI/CD / tests-web-async (node24, pg15, mode) (push) Blocked by required conditions
CI/CD / tests-web-async (node24, pg12, mode-azure) (push) Blocked by required conditions
CI/CD / tests-web-async (node24, pg15, mode-azure) (push) Blocked by required conditions
CI/CD / tests-web-async (node24, pg12, mode-redis-cluster) (push) Blocked by required conditions
CI/CD / tests-web-async (node24, pg15, mode-redis-cluster) (push) Blocked by required conditions
CI/CD / tests-worker (node24, pg12, mode) (push) Blocked by required conditions
CI/CD / tests-worker (node24, pg15, mode) (push) Blocked by required conditions
CI/CD / tests-worker (node24, pg12, mode-azure) (push) Blocked by required conditions
CI/CD / tests-worker (node24, pg15, mode-azure) (push) Blocked by required conditions
CI/CD / tests-worker (node24, pg12, mode-redis-cluster) (push) Blocked by required conditions
CI/CD / tests-worker (node24, pg15, mode-redis-cluster) (push) Blocked by required conditions
CI/CD / e2e-tests (push) Blocked by required conditions
CI/CD / e2e-server-tests (push) Blocked by required conditions
CI/CD / all-ci-passed (push) Blocked by required conditions
CI/CD / push-docker-image (push) Blocked by required conditions
release.yml / release (push) Waiting to run
2025-09-01 20:54:19 +02:00
NimarandGitHub ef950ae381 Revert "chore: upgrade to nextjs v15" (#8853)
Revert "chore: upgrade to nextjs v15 (#8795)"

This reverts commit c24ac641e0.
2025-09-01 20:53:42 +02:00
Nimar 79762e8eb0 chore: release v3.106.2
Codespell / Check for spelling errors (push) Waiting to run
CI/CD / pre-job (push) Waiting to run
CI/CD / lint (push) Blocked by required conditions
CI/CD / prettier-check (push) Blocked by required conditions
CI/CD / test-docker-build (push) Blocked by required conditions
CI/CD / tests-web-sync (node24, pg12) (push) Blocked by required conditions
CI/CD / tests-web-sync (node24, pg15) (push) Blocked by required conditions
CI/CD / tests-web-async (node24, pg12, mode) (push) Blocked by required conditions
CI/CD / tests-web-async (node24, pg15, mode) (push) Blocked by required conditions
CI/CD / tests-web-async (node24, pg12, mode-azure) (push) Blocked by required conditions
CI/CD / tests-web-async (node24, pg15, mode-azure) (push) Blocked by required conditions
CI/CD / tests-web-async (node24, pg12, mode-redis-cluster) (push) Blocked by required conditions
CI/CD / tests-web-async (node24, pg15, mode-redis-cluster) (push) Blocked by required conditions
CI/CD / tests-worker (node24, pg12, mode) (push) Blocked by required conditions
CI/CD / tests-worker (node24, pg15, mode) (push) Blocked by required conditions
CI/CD / tests-worker (node24, pg12, mode-azure) (push) Blocked by required conditions
CI/CD / tests-worker (node24, pg15, mode-azure) (push) Blocked by required conditions
CI/CD / tests-worker (node24, pg12, mode-redis-cluster) (push) Blocked by required conditions
CI/CD / tests-worker (node24, pg15, mode-redis-cluster) (push) Blocked by required conditions
CI/CD / e2e-tests (push) Blocked by required conditions
CI/CD / e2e-server-tests (push) Blocked by required conditions
CI/CD / all-ci-passed (push) Blocked by required conditions
CI/CD / push-docker-image (push) Blocked by required conditions
release.yml / release (push) Waiting to run
2025-09-01 17:43:45 +02:00
NimarandGitHub c24ac641e0 chore: upgrade to nextjs v15 (#8795)
* chore: upgrade to nextjs v15

* update mui x-tree-view

* upgrade next themese

* update react-day-picker react-resizable-panels

* update sonner

* update next-query-params

* update react-email

* upgrade types react in shared

* upgrade next in ee

* next config remove now stable hook

* fix next-themes not exporting prop types directly anymore

* fix mui-x-tree peer dependency

* fix markdown viewer (ugly) to get around stricter typing

* add config to oltp protobus for nextjs15

* HACK: temp circumvent ApiRouteConfig typing

* TEMP: try nextjs15 workaround for SCIM

* try more ram

* not experimental no more

* moar memory

* update memory

* over 8000

* fix turbopack issues

* fix dockerfile

* default to turbo in web

* accept warnings for now

* cleanup

* cleanup

* remove tiktoken stuff
2025-09-01 15:08:22 +00:00
marliessophieandGitHub 9464e9bfbe fix(datasets): do not rely on pagination in compare view (#8842)
* fix(datasets): do not rely on pagination in compare view

* fixup: test

* fixup: test

* fixup: test
2025-09-01 12:57:25 +00:00
Marc KlingenandGitHub 2893fc44db chore: mention in docs that node 24 is required now (#8846)
* chore: mention in docs that node 24 is required now

* push
2025-09-01 15:28:45 +02:00
marliessophieandGitHub 6c0ecf75d0 chore(dataset-remote-run): validate remote run URL (#8835)
chore(dataset-remote-run): validate remote run URL in dataset router
2025-09-01 12:55:46 +00:00
Marc KlingenandGitHub 56ee375a71 feat(cloud): export onboarding survey responses to s3 (#8841)
feat: export survey responses to s3
2025-09-01 12:11:45 +00:00
marliessophieandGitHub a54452e1c7 style(dataset-compare): improve layout and visibility of scores and resource metrics sections in output cells (#8839) 2025-09-01 09:21:18 +00:00
Srijan DubeyandGitHub de4a158efb Fix/keyboard shortcuts modifier handling (#8824)
* fix(keyboard): prevent list/sidebar actions when Cmd+K triggers global search

* fix(keyboard): refine shortcut handling by removing unnecessary modifier checks
2025-09-01 08:00:03 +00:00
Max DeichmannandGitHub 44ce6ef581 chore: validate IP adresses (#8821)
* chore: validate IP adresses

* chore: validate IP adresses

* chore: validate IP adresses

* chore: validate IP adresses

* chore: validate IP adresses

* chore: validate IP adresses

* chore: validate IP adresses

* chore: validate IP adresses

* chore: validate IP adresses

* chore: validate IP adresses

* chore: validate IP adresses

* chore: validate IP adresses

* chore: validate IP adresses

* chore: validate IP adresses
2025-08-29 16:42:56 +00:00
Nimar 65fc850d96 chore: release v3.106.1
Codespell / Check for spelling errors (push) Waiting to run
CI/CD / pre-job (push) Waiting to run
CI/CD / lint (push) Blocked by required conditions
CI/CD / prettier-check (push) Blocked by required conditions
CI/CD / test-docker-build (push) Blocked by required conditions
CI/CD / tests-web-sync (node24, pg12) (push) Blocked by required conditions
CI/CD / tests-web-sync (node24, pg15) (push) Blocked by required conditions
CI/CD / tests-web-async (node24, pg12, mode) (push) Blocked by required conditions
CI/CD / tests-web-async (node24, pg15, mode) (push) Blocked by required conditions
CI/CD / tests-web-async (node24, pg12, mode-azure) (push) Blocked by required conditions
CI/CD / tests-web-async (node24, pg15, mode-azure) (push) Blocked by required conditions
CI/CD / tests-web-async (node24, pg12, mode-redis-cluster) (push) Blocked by required conditions
CI/CD / tests-web-async (node24, pg15, mode-redis-cluster) (push) Blocked by required conditions
CI/CD / tests-worker (node24, pg12, mode) (push) Blocked by required conditions
CI/CD / tests-worker (node24, pg15, mode) (push) Blocked by required conditions
CI/CD / tests-worker (node24, pg12, mode-azure) (push) Blocked by required conditions
CI/CD / tests-worker (node24, pg15, mode-azure) (push) Blocked by required conditions
CI/CD / tests-worker (node24, pg12, mode-redis-cluster) (push) Blocked by required conditions
CI/CD / tests-worker (node24, pg15, mode-redis-cluster) (push) Blocked by required conditions
CI/CD / e2e-tests (push) Blocked by required conditions
CI/CD / e2e-server-tests (push) Blocked by required conditions
CI/CD / all-ci-passed (push) Blocked by required conditions
CI/CD / push-docker-image (push) Blocked by required conditions
release.yml / release (push) Waiting to run
2025-08-29 17:11:39 +02:00
NimarandGitHub af35c65d06 fix: infinite recursion on graph render (#8822) 2025-08-29 15:07:34 +00:00
c0243c6703 fix(ui): docs link for remote dataset run (#8818)
Update docs link for dataset runs to remote run page

Co-authored-by: Cursor Agent <cursoragent@cursor.com>
2025-08-29 14:49:05 +02:00
Max DeichmannandGitHub db11e91539 chore: add evals admin api (#8803) 2025-08-29 00:52:45 +02:00
Max DeichmannandGitHub 9aec4081e1 chore: fix eval attribute assignment (#8802)
push
2025-08-28 23:35:39 +02:00
Max DeichmannandGitHub e6e40403fa chore: Improve trace by id ch tagging (#8799)
* push

* chore: add span metrics for eval executions

* chore: add span metrics for eval executions

* push
2025-08-28 19:48:13 +00:00
Marc KlingenandGitHub 435b19098c chore: bump plain sdk 5.10.3 (#8797) 2025-08-28 17:02:27 +00:00
Nimar 753a716724 chore: release v3.106.0
Codespell / Check for spelling errors (push) Waiting to run
CI/CD / pre-job (push) Waiting to run
CI/CD / lint (push) Blocked by required conditions
CI/CD / prettier-check (push) Blocked by required conditions
CI/CD / test-docker-build (push) Blocked by required conditions
CI/CD / tests-web-sync (node24, pg12) (push) Blocked by required conditions
CI/CD / tests-web-sync (node24, pg15) (push) Blocked by required conditions
CI/CD / tests-web-async (node24, pg12, mode) (push) Blocked by required conditions
CI/CD / tests-web-async (node24, pg15, mode) (push) Blocked by required conditions
CI/CD / tests-web-async (node24, pg12, mode-azure) (push) Blocked by required conditions
CI/CD / tests-web-async (node24, pg15, mode-azure) (push) Blocked by required conditions
CI/CD / tests-web-async (node24, pg12, mode-redis-cluster) (push) Blocked by required conditions
CI/CD / tests-web-async (node24, pg15, mode-redis-cluster) (push) Blocked by required conditions
CI/CD / tests-worker (node24, pg12, mode) (push) Blocked by required conditions
CI/CD / tests-worker (node24, pg15, mode) (push) Blocked by required conditions
CI/CD / tests-worker (node24, pg12, mode-azure) (push) Blocked by required conditions
CI/CD / tests-worker (node24, pg15, mode-azure) (push) Blocked by required conditions
CI/CD / tests-worker (node24, pg12, mode-redis-cluster) (push) Blocked by required conditions
CI/CD / tests-worker (node24, pg15, mode-redis-cluster) (push) Blocked by required conditions
CI/CD / e2e-tests (push) Blocked by required conditions
CI/CD / e2e-server-tests (push) Blocked by required conditions
CI/CD / all-ci-passed (push) Blocked by required conditions
CI/CD / push-docker-image (push) Blocked by required conditions
release.yml / release (push) Waiting to run
2025-08-28 15:57:53 +02:00
Marc KlingenandGitHub ce7ac9888e fix: cursor background agent dev setup (#8728) 2025-08-28 15:34:12 +02:00
NimarandGitHub 8f1a202226 feat(tracing): graph views for new observation types (#8690)
* feat(trace-graph): more generalized availability for graphs

* update

* simplify

* some cleanup

* simplify

* optimize

* fix typescript

* simplify

* physics again

* simplify

* fix color

* fix parallel node display in langgraph

* slightly optimize

* farben

* restructure

* some optimizations

* optimize

* cleanup

* fix do graph detection

* cleanup

* cleanup

* final

* fiux test
2025-08-28 13:13:50 +00:00
Hassieb Pakzad 3ba29710b2 chore: release v3.105.0
Codespell / Check for spelling errors (push) Waiting to run
CI/CD / pre-job (push) Waiting to run
CI/CD / lint (push) Blocked by required conditions
CI/CD / prettier-check (push) Blocked by required conditions
CI/CD / test-docker-build (push) Blocked by required conditions
CI/CD / tests-web-sync (node24, pg12) (push) Blocked by required conditions
CI/CD / tests-web-sync (node24, pg15) (push) Blocked by required conditions
CI/CD / tests-web-async (node24, pg12, mode) (push) Blocked by required conditions
CI/CD / tests-web-async (node24, pg15, mode) (push) Blocked by required conditions
CI/CD / tests-web-async (node24, pg12, mode-azure) (push) Blocked by required conditions
CI/CD / tests-web-async (node24, pg15, mode-azure) (push) Blocked by required conditions
CI/CD / tests-web-async (node24, pg12, mode-redis-cluster) (push) Blocked by required conditions
CI/CD / tests-web-async (node24, pg15, mode-redis-cluster) (push) Blocked by required conditions
CI/CD / tests-worker (node24, pg12, mode) (push) Blocked by required conditions
CI/CD / tests-worker (node24, pg15, mode) (push) Blocked by required conditions
CI/CD / tests-worker (node24, pg12, mode-azure) (push) Blocked by required conditions
CI/CD / tests-worker (node24, pg15, mode-azure) (push) Blocked by required conditions
CI/CD / tests-worker (node24, pg12, mode-redis-cluster) (push) Blocked by required conditions
CI/CD / tests-worker (node24, pg15, mode-redis-cluster) (push) Blocked by required conditions
CI/CD / e2e-tests (push) Blocked by required conditions
CI/CD / e2e-server-tests (push) Blocked by required conditions
CI/CD / all-ci-passed (push) Blocked by required conditions
CI/CD / push-docker-image (push) Blocked by required conditions
release.yml / release (push) Waiting to run
2025-08-28 09:49:02 +02:00
marliessophieandGitHub dafda00e9d chore(annotation-queues-ui): show Source ID as column on Annotation Queue Items Table (#8779) 2025-08-28 06:01:40 +00:00
marliessophieandGitHub f2bcf2f5cc fix(evalQueue): update timeout error handling to match case sensitivity (#8752) (#8777) 2025-08-27 22:47:15 +00:00
marliessophieandGitHub 1acef7c0e8 chore(peek): navigation event to create browser history entry (#8772) 2025-08-27 17:49:43 +00:00
marliessophieandGitHub e6b4af4004 feat(dataset-runs): support score filters in ui tables (#8755)
* fixup(dataset-runs): support score filters

* chore: remove dataset run level score filters

* fixup: adjust DR query

* chore: add working score filters for dataset metrics

* chore: working version of score filters

* chore: fix tests

* test: add filter tests

* fix: add timestamps to remove test flakyness

* chore: rename score filters

* chore: drop aggregated prefix for score columns

* chore: make trpc changes safe

* chore: add ordering ASC to dataset run metrics

* chore: convert ordering to DESC
2025-08-27 17:18:05 +00:00
marliessophieandGitHub 414d950fa0 chore(datasets): enhance description display in table UI (#8771) 2025-08-27 17:09:34 +00:00
Max DeichmannandGitHub f49a20b489 perf: remove traces env filter form observations table (#8768)
push
2025-08-27 15:17:38 +00:00
Hassieb PakzadandGitHub 3c2e963bfb fix(traces-api): include excluded fields in response with empty values (#8753)
* fix(traces-api): include excluded fields in response with empty values

* fix tests
2025-08-27 12:39:27 +00:00
Marc KlingenandGitHub 8769b65157 chore(cloud): show label that demo is not available only on HIPAA data region (#8764) 2025-08-27 12:23:17 +00:00
Max Deichmann 231a8f82bc chore: release v3.104.0
Codespell / Check for spelling errors (push) Waiting to run
CI/CD / pre-job (push) Waiting to run
CI/CD / lint (push) Blocked by required conditions
CI/CD / prettier-check (push) Blocked by required conditions
CI/CD / test-docker-build (push) Blocked by required conditions
CI/CD / tests-web-sync (node24, pg12) (push) Blocked by required conditions
CI/CD / tests-web-sync (node24, pg15) (push) Blocked by required conditions
CI/CD / tests-web-async (node24, pg12, mode) (push) Blocked by required conditions
CI/CD / tests-web-async (node24, pg15, mode) (push) Blocked by required conditions
CI/CD / tests-web-async (node24, pg12, mode-azure) (push) Blocked by required conditions
CI/CD / tests-web-async (node24, pg15, mode-azure) (push) Blocked by required conditions
CI/CD / tests-web-async (node24, pg12, mode-redis-cluster) (push) Blocked by required conditions
CI/CD / tests-web-async (node24, pg15, mode-redis-cluster) (push) Blocked by required conditions
CI/CD / tests-worker (node24, pg12, mode) (push) Blocked by required conditions
CI/CD / tests-worker (node24, pg15, mode) (push) Blocked by required conditions
CI/CD / tests-worker (node24, pg12, mode-azure) (push) Blocked by required conditions
CI/CD / tests-worker (node24, pg15, mode-azure) (push) Blocked by required conditions
CI/CD / tests-worker (node24, pg12, mode-redis-cluster) (push) Blocked by required conditions
CI/CD / tests-worker (node24, pg15, mode-redis-cluster) (push) Blocked by required conditions
CI/CD / e2e-tests (push) Blocked by required conditions
CI/CD / e2e-server-tests (push) Blocked by required conditions
CI/CD / all-ci-passed (push) Blocked by required conditions
CI/CD / push-docker-image (push) Blocked by required conditions
release.yml / release (push) Waiting to run
2025-08-27 11:58:38 +02:00
Max DeichmannandGitHub a933bf68fb chore: fix log comments (#8760) 2025-08-27 08:45:52 +02:00
Max DeichmannandGitHub 21a16963e2 chore: add log comments ch write (#8758)
push
2025-08-27 06:28:52 +00:00
marliessophieandGitHub fe6c647c2b chore(evals): graceful failure in case of timeouts of LLM providers (#8752) 2025-08-26 21:02:43 +00:00
marliessophieandGitHub f1e5f35659 chore(dataset-run-items): remove PG execution code (#8732)
* chore(dataset-run-items): remove PG execution code

* chore: remove all dri postgres code

* chore: fix return

* chore: remove env variables

* chore: drop unused exports

* remove: env's from test env configs
2025-08-26 15:42:15 +00:00
Max DeichmannandGitHub d39f7ba6e5 chore: upgrade to node 24 (#8734) 2025-08-26 17:06:04 +02:00
NimarandGitHub f701562396 fix: make chain coloring unique (#8749) 2025-08-26 13:57:56 +02:00
marliessophieandGitHub 83cbd336e2 fix(runs-compare): infinite re-renders on data table (#8744) 2025-08-26 13:02:29 +02:00
marliessophieandGitHub ec33992412 test(datasets): skip flaky dataset runs test in CI (#8738) 2025-08-25 19:06:04 +00:00
Marc KlingenandGitHub 4e67edaaff fix: cursor bg agent clock scew (skip check) (#8737)
push
2025-08-25 20:01:02 +02:00
marliessophieandGitHub ac3ee18373 chore(dataset-run-items-seeder): remove PG dataset run item creation path (#8733)
* chore(dataset-run-items-seeder): remove PG dataset run item creation path

* chore: drop import

* chore: increase timeout
2025-08-25 16:42:49 +00:00
Hassieb PakzadandGitHub 7f646757c5 feat(otel): parse vercel ai SDK ai.usage.* (#8730) 2025-08-25 14:53:15 +00:00
marliessophieandGitHub 2d82ae6e86 chore(dataset-runs): improve loading animations on runs table (#8719) 2025-08-25 13:04:46 +00:00
629 changed files with 60727 additions and 20669 deletions
+1 -1
View File
@@ -1,4 +1,4 @@
[codespell]
skip = .git,*.pdf,*.svg,package-lock.json,*.prisma,pnpm-lock.yaml
ignore-words-list = afterall,vertx,notIn,alue
ignore-words-list = afterall,vertx,notIn,alue,allTime
-55
View File
@@ -1,55 +0,0 @@
FROM node:20
# ---------- System packages --------------------------------------------------
# The buildpack-deps base already ships git, build-essential, python, etc.
# Add a few extra tools handy during Langfuse development.
RUN apt-get update --option=Acquire::Check-Valid-Until=false && \
DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends \
openssl \
wget \
curl \
ca-certificates \
postgresql-client \
redis-tools \
less nano \
sudo \
&& rm -rf /var/lib/apt/lists/*
# ---------- Docker -----------------------------------------------------------
# Install Docker for background agents that need container capabilities
RUN curl -fsSL https://get.docker.com -o get-docker.sh && \
sh get-docker.sh && \
rm get-docker.sh
# ---------- pnpm -------------------------------------------------------------
# Langfuse monorepo relies on pnpm 9.5.0 (see CONTRIBUTING.md)
ENV PNPM_HOME="/pnpm"
ENV PATH="$PNPM_HOME:$PATH"
RUN corepack enable && \
corepack prepare pnpm@9.5.0 --activate
# ---------- golang-migrate ----------------------------------------------------
# CLI used for database migrations during development.
ENV MIGRATE_VERSION=4.18.3
RUN wget -qO- "https://github.com/golang-migrate/migrate/releases/download/v${MIGRATE_VERSION}/migrate.linux-amd64.tar.gz" \
| tar -xz -C /usr/local/bin && \
chmod +x /usr/local/bin/migrate
# ---------- Non-root user -----------------------------------------------------
# Create non-root user with sudo privileges and docker group access
RUN useradd -ms /bin/bash ubuntu && \
usermod -aG sudo ubuntu && \
usermod -aG docker ubuntu && \
echo "ubuntu ALL=(ALL) NOPASSWD:ALL" >> /etc/sudoers
# Pre-create pnpm store and set correct ownership to avoid first-run cost & permission issues
RUN pnpm store path > /dev/null && \
chown -R ubuntu:ubuntu /pnpm
USER ubuntu
WORKDIR /home/ubuntu
ENV HOME=/home/ubuntu
ENV TERM=xterm-256color
# Container starts with a bash shell ready for hacking.
CMD ["bash"]
+4 -7
View File
@@ -1,14 +1,11 @@
{
"agentCanUpdateSnapshot": false,
"install": "cp .env.dev.example .env && pnpm i",
"build": {
"context": ".",
"dockerfile": "Dockerfile"
},
"start": "sudo service docker start",
"terminals": [
{
"name": "dev server",
"command": "pnpm run dx-f"
"name": "Development Terminal",
"command": "pnpm run dev",
"description": "Main development terminal running the development server"
}
]
}
+1 -1
View File
@@ -2,7 +2,7 @@
FROM mcr.microsoft.com/vscode/devcontainers/typescript-node:20-bookworm
# Install golang-migrate for database migrations
RUN curl -L https://github.com/golang-migrate/migrate/releases/download/v4.18.3/migrate.linux-amd64.tar.gz | tar xvz && \
RUN curl -L https://github.com/golang-migrate/migrate/releases/download/v4.19.0/migrate.linux-amd64.tar.gz | tar xvz && \
chmod +x migrate && \
mv migrate /usr/local/bin/migrate
-3
View File
@@ -83,6 +83,3 @@ ENCRYPTION_KEY=0000000000000000000000000000000000000000000000000000000000000000
# speeds up local development by not executing init scripts on server startup
NEXT_PUBLIC_LANGFUSE_RUN_NEXT_INIT="false"
LANGFUSE_EXPERIMENT_DATASET_RUN_ITEMS_WRITE_CH=true
LANGFUSE_EXPERIMENT_DATASET_RUN_ITEMS_READ_CH=true
+1 -7
View File
@@ -76,6 +76,7 @@ REDIS_AUTH="bitnami"
REDIS_CLUSTER_ENABLED="true"
REDIS_CLUSTER_NODES="127.0.0.1:6370,127.0.0.1:6371,127.0.0.1:6372,127.0.0.1:6373,127.0.0.1:6374,127.0.0.1:6375"
LANGFUSE_INGESTION_QUEUE_SHARD_COUNT=8
LANGFUSE_OTEL_INGESTION_QUEUE_SHARD_COUNT=4
LANGFUSE_TRACE_UPSERT_QUEUE_SHARD_COUNT=4
# openssl rand -hex 32 used only here
@@ -84,10 +85,3 @@ ENCRYPTION_KEY=0000000000000000000000000000000000000000000000000000000000000000
# speeds up local development by not executing init scripts on server startup
NEXT_PUBLIC_LANGFUSE_RUN_NEXT_INIT="false"
# Use the following settings to enforce running the new AMTs during the tests
LANGFUSE_EXPERIMENT_INSERT_INTO_AGGREGATING_MERGE_TREES="true"
LANGFUSE_EXPERIMENT_INSERT_INTO_TRACES_TABLE="false"
LANGFUSE_EXPERIMENT_RETURN_NEW_RESULT="true"
LANGFUSE_EXPERIMENT_DATASET_RUN_ITEMS_WRITE_CH=true
LANGFUSE_EXPERIMENT_DATASET_RUN_ITEMS_READ_CH=true
+12 -2
View File
@@ -33,6 +33,7 @@ SALT="salt"
# Email
EMAIL_FROM_ADDRESS="" # Defines the email address to use as the from address.
SMTP_CONNECTION_URL="" # Defines the connection url for smtp server.
CLOUD_CRM_EMAIL="" # Optional BCC address for usage threshold emails (e.g., for CRM integration like HubSpot)
# S3 Batch Exports
LANGFUSE_S3_BATCH_EXPORT_ENABLED=true
@@ -89,5 +90,14 @@ SLACK_CLIENT_ID=your_slack_client_id
SLACK_CLIENT_SECRET=your_slack_client_secret
SLACK_STATE_SECRET=your_slack_state_secret
LANGFUSE_EXPERIMENT_DATASET_RUN_ITEMS_WRITE_CH=true
LANGFUSE_EXPERIMENT_DATASET_RUN_ITEMS_READ_CH=true
# Langfuse AI instance for tracing, prompts
LANGFUSE_AI_FEATURES_PUBLIC_KEY="pk-lf-1234567890"
LANGFUSE_AI_FEATURES_SECRET_KEY="sk-lf-1234567890"
LANGFUSE_AI_FEATURES_HOST="http://localhost:3000"
LANGFUSE_AI_FEATURES_PROJECT_ID=7a88fb47-b4e2-43b8-a06c-a5ce950dc53a
# Langfuse AI Bedrock credentials
AWS_ACCESS_KEY_ID="A123456789"
AWS_SECRET_ACCESS_KEY="SAK123456789"
LANGFUSE_AWS_BEDROCK_REGION="eu-west-1"
LANGFUSE_AWS_BEDROCK_MODEL="eu.anthropic.claude-3-haiku-20240307-v1:0"
+8
View File
@@ -270,6 +270,14 @@ LANGFUSE_ENABLE_BLOB_STORAGE_FILE_LOG=true
# Rate limiting
# LANGFUSE_RATE_LIMITS_ENABLED=
# Free tier usage thresholds (Cloud deployments only)
# Enable the queue consumer that monitors free tier usage (default: true, but requires cloud region)
# QUEUE_CONSUMER_FREE_TIER_USAGE_THRESHOLD_QUEUE_IS_ENABLED=true
# Enable enforcement: send emails and block orgs that exceed free tier limits (default: false)
# LANGFUSE_FREE_TIER_USAGE_THRESHOLD_ENFORCEMENT_ENABLED=false
# Optional BCC address for usage threshold emails (e.g., for CRM integration like HubSpot)
# CLOUD_CRM_EMAIL=
# Stripe
# STRIPE_SECRET_KEY=
# STRIPE_WEBHOOK_SIGNING_SECRET=
+21
View File
@@ -0,0 +1,21 @@
body:
- type: textarea
attributes:
label: Describe your question
description: Please describe the question you have as clear and concise as possible.
validations:
required: true
- type: dropdown
id: hosting
attributes:
label: Are you using Langfuse Cloud or self-host Langfuse?
options:
- Langfuse Cloud
- Self-hosted Langfuse
validations:
required: true
- type: checkboxes
attributes:
label: I checked if there is already an [issue](https://github.com/langfuse/langfuse/issues) or [discussion](https://github.com/orgs/langfuse/discussions) for my question and asked the [Langfuse AI](https://langfuse.com/docs/ask-ai) for help.
validations:
required: true
+2 -2
View File
@@ -6,7 +6,7 @@ on:
workflow_dispatch:
inputs:
service:
description: 'Service to be deployed'
description: "Service to be deployed"
type: choice
options:
- all
@@ -15,7 +15,7 @@ on:
- worker
required: true
environment:
description: 'Environment to deploy to'
description: "Environment to deploy to"
type: choice
options:
- staging
+99 -17
View File
@@ -40,7 +40,7 @@ jobs:
version: 9.5.0
- uses: actions/setup-node@v4
with:
node-version: 20
node-version: 24
cache: "pnpm"
cache-dependency-path: "pnpm-lock.yaml"
- name: install dependencies
@@ -66,7 +66,7 @@ jobs:
version: 9.5.0
- uses: actions/setup-node@v4
with:
node-version: 20
node-version: 24
cache: "pnpm"
cache-dependency-path: "pnpm-lock.yaml"
- name: install dependencies
@@ -82,12 +82,12 @@ jobs:
else
BASE_SHA=$(git merge-base origin/main HEAD)
fi
echo "Checking files changed from $BASE_SHA to HEAD"
# Get changed files
CHANGED_FILES=$(git diff --name-only $BASE_SHA HEAD -- '*.js' '*.jsx' '*.ts' '*.tsx' '*.css' | tr '\n' ' ')
if [ -n "$CHANGED_FILES" ] && [ "$CHANGED_FILES" != " " ]; then
echo "Files to check: $CHANGED_FILES"
pnpm prettier --check --experimental-cli $CHANGED_FILES
@@ -142,13 +142,13 @@ jobs:
name: tests-web-sync (node${{ matrix.node-version }}, pg${{ matrix.postgres-version }})
strategy:
matrix:
node-version: [20]
node-version: [24]
postgres-version: [12, 15]
steps:
- uses: actions/checkout@v4
- name: Install golang-migrate for Clickhouse migrations
run: |
curl -L https://github.com/golang-migrate/migrate/releases/download/v4.18.3/migrate.linux-amd64.tar.gz | tar xvz
curl -L https://github.com/golang-migrate/migrate/releases/download/v4.19.0/migrate.linux-amd64.tar.gz | tar xvz
sudo mv migrate /usr/bin/migrate
which migrate
- uses: pnpm/action-setup@v3
@@ -189,6 +189,8 @@ jobs:
pnpm --filter=shared ch:up
- name: Build
run: pnpm run build
env:
NODE_OPTIONS: --max_old_space_size=8192
- name: Start Langfuse
run: (pnpm run start&)
env:
@@ -216,14 +218,14 @@ jobs:
name: tests-web-async (node${{ matrix.node-version }}, pg${{ matrix.postgres-version }}, mode${{ matrix.deploy-mode }})
strategy:
matrix:
node-version: [20]
node-version: [24]
postgres-version: [12, 15]
deploy-mode: ["", "-azure", "-redis-cluster"]
steps:
- uses: actions/checkout@v4
- name: Install golang-migrate for Clickhouse migrations
run: |
curl -L https://github.com/golang-migrate/migrate/releases/download/v4.18.3/migrate.linux-amd64.tar.gz | tar xvz
curl -L https://github.com/golang-migrate/migrate/releases/download/v4.19.0/migrate.linux-amd64.tar.gz | tar xvz
sudo mv migrate /usr/bin/migrate
which migrate
- uses: pnpm/action-setup@v3
@@ -266,6 +268,8 @@ jobs:
pnpm --filter=shared ch:up
- name: Build
run: pnpm run build
env:
NODE_OPTIONS: --max_old_space_size=8192
- name: Start Langfuse
run: (pnpm run start&)
env:
@@ -291,7 +295,7 @@ jobs:
name: tests-worker (node${{ matrix.node-version }}, pg${{ matrix.postgres-version }}, mode${{ matrix.deploy-mode }})
strategy:
matrix:
node-version: [20]
node-version: [24]
postgres-version: [12, 15]
deploy-mode: ["", "-azure", "-redis-cluster"]
steps:
@@ -316,7 +320,7 @@ jobs:
pnpm install
- name: Install golang-migrate for Clickhouse migrations
run: |
curl -L https://github.com/golang-migrate/migrate/releases/download/v4.18.3/migrate.linux-amd64.tar.gz | tar xvz
curl -L https://github.com/golang-migrate/migrate/releases/download/v4.19.0/migrate.linux-amd64.tar.gz | tar xvz
sudo mv migrate /usr/bin/migrate
which migrate
- name: Load default env
@@ -345,7 +349,80 @@ jobs:
- name: Build
run: pnpm --filter=worker... run build
- name: run tests
run: pnpm --filter=worker run test
run: pnpm --filter=worker run test:exclude-llm-connections
test-worker-llm-connections:
timeout-minutes: 20
runs-on: ubuntu-latest
needs:
- pre-job
if: needs.pre-job.outputs.should_skip != 'true'
name: test-worker-llm-connections (node24, pg15)
steps:
- uses: actions/checkout@v4
- uses: pnpm/action-setup@v3
with:
version: 9.5.0
- name: Login to Docker Hub
if: github.repository == 'langfuse/langfuse' && (github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository)
uses: docker/login-action@v3
with:
username: ${{ secrets.DOCKERHUB_USERNAME_READ }}
password: ${{ secrets.DOCKERHUB_TOKEN_READ }}
- name: Use Node.js 24
uses: actions/setup-node@v4
with:
node-version: 24
cache: "pnpm"
cache-dependency-path: "pnpm-lock.yaml"
- name: install dependencies
run: |
pnpm install
- name: Install golang-migrate for Clickhouse migrations
run: |
curl -L https://github.com/golang-migrate/migrate/releases/download/v4.19.0/migrate.linux-amd64.tar.gz | tar xvz
sudo mv migrate /usr/bin/migrate
which migrate
- name: Load default env
run: |
cp .env.dev.example .env
cp .env.dev.example web/.env
cp .env.dev.example worker/.env
- name: Run + migrate
run: |
docker compose -f docker-compose.dev.yml up -d
sleep 5 # Wait for PostgreSQL to accept connections
docker compose ps
env:
POSTGRES_VERSION: 15
- name: Ensure no unhealthy status
run: |
if docker compose ps | grep "(unhealthy)"; then
echo "One or more services are unhealthy"
exit 1
else
echo "All services are healthy"
fi
- name: Seed DB
run: |
pnpm run db:migrate
pnpm run db:seed
pnpm run --filter=shared ch:up
- name: Build
run: pnpm --filter=worker... run build
- name: run llm connection tests
run: pnpm --filter=worker run test:llm-connections-only
env:
LANGFUSE_LLM_CONNECTION_OPENAI_KEY: ${{ secrets.LANGFUSE_LLM_CONNECTION_OPENAI_KEY }}
LANGFUSE_LLM_CONNECTION_ANTHROPIC_KEY: ${{ secrets.LANGFUSE_LLM_CONNECTION_ANTHROPIC_KEY }}
LANGFUSE_LLM_CONNECTION_AZURE_KEY: ${{ secrets.LANGFUSE_LLM_CONNECTION_AZURE_KEY }}
LANGFUSE_LLM_CONNECTION_AZURE_BASE_URL: ${{ secrets.LANGFUSE_LLM_CONNECTION_AZURE_BASE_URL }}
LANGFUSE_LLM_CONNECTION_AZURE_MODEL: ${{ secrets.LANGFUSE_LLM_CONNECTION_AZURE_MODEL }}
LANGFUSE_LLM_CONNECTION_BEDROCK_ACCESS_KEY_ID: ${{ secrets.LANGFUSE_LLM_CONNECTION_BEDROCK_ACCESS_KEY_ID }}
LANGFUSE_LLM_CONNECTION_BEDROCK_SECRET_ACCESS_KEY: ${{ secrets.LANGFUSE_LLM_CONNECTION_BEDROCK_SECRET_ACCESS_KEY }}
LANGFUSE_LLM_CONNECTION_BEDROCK_REGION: ${{ secrets.LANGFUSE_LLM_CONNECTION_BEDROCK_REGION }}
LANGFUSE_LLM_CONNECTION_VERTEXAI_KEY: ${{ secrets.LANGFUSE_LLM_CONNECTION_VERTEXAI_KEY }}
LANGFUSE_LLM_CONNECTION_GOOGLEAISTUDIO_KEY: ${{ secrets.LANGFUSE_LLM_CONNECTION_GOOGLEAISTUDIO_KEY }}
e2e-tests:
runs-on: ubuntu-latest
@@ -359,7 +436,7 @@ jobs:
version: 9.5.0
- uses: actions/setup-node@v4
with:
node-version: 20
node-version: 24
cache: "pnpm"
cache-dependency-path: "pnpm-lock.yaml"
- name: Login to Docker Hub
@@ -377,7 +454,7 @@ jobs:
cp .env.dev.example web/.env
- name: Install golang-migrate for Clickhouse migrations
run: |
curl -L https://github.com/golang-migrate/migrate/releases/download/v4.18.3/migrate.linux-amd64.tar.gz | tar xvz
curl -L https://github.com/golang-migrate/migrate/releases/download/v4.19.0/migrate.linux-amd64.tar.gz | tar xvz
sudo mv migrate /usr/bin/migrate
which migrate
- name: Run + migrate
@@ -400,6 +477,8 @@ jobs:
pnpm run db:seed
- name: Build
run: pnpm run build
env:
NODE_OPTIONS: --max_old_space_size=8192
- name: Install playwright
run: pnpm --filter=web exec playwright install --with-deps
- name: Run e2e tests
@@ -423,7 +502,7 @@ jobs:
version: 9.5.0
- uses: actions/setup-node@v4
with:
node-version: 20
node-version: 24
cache: "pnpm"
cache-dependency-path: "pnpm-lock.yaml"
- name: install dependencies
@@ -431,7 +510,7 @@ jobs:
pnpm install
- name: Install golang-migrate for Clickhouse migrations
run: |
curl -L https://github.com/golang-migrate/migrate/releases/download/v4.18.3/migrate.linux-amd64.tar.gz | tar xvz
curl -L https://github.com/golang-migrate/migrate/releases/download/v4.19.0/migrate.linux-amd64.tar.gz | tar xvz
sudo mv migrate /usr/bin/migrate
which migrate
- name: Load default env
@@ -457,6 +536,8 @@ jobs:
pnpm run db:seed:examples
- name: Build
run: pnpm run build
env:
NODE_OPTIONS: --max_old_space_size=8192
- name: Run server
run: (pnpm run start&)
- name: Check worker health
@@ -477,6 +558,7 @@ jobs:
prettier-check,
tests-web-sync,
tests-worker,
test-worker-llm-connections,
e2e-tests,
test-docker-build,
e2e-server-tests,
@@ -517,7 +599,7 @@ jobs:
- name: Setup node
uses: actions/setup-node@v4
with:
node-version: 20
node-version: 24
cache-dependency-path: "pnpm-lock.yaml"
- name: Checkout
uses: actions/checkout@v4
+149
View File
@@ -0,0 +1,149 @@
name: SDK API Spec Generation
on:
push:
branches:
- main
paths:
- 'fern/**'
workflow_dispatch:
concurrency:
group: sdk-api-spec-${{ github.ref }}
cancel-in-progress: true
jobs:
generate-sdk-api-specs:
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Install pnpm
uses: pnpm/action-setup@v2
with:
version: 9
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: "24"
cache: "pnpm"
- name: Install dependencies
run: pnpm install
- name: Generate SDK specs
env:
FERN_TOKEN: ${{ secrets.FERN_TOKEN }}
run: npx fern-api generate --api server --force
- name: Update Python SDK
env:
GH_ACCESS_TOKEN: ${{ secrets.GH_ACCESS_TOKEN }}
run: |
# Clone langfuse-python repository
git clone https://github.com/langfuse/langfuse-python.git ../langfuse-python
cd ../langfuse-python
git config user.name "langfuse-bot"
git config user.email "langfuse-bot@langfuse.com"
echo "$GH_ACCESS_TOKEN" | gh auth login --with-token
gh auth setup-git
# Delete existing API contents
rm -rf langfuse/api/*
# Copy generated Python SDK files
cp -r ../langfuse/generated/python/* langfuse/api/
# Remove unnecessary integration tests created by Fern
# (could not find the right option to prevent this in generation step)
rm -rf langfuse/api/tests
# Install poetry and format
pip install poetry
poetry install --all-extras
poetry run ruff format langfuse/api
# Check for changes and show diff for debugging
if git diff --quiet; then
echo "No changes detected in Python SDK"
exit 0
fi
# Close existing api-spec-bot PRs
gh pr list --author langfuse-bot --state open --json number --jq '.[].number' | xargs -I {} gh pr close {}
# Get the GitHub username of the original commit author
cd ../langfuse
ORIGINAL_AUTHOR=$(gh api repos/langfuse/langfuse/commits/${GITHUB_SHA} --jq '.author.login')
cd ../langfuse-python
# Create new branch and push changes
BRANCH_NAME="api-spec-bot-${GITHUB_SHA::7}"
git checkout -b "$BRANCH_NAME"
git add .
git commit -m "feat(api): update API spec from langfuse/langfuse ${GITHUB_SHA::7}"
git push origin "$BRANCH_NAME"
# Create PR with original author as reviewer
gh pr create --title "feat(api): update API spec from langfuse/langfuse ${GITHUB_SHA::7}" --body "" --reviewer "$ORIGINAL_AUTHOR"
- name: Update TypeScript SDK
env:
GH_ACCESS_TOKEN: ${{ secrets.GH_ACCESS_TOKEN }}
run: |
# Clone langfuse-js repository
git clone https://github.com/langfuse/langfuse-js.git ../langfuse-js
cd ../langfuse-js
# Configure git
git config user.name "langfuse-bot"
git config user.email "langfuse-bot@langfuse.com"
echo "$GH_ACCESS_TOKEN" | gh auth login --with-token
gh auth setup-git
# Delete existing API contents
rm -rf packages/core/src/api/*
# Copy generated TypeScript SDK files
cp -r ../langfuse/generated/typescript/* packages/core/src/api/
# Patch compilation error inducing output
# Without this patch, the JS SDK will not build due to
# "Error: packages/core build: src/api/core/auth/index.ts(1,10): error TS1205: Re-exporting a type when 'isolatedModules' is enabled requires using 'export type'."
if [ -f "packages/core/src/api/core/auth/index.ts" ]; then
if grep -q '^export { AuthProvider } from "./AuthProvider.js";$' packages/core/src/api/core/auth/index.ts; then
sed -i '1s/^export { AuthProvider }/export { type AuthProvider }/' packages/core/src/api/core/auth/index.ts
fi
fi
# Install dependencies and format
npm install -g pnpm
pnpm install
pnpm format
# Check for changes and show diff for debugging
if git diff --quiet; then
echo "No changes detected in TypeScript SDK"
exit 0
fi
# Close existing api-spec-bot PRs
gh pr list --author langfuse-bot --state open --json number --jq '.[].number' | xargs -I {} gh pr close {}
# Get the GitHub username of the original commit author
cd ../langfuse
ORIGINAL_AUTHOR=$(gh api repos/langfuse/langfuse/commits/${GITHUB_SHA} --jq '.author.login')
cd ../langfuse-js
# Create new branch and push changes
BRANCH_NAME="api-spec-bot-${GITHUB_SHA::7}"
git checkout -b "$BRANCH_NAME"
git add .
git commit -m "feat(api): update API spec from langfuse/langfuse ${GITHUB_SHA::7}" --no-verify
git push origin "$BRANCH_NAME"
# Create PR with original author as reviewer
gh pr create --title "feat(api): update API spec from langfuse/langfuse ${GITHUB_SHA::7}" --body "" --reviewer "$ORIGINAL_AUTHOR"
+45
View File
@@ -0,0 +1,45 @@
---
name: "Validate PR Title"
on:
pull_request:
branches:
- "**"
types:
- opened
- edited
- synchronize
- reopened
jobs:
validate-pr-title:
runs-on: ubuntu-latest
permissions:
statuses: write
pull-requests: read
steps:
- name: Validate PR title follows conventional commits
uses: amannn/action-semantic-pull-request@v6
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
# Configure the types of commits allowed
types: |
feat
fix
docs
style
refactor
perf
test
build
ci
chore
revert
# Allow scopes (optional)
requireScope: false
# Allow any scope (not just the ones listed above)
validateSingleCommit: false
ignoreLabels: |
bot
ignore-semantic-pull-request
+3 -1
View File
@@ -1 +1,3 @@
public-hoist-pattern[]=*prisma*
public-hoist-pattern[]=*prisma*
# for turbopack, ongoing issue see: https://github.com/vercel/next.js/issues/68805
public-hoist-pattern[]=@aws-sdk/client-s3
+1 -1
View File
@@ -1 +1 @@
v20.19.2
v24.6.0
+4 -2
View File
@@ -85,6 +85,8 @@ Depending on the file location (sync, async)
pnpm test-sync --testPathPattern="$FILE_LOCATION_PATTERN" --testNamePattern="$TEST_NAME_PATTERN"
# For tests in the async folder:
pnpm test -- --testPathPattern="$FILE_LOCATION_PATTERN" --testNamePattern="$TEST_NAME_PATTERN"
# For client tests:
pnpm test-client --testPathPattern="buildStepData" --testNamePattern="buildStepData"
```
### Testing in the Worker Package
@@ -166,7 +168,7 @@ pnpm run nuke # Remove all node_modules, build files, wipe database
## Environment Setup
- **Node.js**: Version 20 (specified in `.nvmrc`)
- **Node.js**: Version 24 (specified in `.nvmrc`)
- **Package Manager**: pnpm v9.5.0
- **Database Dependencies**: Docker for local PostgreSQL, ClickHouse, Redis, MinIO
- **Environment**: Copy `.env.dev.example` to `.env`
@@ -195,4 +197,4 @@ To get a project, use the `get_project` capability with the full project name as
- For easier code reviews, prefer not to move functions etc around within a file unless necessary or instructed to do so
## Development Tips
- Before trying to build the package, try running the linter once first
- Before trying to build the package, try running the linter once first
+9 -7
View File
@@ -108,15 +108,19 @@ We built a monorepo using [pnpm](https://pnpm.io/motivation) and [turbo](https:/
Requirements
- Node.js 20 as specified in the [.nvmrc](.nvmrc)
- Node.js 24 as specified in the [.nvmrc](.nvmrc)
- Pnpm v.9.5.0
- Docker to run the database locally
- Clickhouse client
**Note:** You can also simply run Langfuse in a **GitHub Codespace** via the provided devcontainer. To do this, click on the green "Code" button in the top right corner of the repository and select "Open with Codespaces".
**Steps**
1. Install [golang-migrate](https://github.com/golang-migrate/migrate/tree/master/cmd/migrate#migrate-cli) as CLI
1. Install development dependencies:
- [golang-migrate](https://github.com/golang-migrate/migrate/tree/master/cmd/migrate#migrate-cli) as CLI
- [clickhouse binary](https://clickhouse.com/docs/install) on macOS with brew: `brew install --cask clickhouse`
2. Fork the repository and clone it locally
```bash
@@ -137,7 +141,7 @@ Requirements
cp .env.dev.example .env
```
4. Run the entire infrastructure in dev mode. **Note**: if you have an existing database, this command wipes it. Also, this will fail on the very first run. Please run it again.
5. Run the entire infrastructure in dev mode. **Note**: if you have an existing database, this command wipes it. Also, this will fail on the very first run. Please run it again.
```bash
pnpm run dx # first run only (resets db, docker containers, etc...)
@@ -146,13 +150,11 @@ Requirements
You will be asked whether you want to reset Postgres and ClickHouse. Confirm both with 'Y' and press enter.
5. Open the web app in your browser to start using Langfuse:
6. Open the web app in your browser to start using Langfuse:
- [Sign up page, http://localhost:3000](http://localhost:3000)
- [Demo project, http://localhost:3000/project/7a88fb47-b4e2-43b8-a06c-a5ce950dc53a](http://localhost:3000/project/7a88fb47-b4e2-43b8-a06c-a5ce950dc53a)
6. Log in as a test user:
7. Log in as a test user:
- Username: `demo@langfuse.com`
- Password: `password`
+104 -51
View File
@@ -1,4 +1,4 @@
![Langfuse GitHub Banner](https://langfuse.com/images/docs/github-readme/github-banner.png)
<img width="4856" height="1000" alt="github-banner" src="https://github.com/user-attachments/assets/6f435ef3-1194-4e26-87af-aa13826bbb5f" />
<div align="center">
<div>
@@ -76,7 +76,7 @@ Langfuse is an **open source LLM engineering** platform. It helps teams collabor
## ✨ Core Features
![Langfuse Overview](https://langfuse.com/images/docs/github-readme/github-feature-overview.png)
<img width="4856" height="1944" alt="Langfuse Overview" src="https://github.com/user-attachments/assets/5dac68ef-d546-49fb-b06f-cfafc19282e3" />
- [LLM Application Observability](https://langfuse.com/docs/tracing): Instrument your app and start ingesting traces to Langfuse, thereby tracking LLM calls and other relevant logic in your app such as retrieval, embedding, or agent actions. Inspect and debug complex logs and user sessions. Try the interactive [demo](https://langfuse.com/docs/demo) to see this in action.
@@ -92,7 +92,7 @@ Langfuse is an **open source LLM engineering** platform. It helps teams collabor
## 📦 Deploy Langfuse
![Langfuse Deployment Options](https://langfuse.com/images/docs/github-readme/github-deployment-options.png)
<img width="4856" height="1322" alt="Langfuse Deployment Options" src="https://github.com/user-attachments/assets/98f020c7-7a20-4264-a201-65c41a52a5d5" />
### Langfuse Cloud
@@ -127,7 +127,7 @@ See [self-hosting documentation](https://langfuse.com/self-hosting) to learn mor
## 🔌 Integrations
![Langfuse Integrations](https://langfuse.com/images/docs/github-readme/github-integrations.png)
<img width="4856" height="1322" alt="github-integrations" src="https://github.com/user-attachments/assets/e41ea0fb-742d-41ce-bf94-1d4fb95750cd" />
### Main Integrations:
@@ -215,7 +215,7 @@ main()
See your language model calls and other application logic in Langfuse.
![Example trace in Langfuse](https://langfuse.com/images/docs/github-readme/github-example-trace.png)
<img width="1787" height="674" alt="Example trace in Langfuse" src="https://github.com/user-attachments/assets/f796eb78-dfb5-4570-b236-bdb4b67d4d55" />
_[Public example trace in Langfuse](https://cloud.langfuse.com/project/cloramnkj0002jz088vzn1ja4/traces/2cec01e3-3dc2-472f-afcf-3b968cf0c1f4?timestamp=2025-02-10T14%3A27%3A30.275Z&observation=cb5ff844-07ef-41e6-b8e2-6c64344bc13b)_
@@ -268,52 +268,105 @@ This repository is MIT licensed, except for the `ee` folders. See [LICENSE](LICE
Top open-source Python projects that use Langfuse, ranked by stars ([Source](https://github.com/langfuse/langfuse-docs/blob/main/components-mdx/dependents)):
| Repository | Stars |
| :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ----: |
| <img class="avatar mr-2" src="https://avatars.githubusercontent.com/u/127165244?s=40&v=4" width="20" height="20" alt=""> &nbsp; [langgenius](https://github.com/langgenius) / [dify](https://github.com/langgenius/dify) | 54865 |
| <img class="avatar mr-2" src="https://avatars.githubusercontent.com/u/158137808?s=40&v=4" width="20" height="20" alt=""> &nbsp; [open-webui](https://github.com/open-webui) / [open-webui](https://github.com/open-webui/open-webui) | 51531 |
| <img class="avatar mr-2" src="https://avatars.githubusercontent.com/u/131470832?s=40&v=4" width="20" height="20" alt=""> &nbsp; [lobehub](https://github.com/lobehub) / [lobe-chat](https://github.com/lobehub/lobe-chat) | 49003 |
| <img class="avatar mr-2" src="https://avatars.githubusercontent.com/u/85702467?s=40&v=4" width="20" height="20" alt=""> &nbsp; [langflow-ai](https://github.com/langflow-ai) / [langflow](https://github.com/langflow-ai/langflow) | 39093 |
| <img class="avatar mr-2" src="https://avatars.githubusercontent.com/u/130722866?s=40&v=4" width="20" height="20" alt=""> &nbsp; [run-llama](https://github.com/run-llama) / [llama_index](https://github.com/run-llama/llama_index) | 37368 |
| <img class="avatar mr-2" src="https://avatars.githubusercontent.com/u/139558948?s=40&v=4" width="20" height="20" alt=""> &nbsp; [chatchat-space](https://github.com/chatchat-space) / [Langchain-Chatchat](https://github.com/chatchat-space/Langchain-Chatchat) | 32486 |
| <img class="avatar mr-2" src="https://avatars.githubusercontent.com/u/128289781?s=40&v=4" width="20" height="20" alt=""> &nbsp; [FlowiseAI](https://github.com/FlowiseAI) / [Flowise](https://github.com/FlowiseAI/Flowise) | 32448 |
| <img class="avatar mr-2" src="https://avatars.githubusercontent.com/u/31035808?s=40&v=4" width="20" height="20" alt=""> &nbsp; [mindsdb](https://github.com/mindsdb) / [mindsdb](https://github.com/mindsdb/mindsdb) | 26931 |
| <img class="avatar mr-2" src="https://avatars.githubusercontent.com/u/119600397?s=40&v=4" width="20" height="20" alt=""> &nbsp; [twentyhq](https://github.com/twentyhq) / [twenty](https://github.com/twentyhq/twenty) | 24195 |
| <img class="avatar mr-2" src="https://avatars.githubusercontent.com/u/60330232?s=40&v=4" width="20" height="20" alt=""> &nbsp; [PostHog](https://github.com/PostHog) / [posthog](https://github.com/PostHog/posthog) | 22618 |
| <img class="avatar mr-2" src="https://avatars.githubusercontent.com/u/121462774?s=40&v=4" width="20" height="20" alt=""> &nbsp; [BerriAI](https://github.com/BerriAI) / [litellm](https://github.com/BerriAI/litellm) | 15151 |
| <img class="avatar mr-2" src="https://avatars.githubusercontent.com/u/179202840?s=40&v=4" width="20" height="20" alt=""> &nbsp; [mediar-ai](https://github.com/mediar-ai) / [screenpipe](https://github.com/mediar-ai/screenpipe) | 11037 |
| <img class="avatar mr-2" src="https://avatars.githubusercontent.com/u/105877416?s=40&v=4" width="20" height="20" alt=""> &nbsp; [formbricks](https://github.com/formbricks) / [formbricks](https://github.com/formbricks/formbricks) | 9386 |
| <img class="avatar mr-2" src="https://avatars.githubusercontent.com/u/76263028?s=40&v=4" width="20" height="20" alt=""> &nbsp; [anthropics](https://github.com/anthropics) / [courses](https://github.com/anthropics/courses) | 8385 |
| <img class="avatar mr-2" src="https://avatars.githubusercontent.com/u/78410652?s=40&v=4" width="20" height="20" alt=""> &nbsp; [GreyDGL](https://github.com/GreyDGL) / [PentestGPT](https://github.com/GreyDGL/PentestGPT) | 7374 |
| <img class="avatar mr-2" src="https://avatars.githubusercontent.com/u/152537519?s=40&v=4" width="20" height="20" alt=""> &nbsp; [superagent-ai](https://github.com/superagent-ai) / [superagent](https://github.com/superagent-ai/superagent) | 5391 |
| <img class="avatar mr-2" src="https://avatars.githubusercontent.com/u/137907881?s=40&v=4" width="20" height="20" alt=""> &nbsp; [promptfoo](https://github.com/promptfoo) / [promptfoo](https://github.com/promptfoo/promptfoo) | 4976 |
| <img class="avatar mr-2" src="https://avatars.githubusercontent.com/u/157326433?s=40&v=4" width="20" height="20" alt=""> &nbsp; [onlook-dev](https://github.com/onlook-dev) / [onlook](https://github.com/onlook-dev/onlook) | 4141 |
| <img class="avatar mr-2" src="https://avatars.githubusercontent.com/u/7250217?s=40&v=4" width="20" height="20" alt=""> &nbsp; [Canner](https://github.com/Canner) / [WrenAI](https://github.com/Canner/WrenAI) | 2526 |
| <img class="avatar mr-2" src="https://avatars.githubusercontent.com/u/11855343?s=40&v=4" width="20" height="20" alt=""> &nbsp; [pingcap](https://github.com/pingcap) / [autoflow](https://github.com/pingcap/autoflow) | 2061 |
| <img class="avatar mr-2" src="https://avatars.githubusercontent.com/u/85268109?s=40&v=4" width="20" height="20" alt=""> &nbsp; [MLSysOps](https://github.com/MLSysOps) / [MLE-agent](https://github.com/MLSysOps/MLE-agent) | 1161 |
| <img class="avatar mr-2" src="https://avatars.githubusercontent.com/u/158137808?s=40&v=4" width="20" height="20" alt=""> &nbsp; [open-webui](https://github.com/open-webui) / [pipelines](https://github.com/open-webui/pipelines) | 1100 |
| <img class="avatar mr-2" src="https://avatars.githubusercontent.com/u/18422723?s=40&v=4" width="20" height="20" alt=""> &nbsp; [alishobeiri](https://github.com/alishobeiri) / [thread](https://github.com/alishobeiri/thread) | 1074 |
| <img class="avatar mr-2" src="https://avatars.githubusercontent.com/u/125468716?s=40&v=4" width="20" height="20" alt=""> &nbsp; [topoteretes](https://github.com/topoteretes) / [cognee](https://github.com/topoteretes/cognee) | 971 |
| <img class="avatar mr-2" src="https://avatars.githubusercontent.com/u/188657705?s=40&v=4" width="20" height="20" alt=""> &nbsp; [bRAGAI](https://github.com/bRAGAI) / [bRAG-langchain](https://github.com/bRAGAI/bRAG-langchain) | 823 |
| <img class="avatar mr-2" src="https://avatars.githubusercontent.com/u/169500408?s=40&v=4" width="20" height="20" alt=""> &nbsp; [opslane](https://github.com/opslane) / [opslane](https://github.com/opslane/opslane) | 677 |
| <img class="avatar mr-2" src="https://avatars.githubusercontent.com/u/151867818?s=40&v=4" width="20" height="20" alt=""> &nbsp; [dynamiq-ai](https://github.com/dynamiq-ai) / [dynamiq](https://github.com/dynamiq-ai/dynamiq) | 639 |
| <img class="avatar mr-2" src="https://avatars.githubusercontent.com/u/48585267?s=40&v=4" width="20" height="20" alt=""> &nbsp; [theopenconversationkit](https://github.com/theopenconversationkit) / [tock](https://github.com/theopenconversationkit/tock) | 514 |
| <img class="avatar mr-2" src="https://avatars.githubusercontent.com/u/20493493?s=40&v=4" width="20" height="20" alt=""> &nbsp; [andysingal](https://github.com/andysingal) / [llm-course](https://github.com/andysingal/llm-course) | 394 |
| <img class="avatar mr-2" src="https://avatars.githubusercontent.com/u/132396805?s=40&v=4" width="20" height="20" alt=""> &nbsp; [phospho-app](https://github.com/phospho-app) / [phospho](https://github.com/phospho-app/phospho) | 384 |
| <img class="avatar mr-2" src="https://avatars.githubusercontent.com/u/178644984?s=40&v=4" width="20" height="20" alt=""> &nbsp; [sentient-engineering](https://github.com/sentient-engineering) / [agent-q](https://github.com/sentient-engineering/agent-q) | 370 |
| <img class="avatar mr-2" src="https://avatars.githubusercontent.com/u/168552753?s=40&v=4" width="20" height="20" alt=""> &nbsp; [sql-agi](https://github.com/sql-agi) / [DB-GPT](https://github.com/sql-agi/DB-GPT) | 324 |
| <img class="avatar mr-2" src="https://avatars.githubusercontent.com/u/60330232?s=40&v=4" width="20" height="20" alt=""> &nbsp; [PostHog](https://github.com/PostHog) / [posthog-foss](https://github.com/PostHog/posthog-foss) | 305 |
| <img class="avatar mr-2" src="https://avatars.githubusercontent.com/u/154247157?s=40&v=4" width="20" height="20" alt=""> &nbsp; [vespperhq](https://github.com/vespperhq) / [vespper](https://github.com/vespperhq/vespper) | 304 |
| <img class="avatar mr-2" src="https://avatars.githubusercontent.com/u/185116535?s=40&v=4" width="20" height="20" alt=""> &nbsp; [block](https://github.com/block) / [goose](https://github.com/block/goose) | 295 |
| <img class="avatar mr-2" src="https://avatars.githubusercontent.com/u/609489?s=40&v=4" width="20" height="20" alt=""> &nbsp; [aorwall](https://github.com/aorwall) / [moatless-tools](https://github.com/aorwall/moatless-tools) | 291 |
| <img class="avatar mr-2" src="https://avatars.githubusercontent.com/u/2357342?s=40&v=4" width="20" height="20" alt=""> &nbsp; [dmayboroda](https://github.com/dmayboroda) / [minima](https://github.com/dmayboroda/minima) | 221 |
| <img class="avatar mr-2" src="https://avatars.githubusercontent.com/u/66303003?s=40&v=4" width="20" height="20" alt=""> &nbsp; [RobotecAI](https://github.com/RobotecAI) / [rai](https://github.com/RobotecAI/rai) | 172 |
| <img class="avatar mr-2" src="https://avatars.githubusercontent.com/u/148684274?s=40&v=4" width="20" height="20" alt=""> &nbsp; [i-am-alice](https://github.com/i-am-alice) / [3rd-devs](https://github.com/i-am-alice/3rd-devs) | 148 |
| <img class="avatar mr-2" src="https://avatars.githubusercontent.com/u/171735272?s=40&v=4" width="20" height="20" alt=""> &nbsp; [8090-inc](https://github.com/8090-inc) / [xrx-sample-apps](https://github.com/8090-inc/xrx-sample-apps) | 138 |
| <img class="avatar mr-2" src="https://avatars.githubusercontent.com/u/104478511?s=40&v=4" width="20" height="20" alt=""> &nbsp; [babelcloud](https://github.com/babelcloud) / [LLM-RGB](https://github.com/babelcloud/LLM-RGB) | 135 |
| <img class="avatar mr-2" src="https://avatars.githubusercontent.com/u/15125613?s=40&v=4" width="20" height="20" alt=""> &nbsp; [souzatharsis](https://github.com/souzatharsis) / [tamingLLMs](https://github.com/souzatharsis/tamingLLMs) | 129 |
| <img class="avatar mr-2" src="https://avatars.githubusercontent.com/u/169401942?s=40&v=4" width="20" height="20" alt=""> &nbsp; [LibreChat-AI](https://github.com/LibreChat-AI) / [librechat.ai](https://github.com/LibreChat-AI/librechat.ai) | 128 |
| <img class="avatar mr-2" src="https://avatars.githubusercontent.com/u/51827949?s=40&v=4" width="20" height="20" alt=""> &nbsp; [deepset-ai](https://github.com/deepset-ai) / [haystack-core-integrations](https://github.com/deepset-ai/haystack-core-integrations) | 126 |
| Repository | Stars |
| :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -----: |
| <img class="avatar mr-2" src="https://avatars.githubusercontent.com/u/85702467?s=40&v=4" width="20" height="20" alt=""> &nbsp; [langflow-ai](https://github.com/langflow-ai) / [langflow](https://github.com/langflow-ai/langflow) | 116251 |
| <img class="avatar mr-2" src="https://avatars.githubusercontent.com/u/158137808?s=40&v=4" width="20" height="20" alt=""> &nbsp; [open-webui](https://github.com/open-webui) / [open-webui](https://github.com/open-webui/open-webui) | 109642 |
| <img class="avatar mr-2" src="https://avatars.githubusercontent.com/u/23818?s=40&v=4" width="20" height="20" alt=""> &nbsp; [abi](https://github.com/abi) / [screenshot-to-code](https://github.com/abi/screenshot-to-code) | 70877 |
| <img class="avatar mr-2" src="https://avatars.githubusercontent.com/u/131470832?s=40&v=4" width="20" height="20" alt=""> &nbsp; [lobehub](https://github.com/lobehub) / [lobe-chat](https://github.com/lobehub/lobe-chat) | 65454 |
| <img class="avatar mr-2" src="https://avatars.githubusercontent.com/u/69962740?s=40&v=4" width="20" height="20" alt=""> &nbsp; [infiniflow](https://github.com/infiniflow) / [ragflow](https://github.com/infiniflow/ragflow) | 64118 |
| <img class="avatar mr-2" src="https://avatars.githubusercontent.com/u/135057108?s=40&v=4" width="20" height="20" alt=""> &nbsp; [firecrawl](https://github.com/firecrawl) / [firecrawl](https://github.com/firecrawl/firecrawl) | 56713 |
| <img class="avatar mr-2" src="https://avatars.githubusercontent.com/u/130722866?s=40&v=4" width="20" height="20" alt=""> &nbsp; [run-llama](https://github.com/run-llama) / [llama_index](https://github.com/run-llama/llama_index) | 44203 |
| <img class="avatar mr-2" src="https://avatars.githubusercontent.com/u/128289781?s=40&v=4" width="20" height="20" alt=""> &nbsp; [FlowiseAI](https://github.com/FlowiseAI) / [Flowise](https://github.com/FlowiseAI/Flowise) | 43547 |
| <img class="avatar mr-2" src="https://avatars.githubusercontent.com/u/159330290?s=40&v=4" width="20" height="20" alt=""> &nbsp; [QuivrHQ](https://github.com/QuivrHQ) / [quivr](https://github.com/QuivrHQ/quivr) | 38415 |
| <img class="avatar mr-2" src="https://avatars.githubusercontent.com/u/6154722?s=40&v=4" width="20" height="20" alt=""> &nbsp; [microsoft](https://github.com/microsoft) / [ai-agents-for-beginners](https://github.com/microsoft/ai-agents-for-beginners) | 38012 |
| <img class="avatar mr-2" src="https://avatars.githubusercontent.com/u/139558948?s=40&v=4" width="20" height="20" alt=""> &nbsp; [chatchat-space](https://github.com/chatchat-space) / [Langchain-Chatchat](https://github.com/chatchat-space/Langchain-Chatchat) | 36071 |
| <img class="avatar mr-2" src="https://avatars.githubusercontent.com/u/31035808?s=40&v=4" width="20" height="20" alt=""> &nbsp; [mindsdb](https://github.com/mindsdb) / [mindsdb](https://github.com/mindsdb/mindsdb) | 35669 |
| <img class="avatar mr-2" src="https://avatars.githubusercontent.com/u/121462774?s=40&v=4" width="20" height="20" alt=""> &nbsp; [BerriAI](https://github.com/BerriAI) / [litellm](https://github.com/BerriAI/litellm) | 28726 |
| <img class="avatar mr-2" src="https://avatars.githubusercontent.com/u/157326433?s=40&v=4" width="20" height="20" alt=""> &nbsp; [onlook-dev](https://github.com/onlook-dev) / [onlook](https://github.com/onlook-dev/onlook) | 22447 |
| <img class="avatar mr-2" src="https://avatars.githubusercontent.com/u/487568?s=40&v=4" width="20" height="20" alt=""> &nbsp; [NixOS](https://github.com/NixOS) / [nixpkgs](https://github.com/NixOS/nixpkgs) | 21748 |
| <img class="avatar mr-2" src="https://avatars.githubusercontent.com/u/170767358?s=40&v=4" width="20" height="20" alt=""> &nbsp; [kortix-ai](https://github.com/kortix-ai) / [suna](https://github.com/kortix-ai/suna) | 17976 |
| <img class="avatar mr-2" src="https://avatars.githubusercontent.com/u/76263028?s=40&v=4" width="20" height="20" alt=""> &nbsp; [anthropics](https://github.com/anthropics) / [courses](https://github.com/anthropics/courses) | 17057 |
| <img class="avatar mr-2" src="https://avatars.githubusercontent.com/u/149120496?s=40&v=4" width="20" height="20" alt=""> &nbsp; [mastra-ai](https://github.com/mastra-ai) / [mastra](https://github.com/mastra-ai/mastra) | 16484 |
| <img class="avatar mr-2" src="https://avatars.githubusercontent.com/u/134601687?s=40&v=4" width="20" height="20" alt=""> &nbsp; [langfuse](https://github.com/langfuse) / [langfuse](https://github.com/langfuse/langfuse) | 16054 |
| <img class="avatar mr-2" src="https://avatars.githubusercontent.com/u/7250217?s=40&v=4" width="20" height="20" alt=""> &nbsp; [Canner](https://github.com/Canner) / [WrenAI](https://github.com/Canner/WrenAI) | 11868 |
| <img class="avatar mr-2" src="https://avatars.githubusercontent.com/u/137907881?s=40&v=4" width="20" height="20" alt=""> &nbsp; [promptfoo](https://github.com/promptfoo) / [promptfoo](https://github.com/promptfoo/promptfoo) | 8350 |
| <img class="avatar mr-2" src="https://avatars.githubusercontent.com/u/193350244?s=40&v=4" width="20" height="20" alt=""> &nbsp; [The-Pocket](https://github.com/The-Pocket) / [PocketFlow](https://github.com/The-Pocket/PocketFlow) | 8313 |
| <img class="avatar mr-2" src="https://avatars.githubusercontent.com/u/139012218?s=40&v=4" width="20" height="20" alt=""> &nbsp; [OpenPipe](https://github.com/OpenPipe) / [ART](https://github.com/OpenPipe/ART) | 7093 |
| <img class="avatar mr-2" src="https://avatars.githubusercontent.com/u/125468716?s=40&v=4" width="20" height="20" alt=""> &nbsp; [topoteretes](https://github.com/topoteretes) / [cognee](https://github.com/topoteretes/cognee) | 7011 |
| <img class="avatar mr-2" src="https://avatars.githubusercontent.com/u/3299148?s=40&v=4" width="20" height="20" alt=""> &nbsp; [awslabs](https://github.com/awslabs) / [agent-squad](https://github.com/awslabs/agent-squad) | 6785 |
| <img class="avatar mr-2" src="https://avatars.githubusercontent.com/u/162546372?s=40&v=4" width="20" height="20" alt=""> &nbsp; [BasedHardware](https://github.com/BasedHardware) / [omi](https://github.com/BasedHardware/omi) | 6231 |
| <img class="avatar mr-2" src="https://avatars.githubusercontent.com/u/118301880?s=40&v=4" width="20" height="20" alt=""> &nbsp; [hatchet-dev](https://github.com/hatchet-dev) / [hatchet](https://github.com/hatchet-dev/hatchet) | 6019 |
| <img class="avatar mr-2" src="https://avatars.githubusercontent.com/u/88676955?s=40&v=4" width="20" height="20" alt=""> &nbsp; [zenml-io](https://github.com/zenml-io) / [zenml](https://github.com/zenml-io/zenml) | 4873 |
| <img class="avatar mr-2" src="https://avatars.githubusercontent.com/u/132635806?s=40&v=4" width="20" height="20" alt=""> &nbsp; [refly-ai](https://github.com/refly-ai) / [refly](https://github.com/refly-ai/refly) | 4654 |
| <img class="avatar mr-2" src="https://avatars.githubusercontent.com/u/47287758?s=40&v=4" width="20" height="20" alt=""> &nbsp; [coleam00](https://github.com/coleam00) / [ottomator-agents](https://github.com/coleam00/ottomator-agents) | 4165 |
| <img class="avatar mr-2" src="https://avatars.githubusercontent.com/u/8251002?s=40&v=4" width="20" height="20" alt=""> &nbsp; [JoshuaC215](https://github.com/JoshuaC215) / [agent-service-toolkit](https://github.com/JoshuaC215/agent-service-toolkit) | 3557 |
| <img class="avatar mr-2" src="https://avatars.githubusercontent.com/u/185852128?s=40&v=4" width="20" height="20" alt=""> &nbsp; [colanode](https://github.com/colanode) / [colanode](https://github.com/colanode/colanode) | 3517 |
| <img class="avatar mr-2" src="https://avatars.githubusercontent.com/u/201282378?s=40&v=4" width="20" height="20" alt=""> &nbsp; [VoltAgent](https://github.com/VoltAgent) / [voltagent](https://github.com/VoltAgent/voltagent) | 3210 |
| <img class="avatar mr-2" src="https://avatars.githubusercontent.com/u/188657705?s=40&v=4" width="20" height="20" alt=""> &nbsp; [bragai](https://github.com/bragai) / [bRAG-langchain](https://github.com/bragai/bRAG-langchain) | 3010 |
| <img class="avatar mr-2" src="https://avatars.githubusercontent.com/u/11855343?s=40&v=4" width="20" height="20" alt=""> &nbsp; [pingcap](https://github.com/pingcap) / [autoflow](https://github.com/pingcap/autoflow) | 2651 |
| <img class="avatar mr-2" src="https://avatars.githubusercontent.com/u/135396723?s=40&v=4" width="20" height="20" alt=""> &nbsp; [sourcebot-dev](https://github.com/sourcebot-dev) / [sourcebot](https://github.com/sourcebot-dev/sourcebot) | 2570 |
| <img class="avatar mr-2" src="https://avatars.githubusercontent.com/u/158137808?s=40&v=4" width="20" height="20" alt=""> &nbsp; [open-webui](https://github.com/open-webui) / [pipelines](https://github.com/open-webui/pipelines) | 2055 |
| <img class="avatar mr-2" src="https://avatars.githubusercontent.com/u/179994263?s=40&v=4" width="20" height="20" alt=""> &nbsp; [YFGaia](https://github.com/YFGaia) / [dify-plus](https://github.com/YFGaia/dify-plus) | 1734 |
| <img class="avatar mr-2" src="https://avatars.githubusercontent.com/u/46323662?s=40&v=4" width="20" height="20" alt=""> &nbsp; [TheSpaghettiDetective](https://github.com/TheSpaghettiDetective) / [obico-server](https://github.com/TheSpaghettiDetective/obico-server) | 1687 |
| <img class="avatar mr-2" src="https://avatars.githubusercontent.com/u/85268109?s=40&v=4" width="20" height="20" alt=""> &nbsp; [MLSysOps](https://github.com/MLSysOps) / [MLE-agent](https://github.com/MLSysOps/MLE-agent) | 1387 |
| <img class="avatar mr-2" src="https://avatars.githubusercontent.com/u/144196744?s=40&v=4" width="20" height="20" alt=""> &nbsp; [TIGER-AI-Lab](https://github.com/TIGER-AI-Lab) / [TheoremExplainAgent](https://github.com/TIGER-AI-Lab/TheoremExplainAgent) | 1385 |
| <img class="avatar mr-2" src="https://avatars.githubusercontent.com/u/2314423?s=40&v=4" width="20" height="20" alt=""> &nbsp; [trailofbits](https://github.com/trailofbits) / [buttercup](https://github.com/trailofbits/buttercup) | 1223 |
| <img class="avatar mr-2" src="https://avatars.githubusercontent.com/u/62564480?s=40&v=4" width="20" height="20" alt=""> &nbsp; [wassim249](https://github.com/wassim249) / [fastapi-langgraph-agent-production-ready-template](https://github.com/wassim249/fastapi-langgraph-agent-production-ready-template) | 1200 |
| <img class="avatar mr-2" src="https://avatars.githubusercontent.com/u/18422723?s=40&v=4" width="20" height="20" alt=""> &nbsp; [alishobeiri](https://github.com/alishobeiri) / [thread](https://github.com/alishobeiri/thread) | 1098 |
| <img class="avatar mr-2" src="https://avatars.githubusercontent.com/u/2357342?s=40&v=4" width="20" height="20" alt=""> &nbsp; [dmayboroda](https://github.com/dmayboroda) / [minima](https://github.com/dmayboroda/minima) | 1010 |
| <img class="avatar mr-2" src="https://avatars.githubusercontent.com/u/65890619?s=40&v=4" width="20" height="20" alt=""> &nbsp; [zstar1003](https://github.com/zstar1003) / [ragflow-plus](https://github.com/zstar1003/ragflow-plus) | 993 |
| <img class="avatar mr-2" src="https://avatars.githubusercontent.com/u/121352441?s=40&v=4" width="20" height="20" alt=""> &nbsp; [openops-cloud](https://github.com/openops-cloud) / [openops](https://github.com/openops-cloud/openops) | 939 |
| <img class="avatar mr-2" src="https://avatars.githubusercontent.com/u/151867818?s=40&v=4" width="20" height="20" alt=""> &nbsp; [dynamiq-ai](https://github.com/dynamiq-ai) / [dynamiq](https://github.com/dynamiq-ai/dynamiq) | 927 |
| <img class="avatar mr-2" src="https://avatars.githubusercontent.com/u/74420131?s=40&v=4" width="20" height="20" alt=""> &nbsp; [xataio](https://github.com/xataio) / [agent](https://github.com/xataio/agent) | 857 |
| <img class="avatar mr-2" src="https://avatars.githubusercontent.com/u/123981229?s=40&v=4" width="20" height="20" alt=""> &nbsp; [plastic-labs](https://github.com/plastic-labs) / [tutor-gpt](https://github.com/plastic-labs/tutor-gpt) | 845 |
| <img class="avatar mr-2" src="https://avatars.githubusercontent.com/u/176438967?s=40&v=4" width="20" height="20" alt=""> &nbsp; [trendy-design](https://github.com/trendy-design) / [llmchat](https://github.com/trendy-design/llmchat) | 829 |
| <img class="avatar mr-2" src="https://avatars.githubusercontent.com/u/2045570?s=40&v=4" width="20" height="20" alt=""> &nbsp; [hotovo](https://github.com/hotovo) / [aider-desk](https://github.com/hotovo/aider-desk) | 781 |
| <img class="avatar mr-2" src="https://avatars.githubusercontent.com/u/169500408?s=40&v=4" width="20" height="20" alt=""> &nbsp; [opslane](https://github.com/opslane) / [opslane](https://github.com/opslane/opslane) | 719 |
| <img class="avatar mr-2" src="https://avatars.githubusercontent.com/u/142796018?s=40&v=4" width="20" height="20" alt=""> &nbsp; [wrtnlabs](https://github.com/wrtnlabs) / [autoview](https://github.com/wrtnlabs/autoview) | 688 |
| <img class="avatar mr-2" src="https://avatars.githubusercontent.com/u/20493493?s=40&v=4" width="20" height="20" alt=""> &nbsp; [andysingal](https://github.com/andysingal) / [llm-course](https://github.com/andysingal/llm-course) | 643 |
| <img class="avatar mr-2" src="https://avatars.githubusercontent.com/u/48585267?s=40&v=4" width="20" height="20" alt=""> &nbsp; [theopenconversationkit](https://github.com/theopenconversationkit) / [tock](https://github.com/theopenconversationkit/tock) | 587 |
| <img class="avatar mr-2" src="https://avatars.githubusercontent.com/u/178644984?s=40&v=4" width="20" height="20" alt=""> &nbsp; [sentient-engineering](https://github.com/sentient-engineering) / [agent-q](https://github.com/sentient-engineering/agent-q) | 487 |
| <img class="avatar mr-2" src="https://avatars.githubusercontent.com/u/58037050?s=40&v=4" width="20" height="20" alt=""> &nbsp; [NicholasGoh](https://github.com/NicholasGoh) / [fastapi-mcp-langgraph-template](https://github.com/NicholasGoh/fastapi-mcp-langgraph-template) | 481 |
| <img class="avatar mr-2" src="https://avatars.githubusercontent.com/u/148684274?s=40&v=4" width="20" height="20" alt=""> &nbsp; [i-am-alice](https://github.com/i-am-alice) / [3rd-devs](https://github.com/i-am-alice/3rd-devs) | 472 |
| <img class="avatar mr-2" src="https://avatars.githubusercontent.com/u/163431636?s=40&v=4" width="20" height="20" alt=""> &nbsp; [AIDotNet](https://github.com/AIDotNet) / [koala-ai](https://github.com/AIDotNet/koala-ai) | 470 |
| <img class="avatar mr-2" src="https://avatars.githubusercontent.com/u/132396805?s=40&v=4" width="20" height="20" alt=""> &nbsp; [phospho-app](https://github.com/phospho-app) / [text-analytics-legacy](https://github.com/phospho-app/text-analytics-legacy) | 439 |
| <img class="avatar mr-2" src="https://avatars.githubusercontent.com/u/170831637?s=40&v=4" width="20" height="20" alt=""> &nbsp; [inferablehq](https://github.com/inferablehq) / [inferable](https://github.com/inferablehq/inferable) | 403 |
| <img class="avatar mr-2" src="https://avatars.githubusercontent.com/u/171800766?s=40&v=4" width="20" height="20" alt=""> &nbsp; [duoyang666](https://github.com/duoyang666) / [ai_novel](https://github.com/duoyang666/ai_novel) | 397 |
| <img class="avatar mr-2" src="https://avatars.githubusercontent.com/u/209155962?s=40&v=4" width="20" height="20" alt=""> &nbsp; [strands-agents](https://github.com/strands-agents) / [samples](https://github.com/strands-agents/samples) | 385 |
| <img class="avatar mr-2" src="https://avatars.githubusercontent.com/u/16997807?s=40&v=4" width="20" height="20" alt=""> &nbsp; [FranciscoMoretti](https://github.com/FranciscoMoretti) / [sparka](https://github.com/FranciscoMoretti/sparka) | 380 |
| <img class="avatar mr-2" src="https://avatars.githubusercontent.com/u/66303003?s=40&v=4" width="20" height="20" alt=""> &nbsp; [RobotecAI](https://github.com/RobotecAI) / [rai](https://github.com/RobotecAI/rai) | 373 |
| <img class="avatar mr-2" src="https://avatars.githubusercontent.com/u/137044109?s=40&v=4" width="20" height="20" alt=""> &nbsp; [ElectricCodeGuy](https://github.com/ElectricCodeGuy) / [SupabaseAuthWithSSR](https://github.com/ElectricCodeGuy/SupabaseAuthWithSSR) | 370 |
| <img class="avatar mr-2" src="https://avatars.githubusercontent.com/u/169401942?s=40&v=4" width="20" height="20" alt=""> &nbsp; [LibreChat-AI](https://github.com/LibreChat-AI) / [librechat.ai](https://github.com/LibreChat-AI/librechat.ai) | 339 |
| <img class="avatar mr-2" src="https://avatars.githubusercontent.com/u/15125613?s=40&v=4" width="20" height="20" alt=""> &nbsp; [souzatharsis](https://github.com/souzatharsis) / [tamingLLMs](https://github.com/souzatharsis/tamingLLMs) | 323 |
| <img class="avatar mr-2" src="https://avatars.githubusercontent.com/u/8931462?s=40&v=4" width="20" height="20" alt=""> &nbsp; [aws-samples](https://github.com/aws-samples) / [aws-ai-ml-workshop-kr](https://github.com/aws-samples/aws-ai-ml-workshop-kr) | 295 |
| <img class="avatar mr-2" src="https://avatars.githubusercontent.com/u/25676773?s=40&v=4" width="20" height="20" alt=""> &nbsp; [weizxfree](https://github.com/weizxfree) / [KnowFlow](https://github.com/weizxfree/KnowFlow) | 285 |
| <img class="avatar mr-2" src="https://avatars.githubusercontent.com/u/88676955?s=40&v=4" width="20" height="20" alt=""> &nbsp; [zenml-io](https://github.com/zenml-io) / [zenml-projects](https://github.com/zenml-io/zenml-projects) | 276 |
| <img class="avatar mr-2" src="https://avatars.githubusercontent.com/u/136071305?s=40&v=4" width="20" height="20" alt=""> &nbsp; [wxai-space](https://github.com/wxai-space) / [LightAgent](https://github.com/wxai-space/LightAgent) | 275 |
| <img class="avatar mr-2" src="https://avatars.githubusercontent.com/u/90278288?s=40&v=4" width="20" height="20" alt=""> &nbsp; [Ozamatash](https://github.com/Ozamatash) / [deep-research-mcp](https://github.com/Ozamatash/deep-research-mcp) | 269 |
| <img class="avatar mr-2" src="https://avatars.githubusercontent.com/u/168552753?s=40&v=4" width="20" height="20" alt=""> &nbsp; [sql-agi](https://github.com/sql-agi) / [DB-GPT](https://github.com/sql-agi/DB-GPT) | 241 |
| <img class="avatar mr-2" src="https://avatars.githubusercontent.com/u/828269?s=40&v=4" width="20" height="20" alt=""> &nbsp; [guyernest](https://github.com/guyernest) / [advanced-rag](https://github.com/guyernest/advanced-rag) | 238 |
| <img class="avatar mr-2" src="https://avatars.githubusercontent.com/u/175069511?s=40&v=4" width="20" height="20" alt=""> &nbsp; [bklieger-groq](https://github.com/bklieger-groq) / [mathtutor-on-groq](https://github.com/bklieger-groq/mathtutor-on-groq) | 233 |
| <img class="avatar mr-2" src="https://avatars.githubusercontent.com/u/123981229?s=40&v=4" width="20" height="20" alt=""> &nbsp; [plastic-labs](https://github.com/plastic-labs) / [honcho](https://github.com/plastic-labs/honcho) | 224 |
| <img class="avatar mr-2" src="https://avatars.githubusercontent.com/u/118967077?s=40&v=4" width="20" height="20" alt=""> &nbsp; [OVINC-CN](https://github.com/OVINC-CN) / [OpenWebUI](https://github.com/OVINC-CN/OpenWebUI) | 202 |
| <img class="avatar mr-2" src="https://avatars.githubusercontent.com/u/31960995?s=40&v=4" width="20" height="20" alt=""> &nbsp; [zhutoutoutousan](https://github.com/zhutoutoutousan) / [worldquant-miner](https://github.com/zhutoutoutousan/worldquant-miner) | 202 |
| <img class="avatar mr-2" src="https://avatars.githubusercontent.com/u/29215115?s=40&v=4" width="20" height="20" alt=""> &nbsp; [iceener](https://github.com/iceener) / [ai](https://github.com/iceener/ai) | 186 |
| <img class="avatar mr-2" src="https://avatars.githubusercontent.com/u/187584218?s=40&v=4" width="20" height="20" alt=""> &nbsp; [giselles-ai](https://github.com/giselles-ai) / [giselle](https://github.com/giselles-ai/giselle) | 181 |
| <img class="avatar mr-2" src="https://avatars.githubusercontent.com/u/174666116?s=40&v=4" width="20" height="20" alt=""> &nbsp; [ai-shifu](https://github.com/ai-shifu) / [ai-shifu](https://github.com/ai-shifu/ai-shifu) | 181 |
| <img class="avatar mr-2" src="https://avatars.githubusercontent.com/u/8931462?s=40&v=4" width="20" height="20" alt=""> &nbsp; [aws-samples](https://github.com/aws-samples) / [sample-serverless-mcp-servers](https://github.com/aws-samples/sample-serverless-mcp-servers) | 175 |
| <img class="avatar mr-2" src="https://avatars.githubusercontent.com/u/202488052?s=40&v=4" width="20" height="20" alt=""> &nbsp; [celerforge](https://github.com/celerforge) / [freenote](https://github.com/celerforge/freenote) | 171 |
| <img class="avatar mr-2" src="https://avatars.githubusercontent.com/u/104478511?s=40&v=4" width="20" height="20" alt=""> &nbsp; [babelcloud](https://github.com/babelcloud) / [LLM-RGB](https://github.com/babelcloud/LLM-RGB) | 164 |
| <img class="avatar mr-2" src="https://avatars.githubusercontent.com/u/171735272?s=40&v=4" width="20" height="20" alt=""> &nbsp; [8090-inc](https://github.com/8090-inc) / [xrx-sample-apps](https://github.com/8090-inc/xrx-sample-apps) | 163 |
| <img class="avatar mr-2" src="https://avatars.githubusercontent.com/u/51827949?s=40&v=4" width="20" height="20" alt=""> &nbsp; [deepset-ai](https://github.com/deepset-ai) / [haystack-core-integrations](https://github.com/deepset-ai/haystack-core-integrations) | 163 |
| <img class="avatar mr-2" src="https://avatars.githubusercontent.com/u/1009716?s=40&v=4" width="20" height="20" alt=""> &nbsp; [codecentric](https://github.com/codecentric) / [c4-genai-suite](https://github.com/codecentric/c4-genai-suite) | 152 |
| <img class="avatar mr-2" src="https://avatars.githubusercontent.com/u/196509932?s=40&v=4" width="20" height="20" alt=""> &nbsp; [XSpoonAi](https://github.com/XSpoonAi) / [spoon-core](https://github.com/XSpoonAi/spoon-core) | 150 |
| <img class="avatar mr-2" src="https://avatars.githubusercontent.com/u/139558948?s=40&v=4" width="20" height="20" alt=""> &nbsp; [chatchat-space](https://github.com/chatchat-space) / [LangGraph-Chatchat](https://github.com/chatchat-space/LangGraph-Chatchat) | 144 |
| <img class="avatar mr-2" src="https://avatars.githubusercontent.com/u/134601687?s=40&v=4" width="20" height="20" alt=""> &nbsp; [langfuse](https://github.com/langfuse) / [langfuse-docs](https://github.com/langfuse/langfuse-docs) | 139 |
| <img class="avatar mr-2" src="https://avatars.githubusercontent.com/u/44976328?s=40&v=4" width="20" height="20" alt=""> &nbsp; [piyushgarg-dev](https://github.com/piyushgarg-dev) / [genai-cohort](https://github.com/piyushgarg-dev/genai-cohort) | 135 |
| <img class="avatar mr-2" src="https://avatars.githubusercontent.com/u/105285801?s=40&v=4" width="20" height="20" alt=""> &nbsp; [i-dot-ai](https://github.com/i-dot-ai) / [redbox](https://github.com/i-dot-ai/redbox) | 132 |
| <img class="avatar mr-2" src="https://avatars.githubusercontent.com/u/90423581?s=40&v=4" width="20" height="20" alt=""> &nbsp; [bmd1905](https://github.com/bmd1905) / [ChatOpsLLM](https://github.com/bmd1905/ChatOpsLLM) | 127 |
| <img class="avatar mr-2" src="https://avatars.githubusercontent.com/u/202074444?s=40&v=4" width="20" height="20" alt=""> &nbsp; [Fintech-Dreamer](https://github.com/Fintech-Dreamer) / [FinSynth](https://github.com/Fintech-Dreamer/FinSynth) | 121 |
| <img class="avatar mr-2" src="https://avatars.githubusercontent.com/u/1566555?s=40&v=4" width="20" height="20" alt=""> &nbsp; [kenshiro-o](https://github.com/kenshiro-o) / [nagato-ai](https://github.com/kenshiro-o/nagato-ai) | 119 |
## 🔒 Security & Privacy
+13
View File
@@ -13,3 +13,16 @@
### Postgres
- Most `schema.prisma` changes should produce a change in `packages/shared/prisma/migrations`.
### Environment Variables
- Environment variables should be imported from the `env.mjs/ts` file of the respective package and not from `process.env.*` to ensure validation and typing.
## Redis Invocations
- Highlight usage of `redis.call` invocations. Those may have suboptimal redis cluster routing and will raise errors. Instead, use the native call patterns.
Example: `await redis?.call("SET", key, "1", "NX", "EX", TTLSeconds);` should use `await redis?.set(key, "1", "EX", TTLSeconds, "NX");` instead.
## Langfuse Cloud
- When attempting to confirm if the current environment is Langfuse Cloud in the frontend, use the `useLangfuseCloudRegion` hook and never environment variables directly.
+15 -13
View File
@@ -17,11 +17,11 @@ services:
ports:
- "3000:3000"
environment: &langfuse-web-env
DATABASE_URL: postgresql://postgres:postgres@postgres:5432/postgres
NEXTAUTH_SECRET: mysecret
SALT: mysalt
ENCRYPTION_KEY: "0000000000000000000000000000000000000000000000000000000000000000" # generate via `openssl rand -hex 32`
NEXTAUTH_URL: http://localhost:3000
DATABASE_URL: ${DATABASE_URL:-postgresql://postgres:postgres@postgres:5432/postgres}
NEXTAUTH_SECRET: ${NEXTAUTH_SECRET:-mysecret}
SALT: ${SALT:-mysalt}
ENCRYPTION_KEY: ${ENCRYPTION_KEY:-0000000000000000000000000000000000000000000000000000000000000000} # generate via `openssl rand -hex 32`
NEXTAUTH_URL: ${NEXTAUTH_URL:-http://localhost:3000}
TELEMETRY_ENABLED: ${TELEMETRY_ENABLED:-true}
LANGFUSE_ENABLE_EXPERIMENTAL_FEATURES: ${LANGFUSE_ENABLE_EXPERIMENTAL_FEATURES:-false}
LANGFUSE_INIT_ORG_ID: ${LANGFUSE_INIT_ORG_ID:-}
@@ -82,8 +82,8 @@ services:
user: "101:101"
environment:
CLICKHOUSE_DB: default
CLICKHOUSE_USER: clickhouse
CLICKHOUSE_PASSWORD: clickhouse
CLICKHOUSE_USER: ${CLICKHOUSE_USER:-clickhouse}
CLICKHOUSE_PASSWORD: ${CLICKHOUSE_PASSWORD:-clickhouse}
volumes:
- langfuse_clickhouse_data:/var/lib/clickhouse
- langfuse_clickhouse_logs:/var/log/clickhouse-server
@@ -103,8 +103,8 @@ services:
# create the 'langfuse' bucket before starting the service
command: -c 'mkdir -p /data/langfuse && minio server --address ":9000" --console-address ":9001" /data'
environment:
MINIO_ROOT_USER: minio
MINIO_ROOT_PASSWORD: miniosecret
MINIO_ROOT_USER: ${MINIO_ROOT_USER:-minio}
MINIO_ROOT_PASSWORD: ${MINIO_ROOT_PASSWORD:-miniosecret}
ports:
- "9090:9000"
- "9091:9001"
@@ -131,7 +131,7 @@ services:
retries: 10
postgres:
image: docker.io/postgres:${POSTGRES_VERSION:-latest}
image: docker.io/postgres:${POSTGRES_VERSION:-17}
restart: always
healthcheck:
test: ["CMD-SHELL", "pg_isready -U postgres"]
@@ -139,9 +139,11 @@ services:
timeout: 3s
retries: 10
environment:
POSTGRES_USER: postgres
POSTGRES_PASSWORD: postgres
POSTGRES_DB: postgres
POSTGRES_USER: ${POSTGRES_USER:-postgres}
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:-postgres}
POSTGRES_DB: ${POSTGRES_DB:-postgres}
TZ: UTC
PGTZ: UTC
ports:
- 5432:5432
volumes:
+8 -6
View File
@@ -4,8 +4,8 @@ services:
user: "101:101"
environment:
CLICKHOUSE_DB: default
CLICKHOUSE_USER: clickhouse
CLICKHOUSE_PASSWORD: clickhouse
CLICKHOUSE_USER: ${CLICKHOUSE_USER:-clickhouse}
CLICKHOUSE_PASSWORD: ${CLICKHOUSE_PASSWORD:-clickhouse}
volumes:
- langfuse_clickhouse_data:/var/lib/clickhouse
- langfuse_clickhouse_logs:/var/log/clickhouse-server
@@ -32,7 +32,7 @@ services:
- 6379:6379
postgres:
image: docker.io/postgres:${POSTGRES_VERSION:-latest}
image: docker.io/postgres:${POSTGRES_VERSION:-17}
restart: always
healthcheck:
test: ["CMD-SHELL", "pg_isready -U postgres"]
@@ -41,9 +41,11 @@ services:
retries: 10
command: ["postgres", "-c", "log_statement=all"]
environment:
- POSTGRES_USER=postgres
- POSTGRES_PASSWORD=postgres
- POSTGRES_DB=postgres
- POSTGRES_USER=${POSTGRES_USER:-postgres}
- POSTGRES_PASSWORD=${POSTGRES_PASSWORD:-postgres}
- POSTGRES_DB=${POSTGRES_DB:-postgres}
- TZ=UTC
- PGTZ=UTC
ports:
- 5432:5432
volumes:
+16 -14
View File
@@ -4,8 +4,8 @@ services:
user: "101:101"
environment:
CLICKHOUSE_DB: default
CLICKHOUSE_USER: clickhouse
CLICKHOUSE_PASSWORD: clickhouse
CLICKHOUSE_USER: ${CLICKHOUSE_USER:-clickhouse}
CLICKHOUSE_PASSWORD: ${CLICKHOUSE_PASSWORD:-clickhouse}
volumes:
- langfuse_clickhouse_data:/var/lib/clickhouse
- langfuse_clickhouse_logs:/var/log/clickhouse-server
@@ -21,8 +21,8 @@ services:
# create the 'langfuse' bucket before starting the service
command: -c 'mkdir -p /data/langfuse && minio server --address ":9000" --console-address ":9001" /data'
environment:
MINIO_ACCESS_KEY: minio
MINIO_SECRET_KEY: miniosecret
MINIO_ROOT_USER: ${MINIO_ROOT_USER:-minio}
MINIO_ROOT_PASSWORD: ${MINIO_ROOT_PASSWORD:-miniosecret}
ports:
- 127.0.0.1:9090:9000
- 127.0.0.1:9091:9001
@@ -36,7 +36,7 @@ services:
start_period: 1s
postgres:
image: docker.io/postgres:${POSTGRES_VERSION:-latest}
image: docker.io/postgres:${POSTGRES_VERSION:-17}
restart: always
healthcheck:
test: ["CMD-SHELL", "pg_isready -U postgres"]
@@ -45,9 +45,11 @@ services:
retries: 10
command: ["postgres", "-c", "log_statement=all"]
environment:
- POSTGRES_USER=postgres
- POSTGRES_PASSWORD=postgres
- POSTGRES_DB=postgres
- POSTGRES_USER=${POSTGRES_USER:-postgres}
- POSTGRES_PASSWORD=${POSTGRES_PASSWORD:-postgres}
- POSTGRES_DB=${POSTGRES_DB:-postgres}
- TZ=UTC
- PGTZ=UTC
ports:
- 127.0.0.1:5432:5432
volumes:
@@ -55,7 +57,7 @@ services:
# Redis Cluster. Requires a Unix host to work for network_mode: host. I.e. tests will fail on windows and mac with this setup.
redis-node-0:
image: docker.io/bitnami/redis-cluster:8.0
image: docker.io/bitnamilegacy/redis-cluster:8.0
network_mode: host
volumes:
- redis-cluster_data-0:/bitnami/redis/data
@@ -65,7 +67,7 @@ services:
REDIS_NODES: 127.0.0.1:6370 127.0.0.1:6371 127.0.0.1:6372 127.0.0.1:6373 127.0.0.1:6374 127.0.0.1:6375
redis-node-1:
image: docker.io/bitnami/redis-cluster:8.0
image: docker.io/bitnamilegacy/redis-cluster:8.0
network_mode: host
volumes:
- redis-cluster_data-1:/bitnami/redis/data
@@ -75,7 +77,7 @@ services:
REDIS_NODES: 127.0.0.1:6370 127.0.0.1:6371 127.0.0.1:6372 127.0.0.1:6373 127.0.0.1:6374 127.0.0.1:6375
redis-node-2:
image: docker.io/bitnami/redis-cluster:8.0
image: docker.io/bitnamilegacy/redis-cluster:8.0
network_mode: host
volumes:
- redis-cluster_data-2:/bitnami/redis/data
@@ -85,7 +87,7 @@ services:
REDIS_NODES: 127.0.0.1:6370 127.0.0.1:6371 127.0.0.1:6372 127.0.0.1:6373 127.0.0.1:6374 127.0.0.1:6375
redis-node-3:
image: docker.io/bitnami/redis-cluster:8.0
image: docker.io/bitnamilegacy/redis-cluster:8.0
network_mode: host
volumes:
- redis-cluster_data-3:/bitnami/redis/data
@@ -95,7 +97,7 @@ services:
REDIS_NODES: 127.0.0.1:6370 127.0.0.1:6371 127.0.0.1:6372 127.0.0.1:6373 127.0.0.1:6374 127.0.0.1:6375
redis-node-4:
image: docker.io/bitnami/redis-cluster:8.0
image: docker.io/bitnamilegacy/redis-cluster:8.0
network_mode: host
volumes:
- redis-cluster_data-4:/bitnami/redis/data
@@ -105,7 +107,7 @@ services:
REDIS_NODES: 127.0.0.1:6370 127.0.0.1:6371 127.0.0.1:6372 127.0.0.1:6373 127.0.0.1:6374 127.0.0.1:6375
redis-node-5:
image: docker.io/bitnami/redis-cluster:8.0
image: docker.io/bitnamilegacy/redis-cluster:8.0
network_mode: host
volumes:
- redis-cluster_data-5:/bitnami/redis/data
+13 -9
View File
@@ -1,11 +1,13 @@
services:
clickhouse:
image: docker.io/clickhouse/clickhouse-server:24.3
# Upgrade from 24.3 to check behaviour on new events table.
# Unit tests still verify 24.3 behaviour using -azure and -redis-cluster configs.
image: docker.io/clickhouse/clickhouse-server:25.8
user: "101:101"
environment:
CLICKHOUSE_DB: default
CLICKHOUSE_USER: clickhouse
CLICKHOUSE_PASSWORD: clickhouse
CLICKHOUSE_USER: ${CLICKHOUSE_USER:-clickhouse}
CLICKHOUSE_PASSWORD: ${CLICKHOUSE_PASSWORD:-clickhouse}
volumes:
- langfuse_clickhouse_data:/var/lib/clickhouse
- langfuse_clickhouse_logs:/var/log/clickhouse-server
@@ -21,8 +23,8 @@ services:
# create the 'langfuse' bucket before starting the service
command: -c 'mkdir -p /data/langfuse && minio server --address ":9000" --console-address ":9001" /data'
environment:
MINIO_ACCESS_KEY: minio
MINIO_SECRET_KEY: miniosecret
MINIO_ROOT_USER: ${MINIO_ROOT_USER:-minio}
MINIO_ROOT_PASSWORD: ${MINIO_ROOT_PASSWORD:-miniosecret}
ports:
- 127.0.0.1:9090:9000
- 127.0.0.1:9091:9001
@@ -44,7 +46,7 @@ services:
- 127.0.0.1:6379:6379
postgres:
image: docker.io/postgres:${POSTGRES_VERSION:-latest}
image: docker.io/postgres:${POSTGRES_VERSION:-17}
restart: always
healthcheck:
test: ["CMD-SHELL", "pg_isready -U postgres"]
@@ -53,9 +55,11 @@ services:
retries: 10
command: ["postgres", "-c", "log_statement=all"]
environment:
- POSTGRES_USER=postgres
- POSTGRES_PASSWORD=postgres
- POSTGRES_DB=postgres
- POSTGRES_USER=${POSTGRES_USER:-postgres}
- POSTGRES_PASSWORD=${POSTGRES_PASSWORD:-postgres}
- POSTGRES_DB=${POSTGRES_DB:-postgres}
- TZ=UTC
- PGTZ=UTC
ports:
- 127.0.0.1:5432:5432
volumes:
+15 -13
View File
@@ -19,10 +19,10 @@ services:
ports:
- 127.0.0.1:3030:3030
environment: &langfuse-worker-env
NEXTAUTH_URL: http://localhost:3000
DATABASE_URL: postgresql://postgres:postgres@postgres:5432/postgres # CHANGEME
SALT: "mysalt" # CHANGEME
ENCRYPTION_KEY: "0000000000000000000000000000000000000000000000000000000000000000" # CHANGEME: generate via `openssl rand -hex 32`
NEXTAUTH_URL: ${NEXTAUTH_URL:-http://localhost:3000}
DATABASE_URL: ${DATABASE_URL:-postgresql://postgres:postgres@postgres:5432/postgres} # CHANGEME
SALT: ${SALT:-mysalt} # CHANGEME
ENCRYPTION_KEY: ${ENCRYPTION_KEY:-0000000000000000000000000000000000000000000000000000000000000000} # CHANGEME: generate via `openssl rand -hex 32`
TELEMETRY_ENABLED: ${TELEMETRY_ENABLED:-true}
LANGFUSE_ENABLE_EXPERIMENTAL_FEATURES: ${LANGFUSE_ENABLE_EXPERIMENTAL_FEATURES:-true}
CLICKHOUSE_MIGRATION_URL: ${CLICKHOUSE_MIGRATION_URL:-clickhouse://clickhouse:9000}
@@ -74,7 +74,7 @@ services:
- 3000:3000
environment:
<<: *langfuse-worker-env
NEXTAUTH_SECRET: mysecret # CHANGEME
NEXTAUTH_SECRET: ${NEXTAUTH_SECRET:-mysecret} # CHANGEME
LANGFUSE_INIT_ORG_ID: ${LANGFUSE_INIT_ORG_ID:-}
LANGFUSE_INIT_ORG_NAME: ${LANGFUSE_INIT_ORG_NAME:-}
LANGFUSE_INIT_PROJECT_ID: ${LANGFUSE_INIT_PROJECT_ID:-}
@@ -91,8 +91,8 @@ services:
user: "101:101"
environment:
CLICKHOUSE_DB: default
CLICKHOUSE_USER: clickhouse
CLICKHOUSE_PASSWORD: clickhouse # CHANGEME
CLICKHOUSE_USER: ${CLICKHOUSE_USER:-clickhouse}
CLICKHOUSE_PASSWORD: ${CLICKHOUSE_PASSWORD:-clickhouse} # CHANGEME
volumes:
- langfuse_clickhouse_data:/var/lib/clickhouse
- langfuse_clickhouse_logs:/var/log/clickhouse-server
@@ -113,8 +113,8 @@ services:
# create the 'langfuse' bucket before starting the service
command: -c 'mkdir -p /data/langfuse && minio server --address ":9000" --console-address ":9001" /data'
environment:
MINIO_ROOT_USER: minio
MINIO_ROOT_PASSWORD: miniosecret # CHANGEME
MINIO_ROOT_USER: ${MINIO_ROOT_USER:-minio}
MINIO_ROOT_PASSWORD: ${MINIO_ROOT_PASSWORD:-miniosecret} # CHANGEME
ports:
- 9090:9000
- 127.0.0.1:9091:9001
@@ -142,7 +142,7 @@ services:
retries: 10
postgres:
image: docker.io/postgres:${POSTGRES_VERSION:-latest}
image: docker.io/postgres:${POSTGRES_VERSION:-17}
restart: always
healthcheck:
test: ["CMD-SHELL", "pg_isready -U postgres"]
@@ -150,9 +150,11 @@ services:
timeout: 3s
retries: 10
environment:
POSTGRES_USER: postgres
POSTGRES_PASSWORD: postgres # CHANGEME
POSTGRES_DB: postgres
POSTGRES_USER: ${POSTGRES_USER:-postgres}
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:-postgres} # CHANGEME
POSTGRES_DB: ${POSTGRES_DB:-postgres}
TZ: UTC
PGTZ: UTC
ports:
- 127.0.0.1:5432:5432
volumes:
+3 -3
View File
@@ -15,7 +15,7 @@
}
},
"engines": {
"node": "20"
"node": "24"
},
"scripts": {
"build": "tsc",
@@ -27,14 +27,14 @@
"@langfuse/shared": "workspace:*",
"@opentelemetry/api": ">=1.0.0 <1.10.0",
"https-proxy-agent": "^7.0.6",
"next": "^14.2.30",
"next": "15.5.4",
"next-auth": "^4.24.11",
"zod": "^3.25.62"
},
"devDependencies": {
"@repo/eslint-config": "workspace:*",
"@repo/typescript-config": "workspace:*",
"@types/node": "^20.11.29",
"@types/node": "^24.3.0",
"@typescript-eslint/parser": "^7.12.0",
"eslint": "^8.57.0",
"eslint-config-prettier": "^9.1.0",
+2 -2
View File
@@ -3,10 +3,10 @@
"compilerOptions": {
"moduleResolution": "NodeNext",
"module": "NodeNext",
"lib": ["ES2020"],
"lib": ["es2023"],
"outDir": "./dist",
"types": ["node"],
"target": "ES2020",
"target": "es2024",
"rootDir": "."
},
"include": ["."],
@@ -0,0 +1,119 @@
# yaml-language-server: $schema=https://raw.githubusercontent.com/fern-api/fern/main/fern.schema.json
imports:
commons: ./commons.yml
service:
auth: true
base-path: /api/public/integrations/blob-storage
endpoints:
getBlobStorageIntegrations:
docs: Get all blob storage integrations for the organization (requires organization-scoped API key)
method: GET
path: ""
response: BlobStorageIntegrationsResponse
upsertBlobStorageIntegration:
docs: 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.
method: PUT
path: ""
request: CreateBlobStorageIntegrationRequest
response: BlobStorageIntegrationResponse
deleteBlobStorageIntegration:
docs: Delete a blob storage integration by ID (requires organization-scoped API key)
method: DELETE
path: "/{id}"
path-parameters:
id: string
response: BlobStorageIntegrationDeletionResponse
types:
BlobStorageIntegrationType:
enum:
- S3
- S3_COMPATIBLE
- AZURE_BLOB_STORAGE
BlobStorageIntegrationFileType:
enum:
- JSON
- CSV
- JSONL
BlobStorageExportMode:
enum:
- FULL_HISTORY
- FROM_TODAY
- FROM_CUSTOM_DATE
BlobStorageExportFrequency:
enum:
- hourly
- daily
- weekly
CreateBlobStorageIntegrationRequest:
properties:
projectId:
type: string
docs: ID of the project in which to configure the blob storage integration
type: BlobStorageIntegrationType
bucketName:
type: string
docs: Name of the storage bucket
endpoint:
type: optional<string>
docs: Custom endpoint URL (required for S3_COMPATIBLE type)
region:
type: string
docs: Storage region
accessKeyId:
type: optional<string>
docs: Access key ID for authentication
secretAccessKey:
type: optional<string>
docs: Secret access key for authentication (will be encrypted when stored)
prefix:
type: optional<string>
docs: Path prefix for exported files (must end with forward slash if provided)
exportFrequency: BlobStorageExportFrequency
enabled:
type: boolean
docs: Whether the integration is active
forcePathStyle:
type: boolean
docs: Use path-style URLs for S3 requests
fileType: BlobStorageIntegrationFileType
exportMode: BlobStorageExportMode
exportStartDate:
type: optional<datetime>
docs: Custom start date for exports (required when exportMode is FROM_CUSTOM_DATE)
BlobStorageIntegrationResponse:
properties:
id: string
projectId: string
type: BlobStorageIntegrationType
bucketName: string
endpoint: optional<string>
region: string
accessKeyId: optional<string>
prefix: string
exportFrequency: BlobStorageExportFrequency
enabled: boolean
forcePathStyle: boolean
fileType: BlobStorageIntegrationFileType
exportMode: BlobStorageExportMode
exportStartDate: optional<datetime>
nextSyncAt: optional<datetime>
lastSyncAt: optional<datetime>
createdAt: datetime
updatedAt: datetime
BlobStorageIntegrationsResponse:
properties:
data: list<BlobStorageIntegrationResponse>
BlobStorageIntegrationDeletionResponse:
properties:
message: string
+3 -2
View File
@@ -8,8 +8,9 @@ service:
endpoints:
batch:
docs: |
Batched ingestion for Langfuse Tracing.
If you want to use tracing via the API, such as to build your own Langfuse client implementation, this is the only API route you need to implement.
**Legacy endpoint for batch ingestion for Langfuse Observability.**
-> Please use the OpenTelemetry endpoint (`/api/public/otel`). 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.
@@ -35,6 +35,18 @@ service:
type: string
docs: The unique langfuse identifier of a score config
response: commons.ScoreConfig
update:
docs: Update a score config
method: PATCH
path: /score-configs/{configId}
path-parameters:
configId:
type: string
docs: The unique langfuse identifier of a score config
request: UpdateScoreConfigRequest
response: commons.ScoreConfig
types:
ScoreConfigs:
properties:
@@ -56,3 +68,23 @@ types:
description:
type: optional<string>
docs: 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
UpdateScoreConfigRequest:
properties:
isArchived:
type: optional<boolean>
docs: The status of the score config showing if it is archived or not
name:
type: optional<string>
docs: The name of the score config
categories:
type: optional<list<commons.ConfigCategory>>
docs: 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: optional<double>
docs: Configure a minimum value for numerical scores. If not set, the minimum value defaults to -∞
maxValue:
type: optional<double>
docs: Configure a maximum value for numerical scores. If not set, the maximum value defaults to +∞
description:
type: optional<string>
docs: 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
+3
View File
@@ -50,6 +50,9 @@ service:
configId:
type: optional<string>
docs: Retrieve only scores with a specific configId.
sessionId:
type: optional<string>
docs: Retrieve only scores with a specific sessionId.
queueId:
type: optional<string>
docs: Retrieve only scores with a specific annotation queueId.
+27 -1
View File
@@ -65,7 +65,33 @@ service:
docs: Optional filter for traces where the environment is one of the provided values.
fields:
type: optional<string>
docs: "Comma-separated list of fields to include in the response. Available field groups are 'core' (always included), 'io' (input, output, metadata), 'scores', 'observations', 'metrics'. If not provided, all fields are included. Example: 'core,scores,metrics'"
docs: "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'."
filter:
type: optional<string>
docs: |
JSON string containing an array of filter conditions. When provided, this takes precedence over legacy filter parameters (userId, name, sessionId, tags, version, release, environment, fromTimestamp, toTimestamp).
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
"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
}
]
```
response: Traces
deleteMultiple:
docs: Delete multiple traces
+1 -1
View File
@@ -31,7 +31,7 @@ groups:
# client-class-name: LangfuseClient
- name: fernapi/fern-typescript-node-sdk
version: 2.6.1
version: 2.12.3
output:
location: local-file-system
path: ../../../generated/typescript
+2 -2
View File
@@ -1,4 +1,4 @@
{
"organization": "langfuse",
"version": "0.56.0"
}
"version": "0.77.5"
}
+8 -8
View File
@@ -1,11 +1,11 @@
{
"name": "langfuse",
"version": "3.103.0",
"version": "3.117.2",
"author": "engineering@langfuse.com",
"license": "MIT",
"private": true,
"engines": {
"node": "20"
"node": "24"
},
"scripts": {
"preinstall": "npx only-allow pnpm",
@@ -24,8 +24,8 @@
"start": "turbo run start",
"dev": "turbo run dev",
"dev:worker": "turbo run dev --filter=worker",
"dev:web": "turbo run dev --filter=web",
"dev:web-turbo": "turbo run dev --filter=web -- --turbo",
"dev:web": "turbo run dev --filter=web -- --turbo",
"dev:web-no-turbo": "turbo run dev --filter=web",
"lint": "turbo run lint",
"format": "prettier --write \"**/*.{js,jsx,ts,tsx,css}\" --experimental-cli",
"format:check": "prettier --check \"**/*.{js,jsx,ts,tsx,css}\" --experimental-cli",
@@ -36,11 +36,11 @@
"devDependencies": {
"@release-it/bumper": "^7.0.5",
"braces": "3.0.3",
"dotenv-cli": "^7.4.2",
"husky": "^9.0.11",
"dotenv-cli": "^7.4.4",
"husky": "^9.1.7",
"prettier": "^3.6.2",
"release-it": "^19.0.3",
"turbo": "^2.5.6"
"release-it": "^19.0.4",
"turbo": "^2.5.8"
},
"release-it": {
"git": {
+9 -2
View File
@@ -2,10 +2,15 @@ const { resolve } = require("node:path");
const project = resolve(process.cwd(), "tsconfig.json");
// Handle eslint-config-turbo's default export
const turboConfig = require("eslint-config-turbo");
const turboConfigToUse = turboConfig.default || turboConfig;
/** @type {import("eslint").Linter.Config} */
module.exports = {
extends: ["eslint:recommended", "prettier", "eslint-config-turbo"],
plugins: ["only-warn"],
// extends: ["eslint:recommended", "prettier"require().default],
extends: ["eslint:recommended", "prettier"],
plugins: ["only-warn", "turbo"],
globals: {
React: true,
JSX: true,
@@ -30,6 +35,7 @@ module.exports = {
rules: {
"no-redeclare": "off",
"import/order": "off",
...(turboConfigToUse.rules || {}),
},
overrides: [
{
@@ -51,5 +57,6 @@ module.exports = {
],
},
},
...(turboConfigToUse.overrides || []),
],
};
+4 -1
View File
@@ -2,6 +2,9 @@ const { resolve } = require("node:path");
const project = resolve(process.cwd(), "tsconfig.json");
const turboConfig = require("eslint-config-turbo");
const turboConfigToUse = turboConfig.default || turboConfig;
/*
* This is a custom ESLint configuration for use with
* Next.js apps.
@@ -16,7 +19,6 @@ module.exports = {
extends: [
"plugin:@typescript-eslint/recommended",
"plugin:@typescript-eslint/strict-type-checked",
"eslint-config-turbo",
],
rules: {
"@typescript-eslint/no-non-null-assertion": "off",
@@ -47,6 +49,7 @@ module.exports = {
ignorePatterns: ["node_modules/", "dist/"],
// add rules configurations here
rules: {
...(turboConfigToUse.rules || {}),
"@typescript-eslint/consistent-type-imports": [
"warn",
{
+1 -1
View File
@@ -13,7 +13,7 @@
"@vercel/style-guide": "^6.0.0",
"eslint-config-next": "^14.2.15",
"eslint-config-prettier": "^9.1.0",
"eslint-config-turbo": "^2.5.6",
"eslint-config-turbo": "^2.5.8",
"eslint-plugin-only-warn": "^1.1.0",
"typescript": "^5.7.2"
}
+23 -7
View File
@@ -3,9 +3,17 @@
"display": "Next.js",
"extends": "./base.json",
"compilerOptions": {
"plugins": [{ "name": "next" }],
"target": "es2017",
"lib": ["dom", "dom.iterable", "esnext"],
"plugins": [
{
"name": "next"
}
],
"target": "es2024",
"lib": [
"dom",
"dom.iterable",
"esnext"
],
"allowJs": true,
"checkJs": true,
"skipLibCheck": true,
@@ -18,8 +26,16 @@
"resolveJsonModule": true,
"isolatedModules": true,
"jsx": "preserve",
"types": ["jest", "node"],
"types": [
"jest",
"node"
],
},
"include": ["src", "next-env.d.ts"],
"exclude": ["node_modules"]
}
"include": [
"src",
"next-env.d.ts"
],
"exclude": [
"node_modules"
]
}
@@ -0,0 +1,21 @@
-- Recreate materialized views for project_environments table
CREATE MATERIALIZED VIEW project_environments_traces_mv ON CLUSTER default TO project_environments AS
SELECT
project_id,
groupUniqArray(environment) AS environments
FROM traces
GROUP BY project_id;
CREATE MATERIALIZED VIEW project_environments_observations_mv ON CLUSTER default TO project_environments AS
SELECT
project_id,
groupUniqArray(environment) AS environments
FROM observations
GROUP BY project_id;
CREATE MATERIALIZED VIEW project_environments_scores_mv ON CLUSTER default TO project_environments AS
SELECT
project_id,
groupUniqArray(environment) AS environments
FROM scores
GROUP BY project_id;
@@ -0,0 +1,4 @@
-- Drop materialized views feeding project_environments table
DROP VIEW IF EXISTS project_environments_traces_mv ON CLUSTER default;
DROP VIEW IF EXISTS project_environments_observations_mv ON CLUSTER default;
DROP VIEW IF EXISTS project_environments_scores_mv ON CLUSTER default;
@@ -0,0 +1,114 @@
-- Recreate materialized views derived from traces_null table
CREATE MATERIALIZED VIEW IF NOT EXISTS traces_all_amt_mv ON CLUSTER default TO traces_all_amt AS
SELECT
-- Identifiers
tn.project_id as project_id,
tn.id as id,
min(tn.start_time) as timestamp, -- Backward compatibility: redundant with start_time
min(tn.start_time) as start_time,
max(coalesce(tn.end_time, tn.start_time)) as end_time,
anyLast(tn.name) as name,
-- Metadata properties
maxMap(tn.metadata) as metadata,
anyLast(tn.user_id) as user_id,
anyLast(tn.session_id) as session_id,
anyLast(tn.environment) as environment,
groupUniqArrayArray(tn.tags) as tags,
anyLast(tn.version) as version,
anyLast(tn.release) as release,
-- UI properties
argMaxState(tn.bookmarked, if(tn.bookmarked is not null, tn.event_ts, toDateTime64(0, 3))) as bookmarked,
argMaxState(tn.public, if(tn.public is not null, tn.event_ts, toDateTime64(0, 3))) as public,
-- Aggregations -- DO NOT USE
groupUniqArrayArray(tn.observation_ids) as observation_ids,
groupUniqArrayArray(tn.score_ids) as score_ids,
sumMap(tn.cost_details) as cost_details,
sumMap(tn.usage_details) as usage_details,
-- Input/Output
argMaxState(tn.input, if(tn.input <> '', tn.event_ts, toDateTime64(0, 3))) as input,
argMaxState(tn.output, if(tn.output <> '', tn.event_ts, toDateTime64(0, 3))) as output,
min(tn.created_at) as created_at,
max(tn.updated_at) as updated_at
FROM traces_null tn
GROUP BY project_id, id;
CREATE MATERIALIZED VIEW IF NOT EXISTS traces_7d_amt_mv ON CLUSTER default TO traces_7d_amt AS
SELECT
-- Identifiers
tn.project_id as project_id,
tn.id as id,
min(tn.start_time) as timestamp, -- Backward compatibility: redundant with start_time
min(tn.start_time) as start_time,
max(coalesce(tn.end_time, tn.start_time)) as end_time,
anyLast(tn.name) as name,
-- Metadata properties
maxMap(tn.metadata) as metadata,
anyLast(tn.user_id) as user_id,
anyLast(tn.session_id) as session_id,
anyLast(tn.environment) as environment,
groupUniqArrayArray(tn.tags) as tags,
anyLast(tn.version) as version,
anyLast(tn.release) as release,
-- UI properties
argMaxState(tn.bookmarked, if(tn.bookmarked is not null, tn.event_ts, toDateTime64(0, 3))) as bookmarked,
argMaxState(tn.public, if(tn.public is not null, tn.event_ts, toDateTime64(0, 3))) as public,
-- Aggregations -- DO NOT USE
groupUniqArrayArray(tn.observation_ids) as observation_ids,
groupUniqArrayArray(tn.score_ids) as score_ids,
sumMap(tn.cost_details) as cost_details,
sumMap(tn.usage_details) as usage_details,
-- Input/Output
argMaxState(tn.input, if(tn.input <> '', tn.event_ts, toDateTime64(0, 3))) as input,
argMaxState(tn.output, if(tn.output <> '', tn.event_ts, toDateTime64(0, 3))) as output,
min(tn.created_at) as created_at,
max(tn.updated_at) as updated_at
FROM traces_null tn
GROUP BY project_id, id;
CREATE MATERIALIZED VIEW IF NOT EXISTS traces_30d_amt_mv ON CLUSTER default TO traces_30d_amt AS
SELECT
-- Identifiers
tn.project_id as project_id,
tn.id as id,
min(tn.start_time) as timestamp, -- Backward compatibility: redundant with start_time
min(tn.start_time) as start_time,
max(coalesce(tn.end_time, tn.start_time)) as end_time,
anyLast(tn.name) as name,
-- Metadata properties
maxMap(tn.metadata) as metadata,
anyLast(tn.user_id) as user_id,
anyLast(tn.session_id) as session_id,
anyLast(tn.environment) as environment,
groupUniqArrayArray(tn.tags) as tags,
anyLast(tn.version) as version,
anyLast(tn.release) as release,
-- UI properties
argMaxState(tn.bookmarked, if(tn.bookmarked is not null, tn.event_ts, toDateTime64(0, 3))) as bookmarked,
argMaxState(tn.public, if(tn.public is not null, tn.event_ts, toDateTime64(0, 3))) as public,
-- Aggregations -- DO NOT USE
groupUniqArrayArray(tn.observation_ids) as observation_ids,
groupUniqArrayArray(tn.score_ids) as score_ids,
sumMap(tn.cost_details) as cost_details,
sumMap(tn.usage_details) as usage_details,
-- Input/Output
argMaxState(tn.input, if(tn.input <> '', tn.event_ts, toDateTime64(0, 3))) as input,
argMaxState(tn.output, if(tn.output <> '', tn.event_ts, toDateTime64(0, 3))) as output,
min(tn.created_at) as created_at,
max(tn.updated_at) as updated_at
FROM traces_null tn
GROUP BY project_id, id;
@@ -0,0 +1,4 @@
-- Drop materialized views derived from traces_null table
DROP VIEW IF EXISTS traces_all_amt_mv ON CLUSTER default;
DROP VIEW IF EXISTS traces_7d_amt_mv ON CLUSTER default;
DROP VIEW IF EXISTS traces_30d_amt_mv ON CLUSTER default;
@@ -0,0 +1,179 @@
-- Recreate traces_null and trace amt tables
CREATE TABLE traces_null ON CLUSTER default
(
-- Identifiers
`project_id` String,
`id` String,
`start_time` DateTime64(3),
`end_time` Nullable(DateTime64(3)),
`name` Nullable(String),
-- Metadata properties
`metadata` Map(LowCardinality(String), String),
`user_id` Nullable(String),
`session_id` Nullable(String),
`environment` String,
`tags` Array(String),
`version` Nullable(String),
`release` Nullable(String),
-- UI properties - We make them nullable to prevent absent values being interpreted as overwrites.
`bookmarked` Nullable(Bool),
`public` Nullable(Bool),
-- Aggregations -- DO NOT USE
`observation_ids` Array(String),
`score_ids` Array(String),
`cost_details` Map(String, Decimal64(12)),
`usage_details` Map(String, UInt64),
-- TODO: Do we want to aggregate/collect `levels` seen within the trace?
-- Input/Output
`input` String,
`output` String,
`created_at` DateTime64(3),
`updated_at` DateTime64(3),
`event_ts` DateTime64(3)
) Engine = Null();
CREATE TABLE traces_all_amt ON CLUSTER default
(
-- Identifiers
`project_id` String,
`id` String,
`timestamp` SimpleAggregateFunction(min, DateTime64(3)), -- Backward compatibility: redundant with start_time
`start_time` SimpleAggregateFunction(min, DateTime64(3)),
`end_time` SimpleAggregateFunction(max, Nullable(DateTime64(3))),
`name` SimpleAggregateFunction(anyLast, Nullable(String)),
-- Metadata properties
`metadata` SimpleAggregateFunction(maxMap, Map(String, String)),
`user_id` SimpleAggregateFunction(anyLast, Nullable(String)),
`session_id` SimpleAggregateFunction(anyLast, Nullable(String)),
`environment` SimpleAggregateFunction(anyLast, String),
`tags` SimpleAggregateFunction(groupUniqArrayArray, Array(String)),
`version` SimpleAggregateFunction(anyLast, Nullable(String)),
`release` SimpleAggregateFunction(anyLast, Nullable(String)),
-- UI properties
`bookmarked` AggregateFunction(argMax, Nullable(Bool), DateTime64(3)),
`public` AggregateFunction(argMax, Nullable(Bool), DateTime64(3)),
-- Aggregations -- DO NOT USE
`observation_ids` SimpleAggregateFunction(groupUniqArrayArray, Array(String)),
`score_ids` SimpleAggregateFunction(groupUniqArrayArray, Array(String)),
`cost_details` SimpleAggregateFunction(sumMap, Map(String, Decimal(38, 12))),
`usage_details` SimpleAggregateFunction(sumMap, Map(String, UInt64)),
-- Input/Output -> prefer correctness via argMax
`input` AggregateFunction(argMax, String, DateTime64(3)) CODEC (ZSTD(3)),
`output` AggregateFunction(argMax, String, DateTime64(3)) CODEC (ZSTD(3)),
`created_at` SimpleAggregateFunction(min, DateTime64(3)),
`updated_at` SimpleAggregateFunction(max, DateTime64(3)),
-- Indexes
INDEX idx_trace_id id TYPE bloom_filter(0.001) GRANULARITY 1,
INDEX idx_user_id user_id TYPE bloom_filter(0.001) GRANULARITY 1,
INDEX idx_session_id session_id TYPE bloom_filter(0.001) GRANULARITY 1,
INDEX idx_name name TYPE bloom_filter(0.001) GRANULARITY 1,
INDEX idx_version version TYPE bloom_filter(0.001) GRANULARITY 1,
INDEX idx_release release TYPE bloom_filter(0.001) GRANULARITY 1,
INDEX idx_tags tags TYPE bloom_filter(0.001) GRANULARITY 1
) Engine = AggregatingMergeTree()
ORDER BY (project_id, id);
CREATE TABLE traces_7d_amt ON CLUSTER default
(
-- Identifiers
`project_id` String,
`id` String,
`timestamp` SimpleAggregateFunction(min, DateTime64(3)), -- Backward compatibility: redundant with start_time
`start_time` SimpleAggregateFunction(min, DateTime64(3)),
`end_time` SimpleAggregateFunction(max, Nullable(DateTime64(3))),
`name` SimpleAggregateFunction(anyLast, Nullable(String)),
-- Metadata properties
`metadata` SimpleAggregateFunction(maxMap, Map(String, String)),
`user_id` SimpleAggregateFunction(anyLast, Nullable(String)),
`session_id` SimpleAggregateFunction(anyLast, Nullable(String)),
`environment` SimpleAggregateFunction(anyLast, String),
`tags` SimpleAggregateFunction(groupUniqArrayArray, Array(String)),
`version` SimpleAggregateFunction(anyLast, Nullable(String)),
`release` SimpleAggregateFunction(anyLast, Nullable(String)),
-- UI properties
`bookmarked` AggregateFunction(argMax, Nullable(Bool), DateTime64(3)),
`public` AggregateFunction(argMax, Nullable(Bool), DateTime64(3)),
-- Aggregations -- DO NOT USE
`observation_ids` SimpleAggregateFunction(groupUniqArrayArray, Array(String)),
`score_ids` SimpleAggregateFunction(groupUniqArrayArray, Array(String)),
`cost_details` SimpleAggregateFunction(sumMap, Map(String, Decimal(38, 12))),
`usage_details` SimpleAggregateFunction(sumMap, Map(String, UInt64)),
-- Input/Output -> prefer correctness via argMax
`input` AggregateFunction(argMax, String, DateTime64(3)) CODEC (ZSTD(3)),
`output` AggregateFunction(argMax, String, DateTime64(3)) CODEC (ZSTD(3)),
`created_at` SimpleAggregateFunction(min, DateTime64(3)),
`updated_at` SimpleAggregateFunction(max, DateTime64(3)),
-- Indexes
INDEX idx_user_id user_id TYPE bloom_filter(0.001) GRANULARITY 1,
INDEX idx_session_id session_id TYPE bloom_filter(0.001) GRANULARITY 1,
INDEX idx_name name TYPE bloom_filter(0.001) GRANULARITY 1,
INDEX idx_version version TYPE bloom_filter(0.001) GRANULARITY 1,
INDEX idx_release release TYPE bloom_filter(0.001) GRANULARITY 1,
INDEX idx_tags tags TYPE bloom_filter(0.001) GRANULARITY 1
) Engine = AggregatingMergeTree()
ORDER BY (project_id, id)
TTL toDate(start_time) + INTERVAL 7 DAY;
CREATE TABLE traces_30d_amt ON CLUSTER default
(
-- Identifiers
`project_id` String,
`id` String,
`timestamp` SimpleAggregateFunction(min, DateTime64(3)), -- Backward compatibility: redundant with start_time
`start_time` SimpleAggregateFunction(min, DateTime64(3)),
`end_time` SimpleAggregateFunction(max, Nullable(DateTime64(3))),
`name` SimpleAggregateFunction(anyLast, Nullable(String)),
-- Metadata properties
`metadata` SimpleAggregateFunction(maxMap, Map(String, String)),
`user_id` SimpleAggregateFunction(anyLast, Nullable(String)),
`session_id` SimpleAggregateFunction(anyLast, Nullable(String)),
`environment` SimpleAggregateFunction(anyLast, String),
`tags` SimpleAggregateFunction(groupUniqArrayArray, Array(String)),
`version` SimpleAggregateFunction(anyLast, Nullable(String)),
`release` SimpleAggregateFunction(anyLast, Nullable(String)),
-- UI properties
`bookmarked` AggregateFunction(argMax, Nullable(Bool), DateTime64(3)),
`public` AggregateFunction(argMax, Nullable(Bool), DateTime64(3)),
-- Aggregations -- DO NOT USE
`observation_ids` SimpleAggregateFunction(groupUniqArrayArray, Array(String)),
`score_ids` SimpleAggregateFunction(groupUniqArrayArray, Array(String)),
`cost_details` SimpleAggregateFunction(sumMap, Map(String, Decimal(38, 12))),
`usage_details` SimpleAggregateFunction(sumMap, Map(String, UInt64)),
-- Input/Output -> prefer correctness via argMax
`input` AggregateFunction(argMax, String, DateTime64(3)) CODEC (ZSTD(3)),
`output` AggregateFunction(argMax, String, DateTime64(3)) CODEC (ZSTD(3)),
`created_at` SimpleAggregateFunction(min, DateTime64(3)),
`updated_at` SimpleAggregateFunction(max, DateTime64(3)),
-- Indexes
INDEX idx_user_id user_id TYPE bloom_filter(0.001) GRANULARITY 1,
INDEX idx_session_id session_id TYPE bloom_filter(0.001) GRANULARITY 1,
INDEX idx_name name TYPE bloom_filter(0.001) GRANULARITY 1,
INDEX idx_version version TYPE bloom_filter(0.001) GRANULARITY 1,
INDEX idx_release release TYPE bloom_filter(0.001) GRANULARITY 1,
INDEX idx_tags tags TYPE bloom_filter(0.001) GRANULARITY 1
) Engine = AggregatingMergeTree()
ORDER BY (project_id, id)
TTL toDate(start_time) + INTERVAL 30 DAY;
@@ -0,0 +1,5 @@
-- Drop traces_null and trace amt tables
DROP TABLE IF EXISTS traces_null ON CLUSTER default;
DROP TABLE IF EXISTS traces_all_amt ON CLUSTER default;
DROP TABLE IF EXISTS traces_7d_amt ON CLUSTER default;
DROP TABLE IF EXISTS traces_30d_amt ON CLUSTER default;
@@ -0,0 +1,21 @@
-- Recreate materialized views for project_environments table
CREATE MATERIALIZED VIEW project_environments_traces_mv TO project_environments AS
SELECT
project_id,
groupUniqArray(environment) AS environments
FROM traces
GROUP BY project_id;
CREATE MATERIALIZED VIEW project_environments_observations_mv TO project_environments AS
SELECT
project_id,
groupUniqArray(environment) AS environments
FROM observations
GROUP BY project_id;
CREATE MATERIALIZED VIEW project_environments_scores_mv TO project_environments AS
SELECT
project_id,
groupUniqArray(environment) AS environments
FROM scores
GROUP BY project_id;
@@ -0,0 +1,4 @@
-- Drop materialized views feeding project_environments table
DROP VIEW IF EXISTS project_environments_traces_mv;
DROP VIEW IF EXISTS project_environments_observations_mv;
DROP VIEW IF EXISTS project_environments_scores_mv;
@@ -0,0 +1,114 @@
-- Recreate materialized views derived from traces_null table
CREATE MATERIALIZED VIEW IF NOT EXISTS traces_all_amt_mv TO traces_all_amt AS
SELECT
-- Identifiers
tn.project_id as project_id,
tn.id as id,
min(tn.start_time) as timestamp, -- Backward compatibility: redundant with start_time
min(tn.start_time) as start_time,
max(coalesce(tn.end_time, tn.start_time)) as end_time,
anyLast(tn.name) as name,
-- Metadata properties
maxMap(tn.metadata) as metadata,
anyLast(tn.user_id) as user_id,
anyLast(tn.session_id) as session_id,
anyLast(tn.environment) as environment,
groupUniqArrayArray(tn.tags) as tags,
anyLast(tn.version) as version,
anyLast(tn.release) as release,
-- UI properties
argMaxState(tn.bookmarked, if(tn.bookmarked is not null, tn.event_ts, toDateTime64(0, 3))) as bookmarked,
argMaxState(tn.public, if(tn.public is not null, tn.event_ts, toDateTime64(0, 3))) as public,
-- Aggregations -- DO NOT USE
groupUniqArrayArray(tn.observation_ids) as observation_ids,
groupUniqArrayArray(tn.score_ids) as score_ids,
sumMap(tn.cost_details) as cost_details,
sumMap(tn.usage_details) as usage_details,
-- Input/Output
argMaxState(tn.input, if(tn.input <> '', tn.event_ts, toDateTime64(0, 3))) as input,
argMaxState(tn.output, if(tn.output <> '', tn.event_ts, toDateTime64(0, 3))) as output,
min(tn.created_at) as created_at,
max(tn.updated_at) as updated_at
FROM traces_null tn
GROUP BY project_id, id;
CREATE MATERIALIZED VIEW IF NOT EXISTS traces_7d_amt_mv TO traces_7d_amt AS
SELECT
-- Identifiers
tn.project_id as project_id,
tn.id as id,
min(tn.start_time) as timestamp, -- Backward compatibility: redundant with start_time
min(tn.start_time) as start_time,
max(coalesce(tn.end_time, tn.start_time)) as end_time,
anyLast(tn.name) as name,
-- Metadata properties
maxMap(tn.metadata) as metadata,
anyLast(tn.user_id) as user_id,
anyLast(tn.session_id) as session_id,
anyLast(tn.environment) as environment,
groupUniqArrayArray(tn.tags) as tags,
anyLast(tn.version) as version,
anyLast(tn.release) as release,
-- UI properties
argMaxState(tn.bookmarked, if(tn.bookmarked is not null, tn.event_ts, toDateTime64(0, 3))) as bookmarked,
argMaxState(tn.public, if(tn.public is not null, tn.event_ts, toDateTime64(0, 3))) as public,
-- Aggregations -- DO NOT USE
groupUniqArrayArray(tn.observation_ids) as observation_ids,
groupUniqArrayArray(tn.score_ids) as score_ids,
sumMap(tn.cost_details) as cost_details,
sumMap(tn.usage_details) as usage_details,
-- Input/Output
argMaxState(tn.input, if(tn.input <> '', tn.event_ts, toDateTime64(0, 3))) as input,
argMaxState(tn.output, if(tn.output <> '', tn.event_ts, toDateTime64(0, 3))) as output,
min(tn.created_at) as created_at,
max(tn.updated_at) as updated_at
FROM traces_null tn
GROUP BY project_id, id;
CREATE MATERIALIZED VIEW IF NOT EXISTS traces_30d_amt_mv TO traces_30d_amt AS
SELECT
-- Identifiers
tn.project_id as project_id,
tn.id as id,
min(tn.start_time) as timestamp, -- Backward compatibility: redundant with start_time
min(tn.start_time) as start_time,
max(coalesce(tn.end_time, tn.start_time)) as end_time,
anyLast(tn.name) as name,
-- Metadata properties
maxMap(tn.metadata) as metadata,
anyLast(tn.user_id) as user_id,
anyLast(tn.session_id) as session_id,
anyLast(tn.environment) as environment,
groupUniqArrayArray(tn.tags) as tags,
anyLast(tn.version) as version,
anyLast(tn.release) as release,
-- UI properties
argMaxState(tn.bookmarked, if(tn.bookmarked is not null, tn.event_ts, toDateTime64(0, 3))) as bookmarked,
argMaxState(tn.public, if(tn.public is not null, tn.event_ts, toDateTime64(0, 3))) as public,
-- Aggregations -- DO NOT USE
groupUniqArrayArray(tn.observation_ids) as observation_ids,
groupUniqArrayArray(tn.score_ids) as score_ids,
sumMap(tn.cost_details) as cost_details,
sumMap(tn.usage_details) as usage_details,
-- Input/Output
argMaxState(tn.input, if(tn.input <> '', tn.event_ts, toDateTime64(0, 3))) as input,
argMaxState(tn.output, if(tn.output <> '', tn.event_ts, toDateTime64(0, 3))) as output,
min(tn.created_at) as created_at,
max(tn.updated_at) as updated_at
FROM traces_null tn
GROUP BY project_id, id;
@@ -0,0 +1,4 @@
-- Drop materialized views derived from traces_null table
DROP VIEW IF EXISTS traces_all_amt_mv;
DROP VIEW IF EXISTS traces_7d_amt_mv;
DROP VIEW IF EXISTS traces_30d_amt_mv;
@@ -0,0 +1,179 @@
-- Recreate traces_null and trace amt tables
CREATE TABLE traces_null
(
-- Identifiers
`project_id` String,
`id` String,
`start_time` DateTime64(3),
`end_time` Nullable(DateTime64(3)),
`name` Nullable(String),
-- Metadata properties
`metadata` Map(LowCardinality(String), String),
`user_id` Nullable(String),
`session_id` Nullable(String),
`environment` String,
`tags` Array(String),
`version` Nullable(String),
`release` Nullable(String),
-- UI properties - We make them nullable to prevent absent values being interpreted as overwrites.
`bookmarked` Nullable(Bool),
`public` Nullable(Bool),
-- Aggregations -- DO NOT USE
`observation_ids` Array(String),
`score_ids` Array(String),
`cost_details` Map(String, Decimal64(12)),
`usage_details` Map(String, UInt64),
-- TODO: Do we want to aggregate/collect `levels` seen within the trace?
-- Input/Output
`input` String,
`output` String,
`created_at` DateTime64(3),
`updated_at` DateTime64(3),
`event_ts` DateTime64(3)
) Engine = Null();
CREATE TABLE traces_all_amt
(
-- Identifiers
`project_id` String,
`id` String,
`timestamp` SimpleAggregateFunction(min, DateTime64(3)), -- Backward compatibility: redundant with start_time
`start_time` SimpleAggregateFunction(min, DateTime64(3)),
`end_time` SimpleAggregateFunction(max, Nullable(DateTime64(3))),
`name` SimpleAggregateFunction(anyLast, Nullable(String)),
-- Metadata properties
`metadata` SimpleAggregateFunction(maxMap, Map(String, String)),
`user_id` SimpleAggregateFunction(anyLast, Nullable(String)),
`session_id` SimpleAggregateFunction(anyLast, Nullable(String)),
`environment` SimpleAggregateFunction(anyLast, String),
`tags` SimpleAggregateFunction(groupUniqArrayArray, Array(String)),
`version` SimpleAggregateFunction(anyLast, Nullable(String)),
`release` SimpleAggregateFunction(anyLast, Nullable(String)),
-- UI properties
`bookmarked` AggregateFunction(argMax, Nullable(Bool), DateTime64(3)),
`public` AggregateFunction(argMax, Nullable(Bool), DateTime64(3)),
-- Aggregations -- DO NOT USE
`observation_ids` SimpleAggregateFunction(groupUniqArrayArray, Array(String)),
`score_ids` SimpleAggregateFunction(groupUniqArrayArray, Array(String)),
`cost_details` SimpleAggregateFunction(sumMap, Map(String, Decimal(38, 12))),
`usage_details` SimpleAggregateFunction(sumMap, Map(String, UInt64)),
-- Input/Output -> prefer correctness via argMax
`input` AggregateFunction(argMax, String, DateTime64(3)) CODEC (ZSTD(3)),
`output` AggregateFunction(argMax, String, DateTime64(3)) CODEC (ZSTD(3)),
`created_at` SimpleAggregateFunction(min, DateTime64(3)),
`updated_at` SimpleAggregateFunction(max, DateTime64(3)),
-- Indexes
INDEX idx_trace_id id TYPE bloom_filter(0.001) GRANULARITY 1,
INDEX idx_user_id user_id TYPE bloom_filter(0.001) GRANULARITY 1,
INDEX idx_session_id session_id TYPE bloom_filter(0.001) GRANULARITY 1,
INDEX idx_name name TYPE bloom_filter(0.001) GRANULARITY 1,
INDEX idx_version version TYPE bloom_filter(0.001) GRANULARITY 1,
INDEX idx_release release TYPE bloom_filter(0.001) GRANULARITY 1,
INDEX idx_tags tags TYPE bloom_filter(0.001) GRANULARITY 1
) Engine = AggregatingMergeTree()
ORDER BY (project_id, id);
CREATE TABLE traces_7d_amt
(
-- Identifiers
`project_id` String,
`id` String,
`timestamp` SimpleAggregateFunction(min, DateTime64(3)), -- Backward compatibility: redundant with start_time
`start_time` SimpleAggregateFunction(min, DateTime64(3)),
`end_time` SimpleAggregateFunction(max, Nullable(DateTime64(3))),
`name` SimpleAggregateFunction(anyLast, Nullable(String)),
-- Metadata properties
`metadata` SimpleAggregateFunction(maxMap, Map(String, String)),
`user_id` SimpleAggregateFunction(anyLast, Nullable(String)),
`session_id` SimpleAggregateFunction(anyLast, Nullable(String)),
`environment` SimpleAggregateFunction(anyLast, String),
`tags` SimpleAggregateFunction(groupUniqArrayArray, Array(String)),
`version` SimpleAggregateFunction(anyLast, Nullable(String)),
`release` SimpleAggregateFunction(anyLast, Nullable(String)),
-- UI properties
`bookmarked` AggregateFunction(argMax, Nullable(Bool), DateTime64(3)),
`public` AggregateFunction(argMax, Nullable(Bool), DateTime64(3)),
-- Aggregations -- DO NOT USE
`observation_ids` SimpleAggregateFunction(groupUniqArrayArray, Array(String)),
`score_ids` SimpleAggregateFunction(groupUniqArrayArray, Array(String)),
`cost_details` SimpleAggregateFunction(sumMap, Map(String, Decimal(38, 12))),
`usage_details` SimpleAggregateFunction(sumMap, Map(String, UInt64)),
-- Input/Output -> prefer correctness via argMax
`input` AggregateFunction(argMax, String, DateTime64(3)) CODEC (ZSTD(3)),
`output` AggregateFunction(argMax, String, DateTime64(3)) CODEC (ZSTD(3)),
`created_at` SimpleAggregateFunction(min, DateTime64(3)),
`updated_at` SimpleAggregateFunction(max, DateTime64(3)),
-- Indexes
INDEX idx_user_id user_id TYPE bloom_filter(0.001) GRANULARITY 1,
INDEX idx_session_id session_id TYPE bloom_filter(0.001) GRANULARITY 1,
INDEX idx_name name TYPE bloom_filter(0.001) GRANULARITY 1,
INDEX idx_version version TYPE bloom_filter(0.001) GRANULARITY 1,
INDEX idx_release release TYPE bloom_filter(0.001) GRANULARITY 1,
INDEX idx_tags tags TYPE bloom_filter(0.001) GRANULARITY 1
) Engine = AggregatingMergeTree()
ORDER BY (project_id, id)
TTL toDate(start_time) + INTERVAL 7 DAY;
CREATE TABLE traces_30d_amt
(
-- Identifiers
`project_id` String,
`id` String,
`timestamp` SimpleAggregateFunction(min, DateTime64(3)), -- Backward compatibility: redundant with start_time
`start_time` SimpleAggregateFunction(min, DateTime64(3)),
`end_time` SimpleAggregateFunction(max, Nullable(DateTime64(3))),
`name` SimpleAggregateFunction(anyLast, Nullable(String)),
-- Metadata properties
`metadata` SimpleAggregateFunction(maxMap, Map(String, String)),
`user_id` SimpleAggregateFunction(anyLast, Nullable(String)),
`session_id` SimpleAggregateFunction(anyLast, Nullable(String)),
`environment` SimpleAggregateFunction(anyLast, String),
`tags` SimpleAggregateFunction(groupUniqArrayArray, Array(String)),
`version` SimpleAggregateFunction(anyLast, Nullable(String)),
`release` SimpleAggregateFunction(anyLast, Nullable(String)),
-- UI properties
`bookmarked` AggregateFunction(argMax, Nullable(Bool), DateTime64(3)),
`public` AggregateFunction(argMax, Nullable(Bool), DateTime64(3)),
-- Aggregations -- DO NOT USE
`observation_ids` SimpleAggregateFunction(groupUniqArrayArray, Array(String)),
`score_ids` SimpleAggregateFunction(groupUniqArrayArray, Array(String)),
`cost_details` SimpleAggregateFunction(sumMap, Map(String, Decimal(38, 12))),
`usage_details` SimpleAggregateFunction(sumMap, Map(String, UInt64)),
-- Input/Output -> prefer correctness via argMax
`input` AggregateFunction(argMax, String, DateTime64(3)) CODEC (ZSTD(3)),
`output` AggregateFunction(argMax, String, DateTime64(3)) CODEC (ZSTD(3)),
`created_at` SimpleAggregateFunction(min, DateTime64(3)),
`updated_at` SimpleAggregateFunction(max, DateTime64(3)),
-- Indexes
INDEX idx_user_id user_id TYPE bloom_filter(0.001) GRANULARITY 1,
INDEX idx_session_id session_id TYPE bloom_filter(0.001) GRANULARITY 1,
INDEX idx_name name TYPE bloom_filter(0.001) GRANULARITY 1,
INDEX idx_version version TYPE bloom_filter(0.001) GRANULARITY 1,
INDEX idx_release release TYPE bloom_filter(0.001) GRANULARITY 1,
INDEX idx_tags tags TYPE bloom_filter(0.001) GRANULARITY 1
) Engine = AggregatingMergeTree()
ORDER BY (project_id, id)
TTL toDate(start_time) + INTERVAL 30 DAY;
@@ -0,0 +1,5 @@
-- Drop traces_null and trace amt tables
DROP TABLE IF EXISTS traces_null;
DROP TABLE IF EXISTS traces_all_amt;
DROP TABLE IF EXISTS traces_7d_amt;
DROP TABLE IF EXISTS traces_30d_amt;
@@ -0,0 +1,261 @@
#!/bin/bash
# Development-only ClickHouse table creation script
# This script is for creating experimental/development tables that are not yet
# ready to be part of the official migration system.
#
# Usage:
# pnpm run ch:dev-tables (from packages/shared/)
#
# This script is automatically run as part of:
# - pnpm run dx
# - pnpm run dx-f
# - pnpm run ch:reset
# Load environment variables
[ -f ../../.env ] && source ../../.env
# Check if CLICKHOUSE_MIGRATION_URL is configured
if [ -z "${CLICKHOUSE_MIGRATION_URL}" ]; then
echo "Error: CLICKHOUSE_MIGRATION_URL is not configured."
echo "Please set CLICKHOUSE_MIGRATION_URL in your environment variables."
exit 1
fi
# Check if CLICKHOUSE_USER is set
if [ -z "${CLICKHOUSE_USER}" ]; then
echo "Error: CLICKHOUSE_USER is not set."
echo "Please set CLICKHOUSE_USER in your environment variables."
exit 1
fi
# Check if CLICKHOUSE_PASSWORD is set
if [ -z "${CLICKHOUSE_PASSWORD}" ]; then
echo "Error: CLICKHOUSE_PASSWORD is not set."
echo "Please set CLICKHOUSE_PASSWORD in your environment variables."
exit 1
fi
# Ensure CLICKHOUSE_DB is set
if [ -z "${CLICKHOUSE_DB}" ]; then
export CLICKHOUSE_DB="default"
fi
# Parse the CLICKHOUSE_MIGRATION_URL to extract host and port
# Expected format: clickhouse://localhost:9000
if [[ $CLICKHOUSE_MIGRATION_URL =~ ^clickhouse://([^:]+):([0-9]+)$ ]]; then
CLICKHOUSE_HOST="${BASH_REMATCH[1]}"
CLICKHOUSE_PORT="${BASH_REMATCH[2]}"
elif [[ $CLICKHOUSE_MIGRATION_URL =~ ^clickhouse://([^:]+)$ ]]; then
CLICKHOUSE_HOST="${BASH_REMATCH[1]}"
CLICKHOUSE_PORT="9000" # Default native protocol port
else
echo "Error: Could not parse CLICKHOUSE_MIGRATION_URL: ${CLICKHOUSE_MIGRATION_URL}"
exit 1
fi
if ! command -v clickhouse &> /dev/null
then
echo "Error: clickhouse binary could not be found. Please install ClickHouse client tools."
exit 1
fi
echo "Creating development tables in ClickHouse..."
# Execute the CREATE TABLE statements
# Add your development tables here using CREATE TABLE IF NOT EXISTS
clickhouse client \
--host="${CLICKHOUSE_HOST}" \
--port="${CLICKHOUSE_PORT}" \
--user="${CLICKHOUSE_USER}" \
--password="${CLICKHOUSE_PASSWORD}" \
--database="${CLICKHOUSE_DB}" \
--multiquery <<EOF
-- Create new events table for development setups.
-- We expect this to be fully immutable and eventually replace observations.
-- See LFE-5394 for ongoing discussion.
-- Remove IF NOT EXISTS when moving this to prod migrations.
CREATE TABLE IF NOT EXISTS events
(
org_id String, -- TODO: Unsure about this one
project_id String,
trace_id String,
span_id String,
parent_span_id Nullable(String),
start_time DateTime64(6),
end_time Nullable(DateTime64(6)),
-- Core properties
name String,
type LowCardinality(String),
environment LowCardinality(String) DEFAULT 'default',
version Nullable(String),
user_id String,
session_id String,
level LowCardinality(String),
status_message String, -- Threat '' and null the same for search
completion_start_time Nullable(DateTime64(6)),
-- Prompt
prompt_id Nullable(String),
prompt_name Nullable(String),
prompt_version Nullable(String),
-- Model
model_id Nullable(String),
provided_model_name Nullable(String),
model_parameters Nullable(String),
-- Usage
provided_usage_details Map(LowCardinality(String), UInt64),
usage_details Map(LowCardinality(String), UInt64),
provided_cost_details Map(LowCardinality(String), Decimal(18, 12)),
cost_details Map(LowCardinality(String), Decimal(18, 12)),
total_cost Decimal(18,12), -- 0 if not provided
-- I/O
input String CODEC(ZSTD(3)),
output String CODEC(ZSTD(3)),
-- TODO Metadata: Decide for approach
-- -- Approach 1: Use plain JSON type with default config
metadata JSON(max_dynamic_paths=1024, max_dynamic_types=32),
-- -- Approach 2: Uses ideas from https://www.uber.com/en-DE/blog/logging/
-- -- but uses Dynamic type to make this a single list
metadata_names Array(String),
metadata_values Array(Dynamic(max_types=32)),
-- -- Approach 3: 1:1 copy of https://www.uber.com/en-DE/blog/logging/
-- -- May require further high-level types and lots of thought during
-- -- write and query-time.
metadata_string_names Array(String),
metadata_string_values Array(String),
metadata_number_names Array(String),
metadata_number_values Array(Float64),
metadata_bool_names Array(String),
metadata_bool_values Array(UInt8),
-- Source metadata (Instrumentation)
source LowCardinality(String),
service_name Nullable(String),
service_version Nullable(String),
scope_name Nullable(String),
scope_version Nullable(String),
telemetry_sdk_language Nullable(String),
telemetry_sdk_name Nullable(String),
telemetry_sdk_version Nullable(String),
-- Generic props
blob_storage_file_path String,
event_raw String,
event_bytes UInt64,
created_at DateTime64(6) DEFAULT now(),
updated_at DateTime64(6) DEFAULT now(),
event_ts DateTime64(6),
is_deleted UInt8,
-- Indexes
INDEX idx_span_id span_id TYPE bloom_filter(0.01) GRANULARITY 1,
INDEX idx_trace_id trace_id TYPE bloom_filter(0.01) GRANULARITY 1,
INDEX idx_type type TYPE set(50) GRANULARITY 1,
INDEX idx_created_at created_at TYPE minmax GRANULARITY 1,
INDEX idx_updated_at updated_at TYPE minmax GRANULARITY 1,
-- Full Text Search Indexes (We should try different index sizes, e.g. 2048, 4096, or 8192)
INDEX idx_fts_input_1 input TYPE ngrambf_v1(1, 1024, 1, 0) GRANULARITY 1,
INDEX idx_fts_input_2 input TYPE ngrambf_v1(2, 1024, 1, 0) GRANULARITY 1,
INDEX idx_fts_input_4 input TYPE ngrambf_v1(4, 1024, 1, 0) GRANULARITY 1,
INDEX idx_fts_input_8 input TYPE ngrambf_v1(8, 1024, 1, 0) GRANULARITY 1,
INDEX idx_fts_output_1 output TYPE ngrambf_v1(1, 1024, 1, 0) GRANULARITY 1,
INDEX idx_fts_output_2 output TYPE ngrambf_v1(2, 1024, 1, 0) GRANULARITY 1,
INDEX idx_fts_output_4 output TYPE ngrambf_v1(4, 1024, 1, 0) GRANULARITY 1,
INDEX idx_fts_output_8 output TYPE ngrambf_v1(8, 1024, 1, 0) GRANULARITY 1,
)
ENGINE = ReplacingMergeTree(event_ts, is_deleted)
-- ENGINE = (Replicated)ReplacingMergeTree(event_ts, is_deleted)
PARTITION BY toYYYYMM(start_time)
ORDER BY (project_id, toUnixTimestamp(start_time), trace_id, span_id)
EOF
echo "Populating development tables with sample data..."
clickhouse client \
--host="${CLICKHOUSE_HOST}" \
--port="${CLICKHOUSE_PORT}" \
--user="${CLICKHOUSE_USER}" \
--password="${CLICKHOUSE_PASSWORD}" \
--database="${CLICKHOUSE_DB}" \
--multiquery <<EOF
TRUNCATE events;
INSERT INTO events (org_id, project_id, trace_id, span_id, parent_span_id, start_time, end_time, name, type,
environment, version, user_id, session_id, level, status_message, completion_start_time, prompt_id,
prompt_name, prompt_version, model_id, provided_model_name, model_parameters,
provided_usage_details, usage_details, provided_cost_details, cost_details, total_cost, input,
output, metadata, metadata_names, metadata_values, metadata_string_names, metadata_string_values,
metadata_number_names, metadata_number_values, metadata_bool_names, metadata_bool_values, source,
service_name, service_version, scope_name, scope_version, telemetry_sdk_language,
telemetry_sdk_name, telemetry_sdk_version, blob_storage_file_path, event_raw, event_bytes,
created_at, updated_at, event_ts, is_deleted)
SELECT concat('o', project_id) AS org_id,
project_id,
trace_id,
id AS span_id,
parent_observation_id AS parent_span_id,
start_time,
end_time,
name,
type,
environment,
version,
concat('u_', floor(randUniform(1, 100))) AS user_id,
concat('s_', floor(randUniform(1, 100))) AS session_id,
level,
ifNull(status_message, '') AS status_message,
completion_start_time,
prompt_id,
prompt_name,
CAST(prompt_version, 'Nullable(String)'),
internal_model_id AS model_id,
provided_model_name,
model_parameters,
provided_usage_details,
usage_details,
provided_cost_details,
cost_details,
ifNull(total_cost, 0) AS total_cost,
ifNull(input, '') AS input,
ifNull(output, '') AS output,
CAST(metadata, 'JSON'),
mapKeys(metadata) AS \`metadata.names\`,
mapValues(metadata) AS \`metadata.values\`,
mapKeys(metadata) AS metadata_string_names,
mapValues(metadata) AS metadata_string_values,
[] AS metadata_number_names,
[] AS metadata_number_values,
[] AS metadata_bool_names,
[] AS metadata_bool_values,
multiIf(mapContains(metadata, 'resourceAttributes'), 'otel', 'ingestion-api') AS source,
NULL AS service_name,
NULL AS service_version,
NULL AS scope_name,
NULL AS scope_version,
NULL AS telemetry_sdk_language,
NULL AS telemetry_sdk_name,
NULL AS telemetry_sdk_version,
'' AS blob_storage_file_path,
'' AS event_raw,
0 AS event_bytes,
created_at,
updated_at,
event_ts,
is_deleted
FROM observations
WHERE (is_deleted = 0);
EOF
echo "Development tables created successfully (or already exist)."
echo ""
+16 -14
View File
@@ -6,7 +6,7 @@
"main": "./dist/src/index.js",
"types": "./dist/src/index.d.ts",
"engines": {
"node": "20"
"node": "24"
},
"exports": {
".": {
@@ -47,7 +47,8 @@
"ch:up": "bash clickhouse/scripts/up.sh",
"ch:down": "bash clickhouse/scripts/down.sh",
"ch:drop": "bash clickhouse/scripts/drop.sh",
"ch:reset": "pnpm run ch:down && pnpm run ch:up && pnpm run ch:seed",
"ch:dev-tables": "bash clickhouse/scripts/dev-tables.sh",
"ch:reset": "pnpm run ch:down && pnpm run ch:up && pnpm run ch:seed && pnpm run ch:dev-tables",
"ch:seed": "dotenv -e ../../.env -- ts-node -r tsconfig-paths/register -r dotenv/config --compiler-options '{\"module\":\"CommonJS\"}' scripts/seeder/seed-clickhouse.ts",
"load:setup": "dotenv -e ../../.env -- tsx scripts/seeder/load-seed-clickhouse.ts"
},
@@ -63,7 +64,7 @@
"@azure/storage-blob": "^12.26.0",
"@clickhouse/client": "^1.12.1",
"@google-cloud/storage": "^7.17.0",
"@langchain/anthropic": "^0.3.22",
"@langchain/anthropic": "^0.3.29",
"@langchain/aws": "^0.1.11",
"@langchain/core": "^0.3.58",
"@langchain/google-genai": "^0.2.12",
@@ -71,21 +72,23 @@
"@langchain/openai": "^0.5.13",
"@opentelemetry/api": ">=1.0.0 <1.10.0",
"@prisma/client": "^6.10.1",
"@react-email/components": "^0.1.0",
"@react-email/render": "^1.1.2",
"@slack/oauth": "^3.0.3",
"@slack/web-api": "^7.9.3",
"@react-email/components": "^0.5.1",
"@react-email/render": "^1.2.1",
"@slack/oauth": "^3.0.4",
"@slack/web-api": "^7.10.0",
"@types/bcryptjs": "^2.4.6",
"bcryptjs": "^2.4.3",
"bullmq": "^5.34.10",
"dd-trace": "^5.36.0",
"date-fns": "^3.3.1",
"dd-trace": "^5.65.0",
"decimal.js": "^10.4.3",
"exponential-backoff": "^3.1.2",
"https-proxy-agent": "^7.0.6",
"ioredis": "^5.4.1",
"ipaddr.js": "^2.2.0",
"jsonpath-plus": "10.3.0",
"kysely": "^0.27.4",
"langchain": "^0.3.28",
"langchain": "^0.3.30",
"langfuse-langchain": "3.38.4",
"lodash": "^4.17.21",
"lossless-json": "^4.1.1",
@@ -101,10 +104,10 @@
"@repo/eslint-config": "workspace:*",
"@repo/typescript-config": "workspace:*",
"@types/lodash": "^4.17.10",
"@types/node": "^20.11.29",
"@types/node": "^24.3.0",
"@types/nodemailer": "^6.4.16",
"@types/pg": "^8.11.10",
"@types/react": "18.2.79",
"@types/react": "19.2.2",
"@types/uuid": "^9.0.8",
"@typescript-eslint/parser": "^7.12.0",
"eslint": "^8.57.0",
@@ -112,7 +115,6 @@
"eslint-config-standard": "^17.1.0",
"eslint-plugin-prettier": "^5.1.3",
"kysely-codegen": "^0.16.8",
"nodemon": "^3.1.7",
"prettier": "^3.6.2",
"prisma": "^6.10.1",
"prisma-erd-generator": "^1.11.2",
@@ -122,7 +124,7 @@
"typescript": "^5.7.2"
},
"peerDependencies": {
"@types/react": "~18.2.79",
"react": "~18.2.0"
"@types/react": "~19.2.2",
"react": "~19.2.0"
}
}
+15
View File
@@ -332,6 +332,15 @@ export type BlobStorageIntegration = {
created_at: Generated<Timestamp>;
updated_at: Generated<Timestamp>;
};
export type CloudSpendAlert = {
id: string;
org_id: string;
title: string;
threshold: string;
triggered_at: Timestamp | null;
created_at: Generated<Timestamp>;
updated_at: Generated<Timestamp>;
};
export type Comment = {
id: string;
project_id: string;
@@ -641,6 +650,11 @@ export type Organization = {
updated_at: Generated<Timestamp>;
cloud_config: unknown | null;
metadata: unknown | null;
cloud_billing_cycle_anchor: Generated<Timestamp | null>;
cloud_billing_cycle_updated_at: Timestamp | null;
cloud_current_cycle_usage: number | null;
cloud_free_tier_usage_threshold_state: string | null;
ai_features_enabled: Generated<boolean>;
};
export type OrganizationMembership = {
id: string;
@@ -846,6 +860,7 @@ export type DB = {
batch_exports: BatchExport;
billing_meter_backups: BillingMeterBackup;
blob_storage_integrations: BlobStorageIntegration;
cloud_spend_alerts: CloudSpendAlert;
comments: Comment;
cron_jobs: CronJobs;
dashboard_widgets: DashboardWidget;
@@ -0,0 +1,2 @@
-- AlterTable
ALTER TABLE "organizations" ADD COLUMN "ai_features_enabled" BOOLEAN NOT NULL DEFAULT false;
@@ -0,0 +1,2 @@
-- CreateIndex
CREATE INDEX CONCURRENTLY IF NOT EXISTS "job_executions_project_id_job_output_score_id_idx" ON "job_executions"("project_id", "job_output_score_id");
@@ -0,0 +1,21 @@
-- AlterTable
ALTER TABLE "organizations" ADD COLUMN "cloud_billing_cycle_anchor" TIMESTAMP(3) DEFAULT CURRENT_TIMESTAMP,
ADD COLUMN "cloud_billing_cycle_updated_at" TIMESTAMP(3),
ADD COLUMN "cloud_current_cycle_usage" INTEGER,
ADD COLUMN "cloud_free_tier_usage_threshold_state" TEXT;
-- Backfill cloud_billing_cycle_anchor for existing organizations:
-- Step 1: Set to NULL for orgs WITH active subscriptions (will be backfilled from Stripe by worker job)
UPDATE "organizations"
SET "cloud_billing_cycle_anchor" = NULL;
-- Step 2: Set to created_at for orgs WITHOUT active subscriptions (free tier orgs)
UPDATE "organizations"
SET "cloud_billing_cycle_anchor" = "created_at"
WHERE
"cloud_config" IS NULL
OR NOT (
"cloud_config"::jsonb -> 'stripe' ? 'activeSubscriptionId'
AND "cloud_config"::jsonb -> 'stripe' ->> 'activeSubscriptionId' IS NOT NULL
AND "cloud_config"::jsonb -> 'stripe' ->> 'activeSubscriptionId' != ''
);
@@ -0,0 +1,2 @@
INSERT INTO background_migrations (id, name, script, args)
VALUES ('0199b890-1093-7d1f-b662-be3c03527e93', '20250102_backfill_billing_cycle_anchors', 'backfillBillingCycleAnchors', '{}');
@@ -0,0 +1,15 @@
-- CreateTable
CREATE TABLE "cloud_spend_alerts" (
"id" TEXT NOT NULL,
"org_id" TEXT NOT NULL,
"title" TEXT NOT NULL,
"threshold" DECIMAL(65,30) NOT NULL,
"triggered_at" TIMESTAMP(3),
"created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updated_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
CONSTRAINT "cloud_spend_alerts_pkey" PRIMARY KEY ("id")
);
-- AddForeignKey
ALTER TABLE "cloud_spend_alerts" ADD CONSTRAINT "cloud_spend_alerts_org_id_fkey" FOREIGN KEY ("org_id") REFERENCES "organizations"("id") ON DELETE CASCADE ON UPDATE CASCADE;
@@ -0,0 +1,3 @@
-- CreateIndex
CREATE INDEX CONCURRENTLY "cloud_spend_alerts_org_id_idx" ON "cloud_spend_alerts"("org_id");
+37 -11
View File
@@ -104,17 +104,23 @@ model VerificationToken {
}
model Organization {
id String @id @default(cuid())
name String
createdAt DateTime @default(now()) @map("created_at")
updatedAt DateTime @default(now()) @updatedAt @map("updated_at")
cloudConfig Json? @map("cloud_config") // Langfuse Cloud, for zod schema see @/src/features/organizations/utils/cloudConfigSchema
metadata Json?
organizationMemberships OrganizationMembership[]
projects Project[]
MembershipInvitation MembershipInvitation[]
ApiKey ApiKey[]
surveys Survey[]
id String @id @default(cuid())
name String
createdAt DateTime @default(now()) @map("created_at")
updatedAt DateTime @default(now()) @updatedAt @map("updated_at")
cloudConfig Json? @map("cloud_config") // Langfuse Cloud, for zod schema see @/src/features/organizations/utils/cloudConfigSchema
metadata Json?
cloudBillingCycleAnchor DateTime? @default(now()) @map("cloud_billing_cycle_anchor")
cloudBillingCycleUpdatedAt DateTime? @map("cloud_billing_cycle_updated_at")
cloudCurrentCycleUsage Int? @map("cloud_current_cycle_usage")
cloudFreeTierUsageThresholdState String? @map("cloud_free_tier_usage_threshold_state")
aiFeaturesEnabled Boolean @default(false) @map("ai_features_enabled")
organizationMemberships OrganizationMembership[]
projects Project[]
MembershipInvitation MembershipInvitation[]
ApiKey ApiKey[]
surveys Survey[]
cloudSpendAlerts CloudSpendAlert[]
@@map("organizations")
}
@@ -853,6 +859,8 @@ model EvalTemplate {
@@map("eval_templates")
}
// We currently assume in the evalRouter that _all_ job_executions are for EVAL job_configs.
// If we ever extend this, we need to adjust the filter condition there. ref.: fetchJobExecutionsByStatus.
enum JobType {
EVAL
}
@@ -927,6 +935,7 @@ model JobExecution {
@@index([projectId, jobConfigurationId, jobInputTraceId])
@@index([projectId, status])
@@index([projectId, id])
@@index([projectId, jobOutputScoreId])
@@map("job_executions")
}
@@ -1425,3 +1434,20 @@ enum SurveyName {
@@map("SurveyName")
}
model CloudSpendAlert {
id String @id @default(cuid())
orgId String @map("org_id")
org Organization @relation(fields: [orgId], references: [id], onDelete: Cascade)
title String // e.g., "Production Alert"
threshold Decimal @map("threshold") // USD amount
triggeredAt DateTime? @map("triggered_at") // Last trigger timestamp
createdAt DateTime @default(now()) @map("created_at")
updatedAt DateTime @default(now()) @updatedAt @map("updated_at")
@@index([orgId])
@@map("cloud_spend_alerts")
}
+33 -45
View File
@@ -8,6 +8,7 @@ import {
JobExecutionStatus,
PrismaClient,
type Project,
ScoreConfigCategoryDomain,
ScoreDataType,
} from "../../src/index";
import { getDisplaySecretKey, hashSecretKey, logger } from "../../src/server";
@@ -24,17 +25,11 @@ import {
} from "./utils/postgres-seed-constants";
import {
generateDatasetItemId,
generateDatasetRunTraceId,
generateEvalObservationId,
generateEvalScoreId,
generateEvalTraceId,
} from "./utils/seed-helpers";
type ConfigCategory = {
label: string;
value: number;
};
const options = {
environment: { type: "string" },
} as const;
@@ -111,6 +106,9 @@ async function main() {
},
});
// Realistic support chat scenario
await createSupportChatSession(project1);
await prisma.organizationMembership.upsert({
where: {
orgId_userId: {
@@ -534,23 +532,13 @@ export async function createDatasets(
const datasetItem = await prisma.datasetItem.upsert({
where: {
id_projectId: {
id: generateDatasetItemId(
datasetName,
index,
projectId,
SEED_DATASETS.indexOf(data) || 0,
),
id: generateDatasetItemId(datasetName, index, projectId),
projectId,
},
},
create: {
projectId,
id: generateDatasetItemId(
datasetName,
index,
projectId,
SEED_DATASETS.indexOf(data) || 0,
),
id: generateDatasetItemId(datasetName, index, projectId),
datasetId: dataset.id,
sourceTraceId: sourceTraceId ?? null,
sourceObservationId: null,
@@ -564,17 +552,17 @@ export async function createDatasets(
}
for (let datasetRunNumber = 0; datasetRunNumber < 3; datasetRunNumber++) {
const datasetRun = await prisma.datasetRuns.upsert({
await prisma.datasetRuns.upsert({
where: {
id_projectId: {
id: `demo-dataset-run-${datasetRunNumber}-${projectId.slice(-8)}`,
id: `demo-dataset-run-${datasetRunNumber}-${datasetName}-${projectId.slice(-8)}`,
projectId,
},
},
create: {
projectId,
id: `demo-dataset-run-${datasetRunNumber}-${projectId.slice(-8)}`,
name: `demo-dataset-run-${datasetRunNumber}`,
id: `demo-dataset-run-${datasetRunNumber}-${datasetName}-${projectId.slice(-8)}`,
name: `demo-dataset-run-${datasetRunNumber}-${datasetName}`,
description: Math.random() > 0.5 ? "Dataset run description" : "",
datasetId: dataset.id,
metadata: [
@@ -587,25 +575,6 @@ export async function createDatasets(
},
update: {},
});
for (let index = 0; index < datasetItemIds.length; index++) {
await prisma.datasetRunItems.upsert({
where: {
id_projectId: {
id: `${dataset.id}-${index}-${datasetRunNumber}`,
projectId,
},
},
create: {
id: `${dataset.id}-${index}-${datasetRunNumber}`,
projectId,
datasetItemId: datasetItemIds[index],
traceId: `${generateDatasetRunTraceId(datasetName, index, projectId, datasetRunNumber)}`,
datasetRunId: datasetRun.id,
},
update: {},
});
}
}
}
}
@@ -753,6 +722,7 @@ async function generatePrompts(project: Project) {
createdBy: version.createdBy,
prompt: version.prompt,
name: version.name,
type: version.type ?? "text",
config: version.config,
version: version.version,
labels: version.labels,
@@ -774,7 +744,7 @@ async function generateConfigsForProject(projects: Project[]) {
name: string;
id: string;
dataType: ScoreDataType;
categories: ConfigCategory[] | null;
categories: ScoreConfigCategoryDomain[] | null;
}[]
> = new Map();
@@ -801,12 +771,30 @@ async function createTraceSessions(project1: Project, project2: Project) {
}
}
async function createSupportChatSession(project: Project) {
const sessionId = "support-chat-session";
await prisma.traceSession.upsert({
where: {
id_projectId: {
id: sessionId,
projectId: project.id,
},
},
create: {
id: sessionId,
projectId: project.id,
environment: "default",
},
update: {},
});
}
async function generateConfigs(project: Project) {
const configNameAndId: {
name: string;
id: string;
dataType: ScoreDataType;
categories: ConfigCategory[] | null;
categories: ScoreConfigCategoryDomain[] | null;
}[] = [];
const configs = [
@@ -883,7 +871,7 @@ async function generateQueuesForProject(
name: string;
id: string;
dataType: ScoreDataType;
categories: ConfigCategory[] | null;
categories: ScoreConfigCategoryDomain[] | null;
}[]
>,
) {
@@ -907,7 +895,7 @@ async function generateQueues(
name: string;
id: string;
dataType: ScoreDataType;
categories: ConfigCategory[] | null;
categories: ScoreConfigCategoryDomain[] | null;
}[],
) {
const queue = {
@@ -34,6 +34,7 @@ import {
TraceRecordInsertType,
DatasetRunItemRecordInsertType,
createDatasetRunItem,
createDatasetRunScore,
} from "../../../src/server";
/**
@@ -86,7 +87,6 @@ export class DataGenerator {
input.runNumber || 0,
);
// TODO: there are too many dataset run items in the postgres database?
return createDatasetRunItem({
id: datasetRunItemId,
project_id: projectId,
@@ -97,8 +97,8 @@ export class DataGenerator {
input.runNumber || 0,
),
dataset_id: `${input.datasetName}-${projectId.slice(-8)}`,
dataset_run_id: `demo-dataset-run-${input.runNumber}-${projectId.slice(-8)}`,
dataset_run_name: `demo-dataset-run-${input.runNumber}-${projectId.slice(-8)}`,
dataset_run_id: `demo-dataset-run-${input.runNumber}-${input.datasetName}-${projectId.slice(-8)}`,
dataset_run_name: `demo-dataset-run-${input.runNumber}-${input.datasetName}`,
dataset_run_created_at: input.runCreatedAt,
dataset_run_description:
(input.runNumber || 0) % 2 === 0 ? "Dataset run description" : "",
@@ -107,10 +107,9 @@ export class DataGenerator {
input.datasetName,
input.itemIndex,
projectId,
input.runNumber || 0,
),
dataset_item_input: input.item.input,
dataset_item_expected_output: input.item.output,
dataset_item_expected_output: input.item.expectedOutput,
});
}
@@ -217,6 +216,59 @@ export class DataGenerator {
});
}
/**
* Creates scores for dataset experiment scores with variable values.
* Use for: Dataset experiments requiring detailed score tracking.
*/
generateDatasetScore(
trace: TraceRecordInsertType,
input: DatasetItemInput,
projectId: string,
scoreNames: string[],
): ScoreRecordInsertType {
const scoreId = `score-dataset-${input.datasetName}-${input.itemIndex}-${input.runNumber}-${projectId.slice(-8)}`;
return createTraceScore({
id: scoreId,
trace_id: trace.id,
project_id: projectId,
name: this.randomElement(scoreNames),
value: Math.random() * 100,
string_value: undefined,
data_type: "NUMERIC",
source: "API",
environment: "langfuse-prompt-experiments",
});
}
/**
* Creates scores for dataset experiment traces with variable values.
* Use for: Dataset experiments requiring detailed score tracking.
*/
generateDatasetRunScore(
runId: string,
input: {
datasetName: string;
runNumber: number;
},
projectId: string,
scoreNames: string[],
): ScoreRecordInsertType {
const scoreId = `score-dataset-run-${input.datasetName}-${input.runNumber}-${projectId.slice(-8)}`;
return createDatasetRunScore({
id: scoreId,
project_id: projectId,
dataset_run_id: runId,
name: this.randomElement(scoreNames),
value: Math.random() * 100,
string_value: undefined,
data_type: "NUMERIC",
source: "API",
environment: "langfuse-prompt-experiments",
});
}
/**
* Creates large-scale synthetic traces for performance testing.
* Use for: Load testing, dashboard demos, realistic usage simulation.
@@ -900,6 +952,310 @@ export class DataGenerator {
: JSON.stringify(this.fileContent.chatMlJson);
}
/**
* Creates realistic support chat session data with conversational flow.
* Use for: Demonstrating session-based conversations with tool calls and scoring.
*/
generateSupportChatSessionData(projectId: string): {
traces: TraceRecordInsertType[];
observations: ObservationRecordInsertType[];
scores: ScoreRecordInsertType[];
} {
// Domain: Art museum membership support (non-tech)
const dialogues = [
{
user: "Hi—my family membership at the City Art Museum was charged twice this month.",
assistant:
"Sorry about that. I can help. Could you share your membership ID and the last 4 digits of the card?",
},
{
user: "Membership ID is CAM-48291, card ending 4417.",
assistant: "Thanks. I'll check your billing records now.",
tool: {
name: "billing.lookup",
input: { membershipId: "CAM-48291", last4: "4417" },
output: {
duplicateCharge: true,
amounts: [95.0, 95.0],
date: "2025-08-28",
},
},
},
{
user: "Okay.",
assistant:
"I found a duplicate charge on 2025-08-28 for $95. I can issue a refund to the original payment method. Shall I proceed?",
},
{
user: "Yes, please issue the refund.",
assistant: "Processing your refund now. One moment.",
tool: {
name: "billing.refund",
input: { membershipId: "CAM-48291", amount: 95.0 },
output: { status: "success", refundId: "RFND-20931" },
},
},
{
user: "Thank you!",
assistant:
"Refund RFND-20931 has been issued. You'll see it on your statement within 35 business days.",
},
{
user: "No, that's all. Appreciate the quick help!",
assistant: "Happy to help. Enjoy your next visit to the museum!",
},
// a couple more lightweight turns for scrolling realism
{
user: "Oh, and do members get early access to exhibitions?",
assistant:
"Yes—members get a 48-hour early booking window and a preview evening invite.",
},
{
user: "Perfect.",
assistant: "You're all set. Have a great day!",
},
];
const now = Date.now();
const traces: TraceRecordInsertType[] = dialogues.map((d, index) => ({
id: `support-chat-${index}-${projectId.slice(-8)}`,
timestamp: now + index * 1000,
name: "SupportChatSession",
user_id: null,
metadata: { scenario: "support-chat" },
release: null,
version: null,
project_id: projectId,
environment: "default",
public: false,
bookmarked: false,
tags: ["support", "chat", "session"],
input: JSON.stringify(
d.tool
? {
messages: [
{ role: "user", content: d.user },
{ role: "assistant", content: d.assistant },
{
role: "tool",
name: d.tool.name,
content: d.tool.output,
},
],
}
: { messages: [{ role: "user", content: d.user }] },
),
output: JSON.stringify({ role: "assistant", content: d.assistant }),
session_id: "support-chat-session",
created_at: now + index * 1000,
updated_at: now + index * 1000 + 500,
event_ts: now + index * 1000,
is_deleted: 0,
}));
// Create one GENERATION observation per trace
const observations: ObservationRecordInsertType[] = dialogues
.map((d, index) => {
const start = now + index * 1000 + 50;
const end = start + 400 + Math.floor(Math.random() * 400);
const inputTokens = 80 + Math.floor(Math.random() * 60);
const outputTokens = 60 + Math.floor(Math.random() * 60);
const totalTokens = inputTokens + outputTokens;
const baseGen: ObservationRecordInsertType = {
id: `support-chat-${index}-${projectId.slice(-8)}-gen`,
trace_id: `support-chat-${index}-${projectId.slice(-8)}`,
project_id: projectId,
type: "GENERATION",
parent_observation_id: null,
environment: "default",
start_time: start,
end_time: end,
name: "llm-generation",
metadata: {},
level: "DEFAULT",
status_message: null,
version: null,
input: JSON.stringify({
messages: [
{ role: "user", content: d.user },
d.tool
? {
role: "tool",
name: d.tool.name,
content: d.tool.output,
}
: undefined,
].filter(Boolean),
}),
output: JSON.stringify({ role: "assistant", content: d.assistant }),
provided_model_name: "gpt-4o",
internal_model_id: null,
model_parameters: JSON.stringify({ temperature: 0.2 }),
provided_usage_details: {
input: inputTokens,
output: outputTokens,
total: totalTokens,
},
usage_details: {
input: inputTokens,
output: outputTokens,
total: totalTokens,
},
provided_cost_details: {
input: Math.round(inputTokens * 2) / 1_000_000,
output: Math.round(outputTokens * 3) / 1_000_000,
total: Math.round(totalTokens * 5) / 1_000_000,
},
cost_details: {
input: Math.round(inputTokens * 2) / 1_000_000,
output: Math.round(outputTokens * 3) / 1_000_000,
total: Math.round(totalTokens * 5) / 1_000_000,
},
total_cost: Math.round(totalTokens * 5) / 1_000_000,
completion_start_time: start + 120,
prompt_id: null,
prompt_name: null,
prompt_version: null,
created_at: start,
updated_at: end,
event_ts: start,
is_deleted: 0,
};
if (!d.tool) return [baseGen];
const toolObs: ObservationRecordInsertType = {
id: `support-chat-${index}-${projectId.slice(-8)}-tool`,
trace_id: `support-chat-${index}-${projectId.slice(-8)}`,
project_id: projectId,
type: "TOOL",
parent_observation_id: null,
environment: "default",
start_time: start - 40,
end_time: start - 5,
name: d.tool.name,
metadata: {},
level: "DEFAULT",
status_message: null,
version: null,
input: JSON.stringify(d.tool.input),
output: JSON.stringify(d.tool.output),
provided_model_name: null,
internal_model_id: null,
model_parameters: null,
provided_usage_details: {},
usage_details: {},
provided_cost_details: {},
cost_details: {},
total_cost: null,
completion_start_time: null,
prompt_id: null,
prompt_name: null,
prompt_version: null,
created_at: start - 40,
updated_at: start - 5,
event_ts: start - 40,
is_deleted: 0,
};
return [toolObs, baseGen];
})
.flat();
// Create a couple of scores per trace
const scores: ScoreRecordInsertType[] = dialogues
.map((_, index) => {
const baseTs = now + index * 1000 + 600;
const helpfulness: ScoreRecordInsertType = {
id: `support-chat-${index}-${projectId.slice(-8)}-score-helpfulness`,
project_id: projectId,
trace_id: `support-chat-${index}-${projectId.slice(-8)}`,
session_id: null,
dataset_run_id: null,
observation_id: null,
environment: "default",
name: "helpfulness",
value: 70 + Math.random() * 25,
source: "API",
comment: "Heuristic helpfulness score",
metadata: {},
author_user_id: null,
config_id: null,
data_type: "NUMERIC",
string_value: null,
queue_id: null,
created_at: baseTs,
updated_at: baseTs,
timestamp: baseTs,
event_ts: baseTs,
is_deleted: 0,
};
const safeVal = Math.random() > 0.1 ? 1 : 0;
const safety: ScoreRecordInsertType = {
id: `support-chat-${index}-${projectId.slice(-8)}-score-safety`,
project_id: projectId,
trace_id: `support-chat-${index}-${projectId.slice(-8)}`,
session_id: null,
dataset_run_id: null,
observation_id: null,
environment: "default",
name: "safe",
value: safeVal,
source: "API",
comment: "Content safety",
metadata: {},
author_user_id: null,
config_id: null,
data_type: "BOOLEAN",
string_value: safeVal === 1 ? "true" : "false",
queue_id: null,
created_at: baseTs + 10,
updated_at: baseTs + 10,
timestamp: baseTs + 10,
event_ts: baseTs + 10,
is_deleted: 0,
};
// Optional: resolution score on last turn
const isFinal = index === dialogues.length - 1;
const resolved: ScoreRecordInsertType | null = isFinal
? {
id: `support-chat-${index}-${projectId.slice(-8)}-score-resolved`,
project_id: projectId,
trace_id: `support-chat-${index}-${projectId.slice(-8)}`,
session_id: null,
dataset_run_id: null,
observation_id: null,
environment: "default",
name: "resolved",
value: 1,
source: "API",
comment: "Conversation resolved",
metadata: {},
author_user_id: null,
config_id: null,
data_type: "BOOLEAN",
string_value: "true",
queue_id: null,
created_at: baseTs + 20,
updated_at: baseTs + 20,
timestamp: baseTs + 20,
event_ts: baseTs + 20,
is_deleted: 0,
}
: null;
return [helpfulness, safety, resolved].filter(
Boolean,
) as ScoreRecordInsertType[];
})
.flat();
return { traces, observations, scores };
}
/**
* Creates exactly one score per evaluation trace with prefixed IDs.
* Use for: Evaluation traces that need score validation, evaluator testing.
@@ -0,0 +1,19 @@
# Framework Traces
This folder contains real traces produced through framework instrumentation.
Most of them stem from here: https://langfuse.com/integrations/frameworks/agno-agents and so on.
## How to add further traces
1. Generate a trace
2. Download the trace from the UI using the download button. This **excludes** the `input`/`output`/`metadata` fields of the observations
3. In the trace, click `Log View (Beta)` and switch to `JSON` format. Click the copy all button and save this to a file in this folder.
4. Run the script `npx ts-node merge-observations.ts trace-file.json observations.json trace-merged.json`
5. Leave the `merged` file in this folder. Name it with the date of the trace as displayed in the UI.
6. ???
7. Profit
## How to use the trace in the UI
All trace ids are like `framework-frameworkName-traceId`, so search for framework in the trace table.
We don't rewrite the time, so you have to filter for All Time most likely.
Also, you can filter for `source: "framework-trace"` on the trace to find them.
File diff suppressed because one or more lines are too long
@@ -0,0 +1,149 @@
{
"trace": {
"id": "1b72c51fabed12ae7df83bfd4a09f545",
"projectId": "cloramnkj0002jz088vzn1ja4",
"name": "autogen-chat-trace",
"timestamp": "2025-06-06T11:31:33.965Z",
"environment": "default",
"tags": [
"autogen",
"dev"
],
"bookmarked": false,
"release": null,
"version": "1.0.0",
"userId": "user_123",
"sessionId": "session_abc",
"public": true,
"input": "\"Say 'Hello World!'\"",
"output": "\"Hello World!\"",
"metadata": "{\"email\":\"user@langfuse.com\",\"resourceAttributes\":{\"telemetry.sdk.language\":\"python\",\"telemetry.sdk.name\":\"opentelemetry\",\"telemetry.sdk.version\":\"1.33.1\",\"service.name\":\"unknown_service\"},\"scope\":{\"name\":\"langfuse-sdk\",\"version\":\"3.0.0\",\"attributes\":{\"public_key\":\"pk-lf-5855d85e-3943-497e-bd10-f50ad414bcba\"}}}",
"createdAt": "2025-06-06T11:31:35.000Z",
"updatedAt": "2025-06-06T11:31:35.337Z",
"scores": [],
"latency": 0.645
},
"observations": [
{
"id": "95f4f51edeb603d9",
"traceId": "1b72c51fabed12ae7df83bfd4a09f545",
"projectId": "cloramnkj0002jz088vzn1ja4",
"type": "SPAN",
"environment": "default",
"parentObservationId": null,
"startTime": "2025-06-06T11:31:33.965Z",
"endTime": "2025-06-06T11:31:34.610Z",
"name": "autogen-chat-trace",
"metadata": {
"resourceAttributes": {
"telemetry.sdk.language": "python",
"telemetry.sdk.name": "opentelemetry",
"telemetry.sdk.version": "1.33.1",
"service.name": "unknown_service"
},
"scope": {
"name": "langfuse-sdk",
"version": "3.0.0",
"attributes": {
"public_key": "pk-lf-5855d85e-3943-497e-bd10-f50ad414bcba"
}
}
},
"level": "DEFAULT",
"statusMessage": null,
"version": "1.0.0",
"modelParameters": null,
"completionStartTime": null,
"promptId": null,
"createdAt": "2025-06-06T11:31:35.295Z",
"updatedAt": "2025-06-06T11:31:35.337Z",
"usageDetails": {},
"costDetails": {},
"providedCostDetails": {},
"model": null,
"internalModelId": null,
"promptName": null,
"promptVersion": null,
"latency": 645,
"timeToFirstToken": null,
"inputCost": null,
"outputCost": null,
"totalCost": 0,
"inputUsage": 0,
"outputUsage": 0,
"totalUsage": 0,
"input": null,
"output": null
},
{
"id": "83517f9062f5ada4",
"traceId": "1b72c51fabed12ae7df83bfd4a09f545",
"projectId": "cloramnkj0002jz088vzn1ja4",
"type": "GENERATION",
"environment": "default",
"parentObservationId": "95f4f51edeb603d9",
"startTime": "2025-06-06T11:31:33.978Z",
"endTime": "2025-06-06T11:31:34.606Z",
"name": "chat gpt-4o",
"metadata": {
"resourceAttributes": {
"telemetry.sdk.language": "python",
"telemetry.sdk.name": "opentelemetry",
"telemetry.sdk.version": "1.33.1",
"service.name": "unknown_service"
},
"scope": {
"name": "langfuse-sdk",
"version": "3.0.0",
"attributes": {
"public_key": "pk-lf-5855d85e-3943-497e-bd10-f50ad414bcba"
}
}
},
"level": "DEFAULT",
"statusMessage": null,
"version": null,
"modelParameters": {
"seed": "",
"frequency_penalty": 0,
"max_tokens": -1,
"presence_penalty": 0,
"stop_sequences": "{\"arrayValue\":{}}",
"temperature": 1,
"top_p": 1,
"service_tier": "auto",
"user": "",
"is_stream": false
},
"completionStartTime": null,
"promptId": null,
"createdAt": "2025-06-06T11:31:35.090Z",
"updatedAt": "2025-06-06T11:31:35.153Z",
"usageDetails": {
"input": 41,
"output": 3,
"total": 44
},
"costDetails": {
"total": 0.000025
},
"providedCostDetails": {
"total": 0.000025
},
"model": "gpt-4o",
"internalModelId": "b9854a5c92dc496b997d99d20",
"promptName": null,
"promptVersion": null,
"latency": 628,
"timeToFirstToken": null,
"inputCost": null,
"outputCost": null,
"totalCost": 0.000025,
"inputUsage": 41,
"outputUsage": 3,
"totalUsage": 44,
"input": "system: You are a helpful AI assistant. Solve tasks using your tools. Reply with TERMINATE when the task has been completed.\nuser: Say 'Hello World!'",
"output": "Hello World!"
}
]
}
File diff suppressed because one or more lines are too long
@@ -0,0 +1,203 @@
import { readFileSync, readdirSync } from "fs";
import path from "path";
import {
createTrace,
createObservation,
logger,
type TraceRecordInsertType,
type ObservationRecordInsertType,
type ScoreRecordInsertType,
} from "../../../../src/server";
/**
* Loads framework traces from JSON files and converts them to ClickHouse insert types.
* Expected JSON structure:
* {
* trace: { trace data without observations },
* observations: [{ individual observation with id and input/output/metadata and all the other stuff}]
* }
*/
export class FrameworkTraceLoader {
private frameworkTracesDir: string;
constructor() {
this.frameworkTracesDir = __dirname;
}
/**
* Load and adapt all framework traces for a project.
*/
loadTracesForProject(projectId: string): {
traces: TraceRecordInsertType[];
observations: ObservationRecordInsertType[];
scores: ScoreRecordInsertType[];
} {
const traces: TraceRecordInsertType[] = [];
const observations: ObservationRecordInsertType[] = [];
const scores: ScoreRecordInsertType[] = [];
const files = readdirSync(this.frameworkTracesDir).filter(
(file) => file.endsWith(".json") && file !== "package.json",
);
for (const file of files) {
const filePath = path.join(this.frameworkTracesDir, file);
const content = readFileSync(filePath, "utf-8");
const data = JSON.parse(content);
const frameworkName = file.replace(".json", "");
const adapted = this.adaptTrace(data, projectId, frameworkName);
traces.push(adapted.trace);
observations.push(...adapted.observations);
scores.push(...adapted.scores);
logger.info(
`Loaded 1 trace with ${adapted.observations.length} observations from ${frameworkName}`,
);
}
return { traces, observations, scores };
}
/**
* Adapt a single framework trace to a specific project.
*/
private adaptTrace(
data: any,
projectId: string,
frameworkName: string,
): {
trace: TraceRecordInsertType;
observations: ObservationRecordInsertType[];
scores: ScoreRecordInsertType[];
} {
const rawTrace = data.trace;
const rawObservations = data.observations || [];
const originalTraceId = rawTrace.id;
const newTraceId = originalTraceId;
// we just change the name for now.
// const newTraceId = `framework-${frameworkName}-${originalTraceId}-${projectId.slice(-8)}`;
const newName = `framework-${frameworkName}-${rawTrace.name}`;
// Parse trace metadata if it's a string
let metadata = rawTrace.metadata;
if (typeof metadata === "string") {
metadata = JSON.parse(metadata);
}
// Create trace
const trace = createTrace({
id: newTraceId,
project_id: projectId,
name: newName,
timestamp: new Date(rawTrace.timestamp).getTime(),
input: rawTrace.input,
output: rawTrace.output,
user_id: rawTrace.userId,
session_id: rawTrace.sessionId,
environment: rawTrace.environment,
metadata: {
...metadata,
// metadata to filter for those kinda traces
source: "framework-trace",
framework: frameworkName,
},
release: rawTrace.release,
version: rawTrace.version,
public: rawTrace.public,
bookmarked: rawTrace.bookmarked,
tags: rawTrace.tags,
});
// Map observation IDs (old -> new)
const obsIdMap = new Map<string, string>();
for (const obs of rawObservations) {
const newObsId = `framework-${frameworkName}-${obs.id}-${projectId.slice(-8)}`;
obsIdMap.set(obs.id, newObsId);
}
// Create observations
const observations: ObservationRecordInsertType[] = [];
for (const obs of rawObservations) {
const newObsId = obsIdMap.get(obs.id)!;
// Map parent observation ID
const parentObservationId = obs.parentObservationId
? obsIdMap.get(obs.parentObservationId) || null
: null;
// Convert input/output to strings, preserving null
const input =
obs.input === undefined || obs.input === null
? obs.input
: JSON.stringify(obs.input);
const output =
obs.output === undefined || obs.output === null
? obs.output
: JSON.stringify(obs.output);
// Parse metadata if it's a string
let obsMetadata = obs.metadata;
if (typeof obsMetadata === "string") {
obsMetadata = JSON.parse(obsMetadata);
}
// Use usage/cost details from JSON if present, otherwise reconstruct from flat fields
const usageDetails =
obs.usageDetails && Object.keys(obs.usageDetails).length > 0
? obs.usageDetails
: obs.totalUsage > 0
? {
input: obs.inputUsage,
output: obs.outputUsage,
total: obs.totalUsage,
}
: undefined;
const costDetails =
obs.costDetails && Object.keys(obs.costDetails).length > 0
? obs.costDetails
: obs.totalCost > 0
? {
input: (obs.totalCost * obs.inputUsage) / obs.totalUsage,
output: (obs.totalCost * obs.outputUsage) / obs.totalUsage,
total: obs.totalCost,
}
: undefined;
observations.push(
createObservation({
id: newObsId,
trace_id: newTraceId,
project_id: projectId,
type: obs.type,
parent_observation_id: parentObservationId,
start_time: new Date(obs.startTime).getTime(),
end_time: obs.endTime ? new Date(obs.endTime).getTime() : undefined,
name: obs.name,
metadata: obsMetadata,
level: obs.level,
status_message: obs.statusMessage,
input,
output,
provided_model_name: obs.model,
model_parameters: obs.modelParameters
? JSON.stringify(obs.modelParameters)
: undefined,
prompt_name: obs.promptName,
prompt_version: obs.promptVersion,
usage_details: usageDetails,
provided_usage_details: obs.providedUsageDetails,
cost_details: costDetails,
provided_cost_details: obs.providedCostDetails,
total_cost: obs.totalCost,
environment: rawTrace.environment,
}),
);
}
return { trace, observations, scores: [] };
}
}
File diff suppressed because it is too large Load Diff
File diff suppressed because one or more lines are too long
@@ -0,0 +1,863 @@
{
"trace": {
"id": "dff173a675b759ce1b70e522b27d6846",
"projectId": "cmcmdwcag00c2ad077xp1qnyc",
"name": "agent.f9f77d1a-437f-4ba6-890e-b592a50cbd21",
"timestamp": "2025-08-26T19:40:59.145Z",
"environment": "default",
"tags": [],
"bookmarked": false,
"release": null,
"version": "0.3.0",
"userId": null,
"sessionId": null,
"public": true,
"input": null,
"output": null,
"metadata": "{\"attributes\":{\"gen_ai.request.model\":\"gpt-4o\",\"gen_ai.system\":\"openai\",\"gen_ai.agent.id\":\"f9f77d1a-437f-4ba6-890e-b592a50cbd21\",\"gen_ai.operation.name\":\"create_agent\"},\"resourceAttributes\":{\"os.arch\":\"aarch64\",\"os.type\":\"Mac OS X\",\"os.version\":\"15.4.1\",\"service.instance.time\":\"2025-08-26T19:40:59.130398Z\",\"service.name\":\"ai.koog\",\"service.version\":\"0.3.0\"},\"scope\":{\"name\":\"ai.koog\",\"version\":\"0.3.0\",\"attributes\":{}}}",
"createdAt": "2025-08-26T19:40:59.145Z",
"updatedAt": "2025-08-26T19:41:05.434Z",
"scores": [],
"latency": 5.843
},
"observations": [
{
"id": "1b7200380e3316e7",
"traceId": "dff173a675b759ce1b70e522b27d6846",
"projectId": "cmcmdwcag00c2ad077xp1qnyc",
"type": "AGENT",
"environment": "default",
"parentObservationId": null,
"startTime": "2025-08-26T19:40:59.145Z",
"endTime": "2025-08-26T19:41:04.988Z",
"name": "agent.f9f77d1a-437f-4ba6-890e-b592a50cbd21",
"metadata": {
"attributes": {
"gen_ai.request.model": "gpt-4o",
"gen_ai.system": "openai",
"gen_ai.agent.id": "f9f77d1a-437f-4ba6-890e-b592a50cbd21",
"gen_ai.operation.name": "create_agent"
},
"resourceAttributes": {
"os.arch": "aarch64",
"os.type": "Mac OS X",
"os.version": "15.4.1",
"service.instance.time": "2025-08-26T19:40:59.130398Z",
"service.name": "ai.koog",
"service.version": "0.3.0"
},
"scope": {
"name": "ai.koog",
"version": "0.3.0",
"attributes": {}
}
},
"level": "DEFAULT",
"statusMessage": null,
"version": "0.3.0",
"modelParameters": {},
"completionStartTime": null,
"promptId": null,
"createdAt": "2025-08-26T19:41:05.401Z",
"updatedAt": "2025-08-26T19:41:05.420Z",
"usageDetails": {},
"costDetails": {},
"providedCostDetails": {},
"model": "gpt-4o",
"internalModelId": "b9854a5c92dc496b997d99d20",
"promptName": null,
"promptVersion": null,
"latency": 5843,
"timeToFirstToken": null,
"inputCost": null,
"outputCost": null,
"totalCost": 0,
"inputUsage": 0,
"outputUsage": 0,
"totalUsage": 0,
"input": null,
"output": null
},
{
"id": "bb189bc3771ff83d",
"traceId": "dff173a675b759ce1b70e522b27d6846",
"projectId": "cmcmdwcag00c2ad077xp1qnyc",
"type": "AGENT",
"environment": "default",
"parentObservationId": "1b7200380e3316e7",
"startTime": "2025-08-26T19:40:59.150Z",
"endTime": "2025-08-26T19:41:04.988Z",
"name": "run.6f8d2449-33ed-4c7c-82e7-5410e34ee4e6",
"metadata": {
"attributes": {
"koog.agent.strategy.name": "single_run_sequential",
"gen_ai.conversation.id": "6f8d2449-33ed-4c7c-82e7-5410e34ee4e6",
"gen_ai.system": "openai",
"gen_ai.agent.id": "f9f77d1a-437f-4ba6-890e-b592a50cbd21",
"gen_ai.operation.name": "invoke_agent"
},
"resourceAttributes": {
"os.arch": "aarch64",
"os.type": "Mac OS X",
"os.version": "15.4.1",
"service.instance.time": "2025-08-26T19:40:59.130398Z",
"service.name": "ai.koog",
"service.version": "0.3.0"
},
"scope": {
"name": "ai.koog",
"version": "0.3.0",
"attributes": {}
}
},
"level": "DEFAULT",
"statusMessage": null,
"version": "0.3.0",
"modelParameters": {},
"completionStartTime": null,
"promptId": null,
"createdAt": "2025-08-26T19:41:05.330Z",
"updatedAt": "2025-08-26T19:41:05.354Z",
"usageDetails": {},
"costDetails": {},
"providedCostDetails": {},
"model": null,
"internalModelId": null,
"promptName": null,
"promptVersion": null,
"latency": 5838,
"timeToFirstToken": null,
"inputCost": null,
"outputCost": null,
"totalCost": 0,
"inputUsage": 0,
"outputUsage": 0,
"totalUsage": 0,
"input": null,
"output": null
},
{
"id": "7578374e12a7faa5",
"traceId": "dff173a675b759ce1b70e522b27d6846",
"projectId": "cmcmdwcag00c2ad077xp1qnyc",
"type": "SPAN",
"environment": "default",
"parentObservationId": "bb189bc3771ff83d",
"startTime": "2025-08-26T19:41:04.984Z",
"endTime": "2025-08-26T19:41:04.985Z",
"name": "node.__finish__",
"metadata": {
"langgraph_node": "__finish__",
"langgraph_step": 4,
"attributes": {
"gen_ai.conversation.id": "6f8d2449-33ed-4c7c-82e7-5410e34ee4e6",
"koog.node.name": "__finish__",
"langfuse.observation.metadata.langgraph_node": "__finish__",
"langfuse.observation.metadata.langgraph_step": "4"
},
"resourceAttributes": {
"os.arch": "aarch64",
"os.type": "Mac OS X",
"os.version": "15.4.1",
"service.instance.time": "2025-08-26T19:40:59.130398Z",
"service.name": "ai.koog",
"service.version": "0.3.0"
},
"scope": {
"name": "ai.koog",
"version": "0.3.0",
"attributes": {}
}
},
"level": "DEFAULT",
"statusMessage": null,
"version": "0.3.0",
"modelParameters": null,
"completionStartTime": null,
"promptId": null,
"createdAt": "2025-08-26T19:41:05.313Z",
"updatedAt": "2025-08-26T19:41:05.340Z",
"usageDetails": {},
"costDetails": {},
"providedCostDetails": {},
"model": null,
"internalModelId": null,
"promptName": null,
"promptVersion": null,
"latency": 1,
"timeToFirstToken": null,
"inputCost": null,
"outputCost": null,
"totalCost": 0,
"inputUsage": 0,
"outputUsage": 0,
"totalUsage": 0,
"input": null,
"output": null
},
{
"id": "e44e72cf2d221781",
"traceId": "dff173a675b759ce1b70e522b27d6846",
"projectId": "cmcmdwcag00c2ad077xp1qnyc",
"type": "GENERATION",
"environment": "default",
"parentObservationId": "6021df46275ad2d8",
"startTime": "2025-08-26T19:41:03.041Z",
"endTime": "2025-08-26T19:41:04.983Z",
"name": "llm.chat",
"metadata": {
"attributes": {
"gen_ai.prompt.4.content": [
{
"function": {
"name": "ByeTool",
"arguments": {
"name": "Bob"
}
},
"id": "call_dOeEHJDI0liHrCdFWTpkTsuO",
"type": "function"
}
],
"gen_ai.prompt.5.role": "tool",
"gen_ai.completion.0.content": "Hello my darling Bob! And, bye my dear Bob!",
"gen_ai.request.temperature": "1",
"gen_ai.prompt.3.role": "tool",
"gen_ai.prompt.0.role": "system",
"gen_ai.prompt.5.content": "Bye my dear Bob!",
"gen_ai.completion.0.role": "assistant",
"gen_ai.prompt.4.role": "tool",
"gen_ai.request.model": "gpt-4o",
"gen_ai.conversation.id": "6f8d2449-33ed-4c7c-82e7-5410e34ee4e6",
"gen_ai.prompt.0.content": "You are a nice and polite assistant.",
"gen_ai.prompt.1.role": "user",
"gen_ai.response.finish_reasons": [
"stop"
],
"gen_ai.prompt.1.content": "Greet and say goodbye to the user in the same message. Use provided tools to generate a proper greeting. The user name is Bob.",
"gen_ai.system": "openai",
"gen_ai.prompt.2.content": [
{
"function": {
"name": "HelloTool",
"arguments": {
"name": "Bob"
}
},
"id": "call_n2HaWg9KFP0BZDqXv6G4b3nJ",
"type": "function"
}
],
"gen_ai.operation.name": "chat",
"gen_ai.prompt.2.role": "tool",
"gen_ai.prompt.3.content": "Hello my darling Bob!"
},
"resourceAttributes": {
"os.arch": "aarch64",
"os.type": "Mac OS X",
"os.version": "15.4.1",
"service.instance.time": "2025-08-26T19:40:59.130398Z",
"service.name": "ai.koog",
"service.version": "0.3.0"
},
"scope": {
"name": "ai.koog",
"version": "0.3.0",
"attributes": {}
}
},
"level": "DEFAULT",
"statusMessage": null,
"version": "0.3.0",
"modelParameters": {
"temperature": 1
},
"completionStartTime": null,
"promptId": null,
"createdAt": "2025-08-26T19:41:05.314Z",
"updatedAt": "2025-08-26T19:41:05.333Z",
"usageDetails": {
"input": 168,
"output": 19,
"total": 187
},
"costDetails": {
"input": 0.00042,
"output": 0.00019,
"total": 0.00061
},
"providedCostDetails": {},
"model": "gpt-4o",
"internalModelId": "b9854a5c92dc496b997d99d20",
"promptName": null,
"promptVersion": null,
"latency": 1942,
"timeToFirstToken": null,
"inputCost": 0.00042,
"outputCost": 0.00019,
"totalCost": 0.00061,
"inputUsage": 168,
"outputUsage": 19,
"totalUsage": 187,
"input": [
{
"role": "system",
"content": "You are a nice and polite assistant."
},
{
"role": "user",
"content": "Greet and say goodbye to the user in the same message. Use provided tools to generate a proper greeting. The user name is Bob."
},
{
"content": [
{
"function": {
"name": "HelloTool",
"arguments": {
"name": "Bob"
}
},
"id": "call_n2HaWg9KFP0BZDqXv6G4b3nJ",
"type": "function"
}
],
"role": "tool"
},
{
"role": "tool",
"content": "Hello my darling Bob!"
},
{
"content": [
{
"function": {
"name": "ByeTool",
"arguments": {
"name": "Bob"
}
},
"id": "call_dOeEHJDI0liHrCdFWTpkTsuO",
"type": "function"
}
],
"role": "tool"
},
{
"role": "tool",
"content": "Bye my dear Bob!"
}
],
"output": [
{
"content": "Hello my darling Bob! And, bye my dear Bob!",
"role": "assistant"
}
]
},
{
"id": "6021df46275ad2d8",
"traceId": "dff173a675b759ce1b70e522b27d6846",
"projectId": "cmcmdwcag00c2ad077xp1qnyc",
"type": "SPAN",
"environment": "default",
"parentObservationId": "bb189bc3771ff83d",
"startTime": "2025-08-26T19:41:03.038Z",
"endTime": "2025-08-26T19:41:04.984Z",
"name": "node.nodeSendToolResult",
"metadata": {
"langgraph_node": "nodeSendToolResult",
"langgraph_step": 3,
"attributes": {
"gen_ai.conversation.id": "6f8d2449-33ed-4c7c-82e7-5410e34ee4e6",
"koog.node.name": "nodeSendToolResult",
"langfuse.observation.metadata.langgraph_node": "nodeSendToolResult",
"langfuse.observation.metadata.langgraph_step": "3"
},
"resourceAttributes": {
"os.arch": "aarch64",
"os.type": "Mac OS X",
"os.version": "15.4.1",
"service.instance.time": "2025-08-26T19:40:59.130398Z",
"service.name": "ai.koog",
"service.version": "0.3.0"
},
"scope": {
"name": "ai.koog",
"version": "0.3.0",
"attributes": {}
}
},
"level": "DEFAULT",
"statusMessage": null,
"version": "0.3.0",
"modelParameters": null,
"completionStartTime": null,
"promptId": null,
"createdAt": "2025-08-26T19:41:05.305Z",
"updatedAt": "2025-08-26T19:41:05.324Z",
"usageDetails": {},
"costDetails": {},
"providedCostDetails": {},
"model": null,
"internalModelId": null,
"promptName": null,
"promptVersion": null,
"latency": 1946,
"timeToFirstToken": null,
"inputCost": null,
"outputCost": null,
"totalCost": 0,
"inputUsage": 0,
"outputUsage": 0,
"totalUsage": 0,
"input": null,
"output": null
},
{
"id": "33188493784a060d",
"traceId": "dff173a675b759ce1b70e522b27d6846",
"projectId": "cmcmdwcag00c2ad077xp1qnyc",
"type": "SPAN",
"environment": "default",
"parentObservationId": "5555afb6655e4d44",
"startTime": "2025-08-26T19:41:02.023Z",
"endTime": "2025-08-26T19:41:03.037Z",
"name": "tool.ByeTool",
"metadata": {
"attributes": {
"input.value": {
"name": "Bob"
},
"output.value": "Bye my dear Bob!",
"gen_ai.tool.description": "A tool that says bye to the user",
"gen_ai.tool.call.id": "call_dOeEHJDI0liHrCdFWTpkTsuO",
"gen_ai.tool.name": "ByeTool"
},
"resourceAttributes": {
"os.arch": "aarch64",
"os.type": "Mac OS X",
"os.version": "15.4.1",
"service.instance.time": "2025-08-26T19:40:59.130398Z",
"service.name": "ai.koog",
"service.version": "0.3.0"
},
"scope": {
"name": "ai.koog",
"version": "0.3.0",
"attributes": {}
}
},
"level": "DEFAULT",
"statusMessage": null,
"version": "0.3.0",
"modelParameters": null,
"completionStartTime": null,
"promptId": null,
"createdAt": "2025-08-26T19:41:03.511Z",
"updatedAt": "2025-08-26T19:41:03.531Z",
"usageDetails": {},
"costDetails": {},
"providedCostDetails": {},
"model": null,
"internalModelId": null,
"promptName": null,
"promptVersion": null,
"latency": 1014,
"timeToFirstToken": null,
"inputCost": null,
"outputCost": null,
"totalCost": 0,
"inputUsage": 0,
"outputUsage": 0,
"totalUsage": 0,
"input": {
"name": "Bob"
},
"output": "Bye my dear Bob!"
},
{
"id": "66f269083a2fff81",
"traceId": "dff173a675b759ce1b70e522b27d6846",
"projectId": "cmcmdwcag00c2ad077xp1qnyc",
"type": "SPAN",
"environment": "default",
"parentObservationId": "5555afb6655e4d44",
"startTime": "2025-08-26T19:41:02.023Z",
"endTime": "2025-08-26T19:41:03.034Z",
"name": "tool.HelloTool",
"metadata": {
"attributes": {
"input.value": {
"name": "Bob"
},
"output.value": "Hello my darling Bob!",
"gen_ai.tool.description": "A tool greets the user",
"gen_ai.tool.call.id": "call_n2HaWg9KFP0BZDqXv6G4b3nJ",
"gen_ai.tool.name": "HelloTool"
},
"resourceAttributes": {
"os.arch": "aarch64",
"os.type": "Mac OS X",
"os.version": "15.4.1",
"service.instance.time": "2025-08-26T19:40:59.130398Z",
"service.name": "ai.koog",
"service.version": "0.3.0"
},
"scope": {
"name": "ai.koog",
"version": "0.3.0",
"attributes": {}
}
},
"level": "DEFAULT",
"statusMessage": null,
"version": "0.3.0",
"modelParameters": null,
"completionStartTime": null,
"promptId": null,
"createdAt": "2025-08-26T19:41:03.431Z",
"updatedAt": "2025-08-26T19:41:03.479Z",
"usageDetails": {},
"costDetails": {},
"providedCostDetails": {},
"model": null,
"internalModelId": null,
"promptName": null,
"promptVersion": null,
"latency": 1011,
"timeToFirstToken": null,
"inputCost": null,
"outputCost": null,
"totalCost": 0,
"inputUsage": 0,
"outputUsage": 0,
"totalUsage": 0,
"input": {
"name": "Bob"
},
"output": "Hello my darling Bob!"
},
{
"id": "5555afb6655e4d44",
"traceId": "dff173a675b759ce1b70e522b27d6846",
"projectId": "cmcmdwcag00c2ad077xp1qnyc",
"type": "SPAN",
"environment": "default",
"parentObservationId": "bb189bc3771ff83d",
"startTime": "2025-08-26T19:41:02.017Z",
"endTime": "2025-08-26T19:41:03.038Z",
"name": "node.nodeExecuteTool",
"metadata": {
"langgraph_node": "nodeExecuteTool",
"langgraph_step": 2,
"attributes": {
"gen_ai.conversation.id": "6f8d2449-33ed-4c7c-82e7-5410e34ee4e6",
"koog.node.name": "nodeExecuteTool",
"langfuse.observation.metadata.langgraph_node": "nodeExecuteTool",
"langfuse.observation.metadata.langgraph_step": "2"
},
"resourceAttributes": {
"os.arch": "aarch64",
"os.type": "Mac OS X",
"os.version": "15.4.1",
"service.instance.time": "2025-08-26T19:40:59.130398Z",
"service.name": "ai.koog",
"service.version": "0.3.0"
},
"scope": {
"name": "ai.koog",
"version": "0.3.0",
"attributes": {}
}
},
"level": "DEFAULT",
"statusMessage": null,
"version": "0.3.0",
"modelParameters": null,
"completionStartTime": null,
"promptId": null,
"createdAt": "2025-08-26T19:41:03.432Z",
"updatedAt": "2025-08-26T19:41:03.456Z",
"usageDetails": {},
"costDetails": {},
"providedCostDetails": {},
"model": null,
"internalModelId": null,
"promptName": null,
"promptVersion": null,
"latency": 1021,
"timeToFirstToken": null,
"inputCost": null,
"outputCost": null,
"totalCost": 0,
"inputUsage": 0,
"outputUsage": 0,
"totalUsage": 0,
"input": null,
"output": null
},
{
"id": "6a0770019fe7c2b4",
"traceId": "dff173a675b759ce1b70e522b27d6846",
"projectId": "cmcmdwcag00c2ad077xp1qnyc",
"type": "SPAN",
"environment": "default",
"parentObservationId": "bb189bc3771ff83d",
"startTime": "2025-08-26T19:40:59.171Z",
"endTime": "2025-08-26T19:41:02.017Z",
"name": "node.nodeCallLLM",
"metadata": {
"langgraph_node": "nodeCallLLM",
"langgraph_step": 1,
"attributes": {
"gen_ai.conversation.id": "6f8d2449-33ed-4c7c-82e7-5410e34ee4e6",
"koog.node.name": "nodeCallLLM",
"langfuse.observation.metadata.langgraph_node": "nodeCallLLM",
"langfuse.observation.metadata.langgraph_step": "1"
},
"resourceAttributes": {
"os.arch": "aarch64",
"os.type": "Mac OS X",
"os.version": "15.4.1",
"service.instance.time": "2025-08-26T19:40:59.130398Z",
"service.name": "ai.koog",
"service.version": "0.3.0"
},
"scope": {
"name": "ai.koog",
"version": "0.3.0",
"attributes": {}
}
},
"level": "DEFAULT",
"statusMessage": null,
"version": "0.3.0",
"modelParameters": null,
"completionStartTime": null,
"promptId": null,
"createdAt": "2025-08-26T19:41:02.422Z",
"updatedAt": "2025-08-26T19:41:02.440Z",
"usageDetails": {},
"costDetails": {},
"providedCostDetails": {},
"model": null,
"internalModelId": null,
"promptName": null,
"promptVersion": null,
"latency": 2846,
"timeToFirstToken": null,
"inputCost": null,
"outputCost": null,
"totalCost": 0,
"inputUsage": 0,
"outputUsage": 0,
"totalUsage": 0,
"input": null,
"output": null
},
{
"id": "042f9350c3729147",
"traceId": "dff173a675b759ce1b70e522b27d6846",
"projectId": "cmcmdwcag00c2ad077xp1qnyc",
"type": "GENERATION",
"environment": "default",
"parentObservationId": "6a0770019fe7c2b4",
"startTime": "2025-08-26T19:40:59.176Z",
"endTime": "2025-08-26T19:41:02.016Z",
"name": "llm.chat",
"metadata": {
"attributes": {
"gen_ai.completion.0.content": [
{
"function": {
"name": "HelloTool",
"arguments": {
"name": "Bob"
}
},
"id": "call_n2HaWg9KFP0BZDqXv6G4b3nJ",
"type": "function"
}
],
"gen_ai.request.temperature": "1",
"gen_ai.prompt.0.role": "system",
"gen_ai.completion.0.role": "assistant",
"gen_ai.request.model": "gpt-4o",
"gen_ai.conversation.id": "6f8d2449-33ed-4c7c-82e7-5410e34ee4e6",
"gen_ai.completion.0.finish_reason": "tool_calls",
"gen_ai.prompt.0.content": "You are a nice and polite assistant.",
"gen_ai.prompt.1.role": "user",
"gen_ai.response.finish_reasons": [
"tool_calls"
],
"gen_ai.prompt.1.content": "Greet and say goodbye to the user in the same message. Use provided tools to generate a proper greeting. The user name is Bob.",
"gen_ai.completion.1.finish_reason": "tool_calls",
"gen_ai.system": "openai",
"gen_ai.completion.1.role": "assistant",
"gen_ai.completion.1.content": [
{
"function": {
"name": "ByeTool",
"arguments": {
"name": "Bob"
}
},
"id": "call_dOeEHJDI0liHrCdFWTpkTsuO",
"type": "function"
}
],
"gen_ai.operation.name": "chat"
},
"resourceAttributes": {
"os.arch": "aarch64",
"os.type": "Mac OS X",
"os.version": "15.4.1",
"service.instance.time": "2025-08-26T19:40:59.130398Z",
"service.name": "ai.koog",
"service.version": "0.3.0"
},
"scope": {
"name": "ai.koog",
"version": "0.3.0",
"attributes": {}
}
},
"level": "DEFAULT",
"statusMessage": null,
"version": "0.3.0",
"modelParameters": {
"temperature": 1
},
"completionStartTime": null,
"promptId": null,
"createdAt": "2025-08-26T19:41:02.328Z",
"updatedAt": "2025-08-26T19:41:02.356Z",
"usageDetails": {
"input": 47,
"output": 106,
"total": 153
},
"costDetails": {
"input": 0.0001175,
"output": 0.00106,
"total": 0.0011775
},
"providedCostDetails": {},
"model": "gpt-4o",
"internalModelId": "b9854a5c92dc496b997d99d20",
"promptName": null,
"promptVersion": null,
"latency": 2840,
"timeToFirstToken": null,
"inputCost": 0.0001175,
"outputCost": 0.00106,
"totalCost": 0.0011775,
"inputUsage": 47,
"outputUsage": 106,
"totalUsage": 153,
"input": [
{
"role": "system",
"content": "You are a nice and polite assistant."
},
{
"role": "user",
"content": "Greet and say goodbye to the user in the same message. Use provided tools to generate a proper greeting. The user name is Bob."
}
],
"output": [
{
"content": [
{
"function": {
"name": "HelloTool",
"arguments": {
"name": "Bob"
}
},
"id": "call_n2HaWg9KFP0BZDqXv6G4b3nJ",
"type": "function"
}
],
"role": "assistant",
"finish_reason": "tool_calls"
},
{
"finish_reason": "tool_calls",
"role": "assistant",
"content": [
{
"function": {
"name": "ByeTool",
"arguments": {
"name": "Bob"
}
},
"id": "call_dOeEHJDI0liHrCdFWTpkTsuO",
"type": "function"
}
]
}
]
},
{
"id": "890e9114a4449257",
"traceId": "dff173a675b759ce1b70e522b27d6846",
"projectId": "cmcmdwcag00c2ad077xp1qnyc",
"type": "SPAN",
"environment": "default",
"parentObservationId": "bb189bc3771ff83d",
"startTime": "2025-08-26T19:40:59.153Z",
"endTime": "2025-08-26T19:40:59.153Z",
"name": "node.__start__",
"metadata": {
"langgraph_node": "__start__",
"langgraph_step": 0,
"attributes": {
"gen_ai.conversation.id": "6f8d2449-33ed-4c7c-82e7-5410e34ee4e6",
"koog.node.name": "__start__",
"langfuse.observation.metadata.langgraph_node": "__start__",
"langfuse.observation.metadata.langgraph_step": "0"
},
"resourceAttributes": {
"os.arch": "aarch64",
"os.type": "Mac OS X",
"os.version": "15.4.1",
"service.instance.time": "2025-08-26T19:40:59.130398Z",
"service.name": "ai.koog",
"service.version": "0.3.0"
},
"scope": {
"name": "ai.koog",
"version": "0.3.0",
"attributes": {}
}
},
"level": "DEFAULT",
"statusMessage": null,
"version": "0.3.0",
"modelParameters": null,
"completionStartTime": null,
"promptId": null,
"createdAt": "2025-08-26T19:40:59.897Z",
"updatedAt": "2025-08-26T19:40:59.915Z",
"usageDetails": {},
"costDetails": {},
"providedCostDetails": {},
"model": null,
"internalModelId": null,
"promptName": null,
"promptVersion": null,
"latency": 0,
"timeToFirstToken": null,
"inputCost": null,
"outputCost": null,
"totalCost": 0,
"inputUsage": 0,
"outputUsage": 0,
"totalUsage": 0,
"input": null,
"output": null
}
]
}
@@ -0,0 +1,182 @@
{
"trace": {
"id": "12ea412956f99347b0503c1144acd0ec",
"projectId": "cloramnkj0002jz088vzn1ja4",
"name": "llama-index-trace",
"timestamp": "2025-06-05T15:45:52.971Z",
"environment": "default",
"tags": [
"llama-index",
"rag"
],
"bookmarked": false,
"release": null,
"version": "1.0.0",
"userId": "user_123",
"sessionId": "session_abc",
"public": true,
"input": "\"What is Langfuse?\"",
"output": "\"Langfuse is a tool designed to help developers monitor and debug applications that utilize large language models (LLMs). It provides features for tracking and analyzing the performance of LLMs, enabling developers to gain insights into how these models are functioning within their applications. Langfuse can be particularly useful for identifying issues, optimizing performance, and ensuring that the integration of language models into applications is smooth and effective.\"",
"metadata": "{\"email\":\"user@langfuse.com\",\"resourceAttributes\":{\"telemetry.sdk.language\":\"python\",\"telemetry.sdk.name\":\"opentelemetry\",\"telemetry.sdk.version\":\"1.33.1\",\"service.name\":\"unknown_service\"},\"scope\":{\"name\":\"langfuse-sdk\",\"version\":\"3.0.0\",\"attributes\":{\"public_key\":\"pk-lf-5855d85e-3943-497e-bd10-f50ad414bcba\"}}}",
"createdAt": "2025-06-05T15:45:56.000Z",
"updatedAt": "2025-06-05T15:45:56.029Z",
"scores": [],
"latency": 2.779
},
"observations": [
{
"id": "3344aabdd4e66110",
"traceId": "12ea412956f99347b0503c1144acd0ec",
"projectId": "cloramnkj0002jz088vzn1ja4",
"type": "SPAN",
"environment": "default",
"parentObservationId": null,
"startTime": "2025-06-05T15:45:52.971Z",
"endTime": "2025-06-05T15:45:55.750Z",
"name": "llama-index-trace",
"metadata": {
"resourceAttributes": {
"telemetry.sdk.language": "python",
"telemetry.sdk.name": "opentelemetry",
"telemetry.sdk.version": "1.33.1",
"service.name": "unknown_service"
},
"scope": {
"name": "langfuse-sdk",
"version": "3.0.0",
"attributes": {
"public_key": "pk-lf-5855d85e-3943-497e-bd10-f50ad414bcba"
}
}
},
"level": "DEFAULT",
"statusMessage": null,
"version": "1.0.0",
"modelParameters": null,
"completionStartTime": null,
"promptId": null,
"createdAt": "2025-06-05T15:45:55.996Z",
"updatedAt": "2025-06-05T15:45:56.027Z",
"usageDetails": {},
"costDetails": {},
"providedCostDetails": {},
"model": null,
"internalModelId": null,
"promptName": null,
"promptVersion": null,
"latency": 2779,
"timeToFirstToken": null,
"inputCost": null,
"outputCost": null,
"totalCost": 0,
"inputUsage": 0,
"outputUsage": 0,
"totalUsage": 0,
"input": null,
"output": null
},
{
"id": "7ca8b22f6628d0c7",
"traceId": "12ea412956f99347b0503c1144acd0ec",
"projectId": "cloramnkj0002jz088vzn1ja4",
"type": "GENERATION",
"environment": "default",
"parentObservationId": "3344aabdd4e66110",
"startTime": "2025-06-05T15:45:52.971Z",
"endTime": "2025-06-05T15:45:55.747Z",
"name": "OpenAI.complete",
"metadata": {
"attributes": {
"llm.model_name": "gpt-4o",
"llm.invocation_parameters": {
"context_window": 128000,
"num_output": -1,
"is_chat_model": true,
"is_function_calling_model": true,
"model_name": "gpt-4o",
"system_role": "system"
},
"llm.provider": "openai",
"llm.system": "openai",
"input.value": {
"args": [
"What is Langfuse?"
]
},
"input.mime_type": "application/json",
"llm.prompts": [
"What is Langfuse?"
],
"output.value": "Langfuse is a tool designed to help developers monitor and debug applications that utilize large language models (LLMs). It provides features for tracking and analyzing the performance of LLMs, enabling developers to gain insights into how these models are functioning within their applications. Langfuse can be particularly useful for identifying issues, optimizing performance, and ensuring that the integration of language models into applications is smooth and effective.",
"llm.token_count.prompt": "13",
"llm.token_count.prompt_details.cache_read": "0",
"llm.token_count.prompt_details.audio": "0",
"llm.token_count.completion": "81",
"llm.token_count.completion_details.reasoning": "0",
"llm.token_count.completion_details.audio": "0",
"llm.token_count.total": "94",
"openinference.span.kind": "LLM"
},
"resourceAttributes": {
"telemetry.sdk.language": "python",
"telemetry.sdk.name": "opentelemetry",
"telemetry.sdk.version": "1.33.1",
"service.name": "unknown_service"
},
"scope": {
"name": "openinference.instrumentation.llama_index",
"version": "4.3.0",
"attributes": {}
}
},
"level": "DEFAULT",
"statusMessage": null,
"version": null,
"modelParameters": {
"context_window": 128000,
"num_output": -1,
"is_chat_model": true,
"is_function_calling_model": true,
"model_name": "gpt-4o",
"system_role": "system"
},
"completionStartTime": null,
"promptId": null,
"createdAt": "2025-06-05T15:45:55.866Z",
"updatedAt": "2025-06-05T15:45:56.021Z",
"usageDetails": {
"input": 13,
"prompt_details.cache_read": 0,
"prompt_details.audio": 0,
"output": 81,
"completion_details.reasoning": 0,
"completion_details.audio": 0,
"total": 94
},
"costDetails": {
"input": 0.0000325,
"output": 0.00081,
"total": 0.000842499999
},
"providedCostDetails": {},
"model": "gpt-4o",
"internalModelId": "b9854a5c92dc496b997d99d20",
"promptName": null,
"promptVersion": null,
"latency": 2776,
"timeToFirstToken": null,
"inputCost": 0.0000325,
"outputCost": 0.00081,
"totalCost": 0.000842499999,
"inputUsage": 13,
"outputUsage": 81,
"totalUsage": 94,
"input": {
"args": [
"What is Langfuse?"
]
},
"output": "Langfuse is a tool designed to help developers monitor and debug applications that utilize large language models (LLMs). It provides features for tracking and analyzing the performance of LLMs, enabling developers to gain insights into how these models are functioning within their applications. Langfuse can be particularly useful for identifying issues, optimizing performance, and ensuring that the integration of language models into applications is smooth and effective."
}
]
}
@@ -0,0 +1,67 @@
import { readFileSync, writeFileSync } from "fs";
/**
* Merges detailed observation data (input/output/metadata) into the base trace file.
* how to use? SEE README.md
*
* Usage: ts-node merge-observations.ts <base-file> <detailed-obs-file> <output-file>
*
* Example:
* ts-node merge-observations.ts pydantic-base.json pydantic-details.json pydantic-ai.json
*
*/
const [baseFile, detailedFile, outputFile] = process.argv.slice(2);
if (!baseFile || !detailedFile || !outputFile) {
console.error(
"Usage: ts-node merge-observations.ts <base-file> <detailed-obs-file> <output-file>",
);
process.exit(1);
}
const baseData = JSON.parse(readFileSync(baseFile, "utf-8"));
const detailedObs = JSON.parse(readFileSync(detailedFile, "utf-8"));
// Build ID to detailed observation map
const obsMap = new Map<string, any>();
for (const obs of Object.values(detailedObs)) {
obsMap.set((obs as any).id, obs);
}
// Merge input/output/metadata into observations array
const mergedObservations = baseData.observations.map((obs: any) => {
const detailed = obsMap.get(obs.id);
if (detailed) {
return {
...obs,
input: detailed.input,
output: detailed.output,
metadata: detailed.metadata,
};
}
return obs;
});
// demo project
const PROJECT_ID = "7a88fb47-b4e2-43b8-a06c-a5ce950dc53a";
// Remove observations from trace, create final structure
const output = {
trace: {
...baseData.trace,
projectId: PROJECT_ID,
},
observations: mergedObservations.map((obs: any) => ({
...obs,
projectId: PROJECT_ID,
})),
};
// Remove observations array from trace if it exists
delete (output.trace as any).observations;
writeFileSync(outputFile, JSON.stringify(output, null, 2));
console.log(`Merged ${mergedObservations.length} observations`);
console.log(`Wrote to ${outputFile}`);
@@ -0,0 +1,931 @@
{
"trace": {
"id": "fee618f96dc31e0ca38b2f7b26eb8b29",
"projectId": "cloramnkj0002jz088vzn1ja4",
"name": "Agent workflow",
"timestamp": "2025-09-30T09:03:59.962Z",
"environment": "default",
"tags": [],
"bookmarked": false,
"release": null,
"version": null,
"userId": null,
"sessionId": null,
"public": true,
"input": null,
"output": null,
"metadata": "{\"attributes\":{\"openinference.span.kind\":\"AGENT\"},\"resourceAttributes\":{\"telemetry.sdk.language\":\"python\",\"telemetry.sdk.name\":\"opentelemetry\",\"telemetry.sdk.version\":\"1.37.0\",\"service.name\":\"unknown_service\"},\"scope\":{\"name\":\"openinference.instrumentation.openai_agents\",\"version\":\"1.3.0\",\"attributes\":{}}}",
"createdAt": "2025-09-30T09:04:03.000Z",
"updatedAt": "2025-09-30T09:04:07.465Z",
"scores": [
{
"id": "0721bd4f-a35c-4d29-a2c6-38879e1d758e",
"timestamp": "2025-09-30T09:04:30.125Z",
"projectId": "cloramnkj0002jz088vzn1ja4",
"environment": "default",
"name": "toxicity",
"source": "EVAL",
"authorUserId": null,
"comment": "The generation contains aggressive and dismissive language that undermines trust in professionals and promotes a negative view of the public's intelligence, which is harmful and disrespectful.",
"metadata": "{}",
"configId": null,
"createdAt": "2025-09-30T09:04:31.000Z",
"updatedAt": "2025-09-30T09:04:30.197Z",
"queueId": null,
"traceId": "fee618f96dc31e0ca38b2f7b26eb8b29",
"observationId": null,
"sessionId": null,
"datasetRunId": null,
"value": 0.8,
"stringValue": null,
"dataType": "NUMERIC"
},
{
"id": "f60dc1c4-17af-4512-ada9-f2440f437ce1",
"timestamp": "2025-09-30T09:04:28.687Z",
"projectId": "cloramnkj0002jz088vzn1ja4",
"environment": "default",
"name": "hallucination",
"source": "EVAL",
"authorUserId": null,
"comment": "There is no specific generation provided to evaluate for hallucination. Therefore, it is impossible to assess or score the degree of hallucination in this case.",
"metadata": "{}",
"configId": null,
"createdAt": "2025-09-30T09:04:29.000Z",
"updatedAt": "2025-09-30T09:04:28.773Z",
"queueId": null,
"traceId": "fee618f96dc31e0ca38b2f7b26eb8b29",
"observationId": null,
"sessionId": null,
"datasetRunId": null,
"value": 0,
"stringValue": null,
"dataType": "NUMERIC"
}
],
"latency": 2.19
},
"observations": [
{
"id": "e5a76f27f51ad40e",
"traceId": "fee618f96dc31e0ca38b2f7b26eb8b29",
"projectId": "cloramnkj0002jz088vzn1ja4",
"type": "AGENT",
"environment": "default",
"parentObservationId": null,
"startTime": "2025-09-30T09:03:59.962Z",
"endTime": "2025-09-30T09:04:02.152Z",
"name": "Agent workflow",
"metadata": {
"attributes": {
"openinference.span.kind": "AGENT"
},
"resourceAttributes": {
"telemetry.sdk.language": "python",
"telemetry.sdk.name": "opentelemetry",
"telemetry.sdk.version": "1.37.0",
"service.name": "unknown_service"
},
"scope": {
"name": "openinference.instrumentation.openai_agents",
"version": "1.3.0",
"attributes": {}
}
},
"level": "DEFAULT",
"statusMessage": null,
"version": null,
"modelParameters": {},
"completionStartTime": null,
"promptId": null,
"createdAt": "2025-09-30T09:04:07.382Z",
"updatedAt": "2025-09-30T09:04:07.382Z",
"usageDetails": {},
"costDetails": {},
"providedCostDetails": {},
"model": null,
"internalModelId": null,
"promptName": null,
"promptVersion": null,
"latency": 2190,
"timeToFirstToken": null,
"inputCost": null,
"outputCost": null,
"totalCost": 0,
"inputUsage": 0,
"outputUsage": 0,
"totalUsage": 0,
"input": null,
"output": null
},
{
"id": "d950b668796240b5",
"traceId": "fee618f96dc31e0ca38b2f7b26eb8b29",
"projectId": "cloramnkj0002jz088vzn1ja4",
"type": "AGENT",
"environment": "default",
"parentObservationId": "e5a76f27f51ad40e",
"startTime": "2025-09-30T09:03:59.962Z",
"endTime": "2025-09-30T09:04:02.152Z",
"name": "Hello world",
"metadata": {
"attributes": {
"llm.system": "openai",
"graph.node.id": "Hello world",
"openinference.span.kind": "AGENT"
},
"resourceAttributes": {
"telemetry.sdk.language": "python",
"telemetry.sdk.name": "opentelemetry",
"telemetry.sdk.version": "1.37.0",
"service.name": "unknown_service"
},
"scope": {
"name": "openinference.instrumentation.openai_agents",
"version": "1.3.0",
"attributes": {}
}
},
"level": "DEFAULT",
"statusMessage": null,
"version": null,
"modelParameters": {},
"completionStartTime": null,
"promptId": null,
"createdAt": "2025-09-30T09:04:07.382Z",
"updatedAt": "2025-09-30T09:04:07.382Z",
"usageDetails": {},
"costDetails": {},
"providedCostDetails": {},
"model": null,
"internalModelId": null,
"promptName": null,
"promptVersion": null,
"latency": 2190,
"timeToFirstToken": null,
"inputCost": null,
"outputCost": null,
"totalCost": 0,
"inputUsage": 0,
"outputUsage": 0,
"totalUsage": 0,
"input": null,
"output": null
},
{
"id": "90d94774e8e3724d",
"traceId": "fee618f96dc31e0ca38b2f7b26eb8b29",
"projectId": "cloramnkj0002jz088vzn1ja4",
"type": "GENERATION",
"environment": "default",
"parentObservationId": "d950b668796240b5",
"startTime": "2025-09-30T09:04:01.001Z",
"endTime": "2025-09-30T09:04:02.151Z",
"name": "response",
"metadata": {
"attributes": {
"llm.system": "openai",
"output.mime_type": "application/json",
"output.value": {
"id": "resp_0f00ca5b7e22bb4c0068db9d0110d8819da676bd1f2dd03a40",
"created_at": 1759223041,
"error": null,
"incomplete_details": null,
"instructions": "You are a helpful agent.",
"metadata": {},
"model": "gpt-4.1-2025-04-14",
"object": "response",
"output": [
{
"id": "msg_0f00ca5b7e22bb4c0068db9d019a78819d9fe1e4d3b6c96b68",
"content": [
{
"annotations": [],
"text": "The weather in Tokyo is currently sunny. If you need more details like temperature or forecast for the upcoming days, just let me know!",
"type": "output_text",
"logprobs": []
}
],
"role": "assistant",
"status": "completed",
"type": "message"
}
],
"parallel_tool_calls": true,
"temperature": 1,
"tool_choice": "auto",
"tools": [
{
"name": "get_weather",
"parameters": {
"properties": {
"city": {
"title": "City",
"type": "string"
}
},
"required": ["city"],
"title": "get_weather_args",
"type": "object",
"additionalProperties": false
},
"strict": true,
"type": "function",
"description": null
}
],
"top_p": 1,
"background": false,
"conversation": null,
"max_output_tokens": null,
"max_tool_calls": null,
"previous_response_id": null,
"prompt": null,
"prompt_cache_key": null,
"reasoning": {
"effort": null,
"generate_summary": null,
"summary": null
},
"safety_identifier": null,
"service_tier": "default",
"status": "completed",
"text": {
"format": {
"type": "text"
},
"verbosity": "medium"
},
"top_logprobs": 0,
"truncation": "disabled",
"usage": {
"input_tokens": 85,
"input_tokens_details": {
"cached_tokens": 0
},
"output_tokens": 29,
"output_tokens_details": {
"reasoning_tokens": 0
},
"total_tokens": 114
},
"user": null,
"billing": {
"payer": "developer"
},
"store": true
},
"llm.tools.0.tool.json_schema": {
"type": "function",
"function": {
"name": "get_weather",
"description": null,
"parameters": {
"properties": {
"city": {
"title": "City",
"type": "string"
}
},
"required": ["city"],
"title": "get_weather_args",
"type": "object",
"additionalProperties": false
},
"strict": true
}
},
"llm.token_count.completion": "29",
"llm.token_count.prompt": "85",
"llm.token_count.total": "114",
"llm.token_count.prompt_details.cache_read": "0",
"llm.token_count.completion_details.reasoning": "0",
"llm.output_messages.0.message.role": "assistant",
"llm.output_messages.0.message.contents.0.message_content.type": "text",
"llm.output_messages.0.message.contents.0.message_content.text": "The weather in Tokyo is currently sunny. If you need more details like temperature or forecast for the upcoming days, just let me know!",
"llm.input_messages.0.message.role": "system",
"llm.input_messages.0.message.content": "You are a helpful agent.",
"llm.model_name": "gpt-4.1-2025-04-14",
"llm.invocation_parameters": {
"id": "resp_0f00ca5b7e22bb4c0068db9d0110d8819da676bd1f2dd03a40",
"created_at": 1759223041,
"instructions": "You are a helpful agent.",
"metadata": {},
"model": "gpt-4.1-2025-04-14",
"parallel_tool_calls": true,
"temperature": 1,
"tool_choice": "auto",
"top_p": 1,
"background": false,
"reasoning": {},
"service_tier": "default",
"text": {
"format": {
"type": "text"
},
"verbosity": "medium"
},
"top_logprobs": 0,
"truncation": "disabled",
"billing": {
"payer": "developer"
},
"store": true
},
"input.mime_type": "application/json",
"input.value": [
{
"content": "What's the weather in Tokyo?",
"role": "user"
},
{
"arguments": {
"city": "Tokyo"
},
"call_id": "call_Kud0j0DxWSmLzv9W5m6qVqXn",
"name": "get_weather",
"type": "function_call",
"id": "fc_0f00ca5b7e22bb4c0068db9d00d258819d987f4b9b63189e76",
"status": "completed"
},
{
"call_id": "call_Kud0j0DxWSmLzv9W5m6qVqXn",
"output": "The weather in Tokyo is sunny.",
"type": "function_call_output"
}
],
"llm.input_messages.1.message.role": "user",
"llm.input_messages.1.message.content": "What's the weather in Tokyo?",
"llm.input_messages.2.message.role": "assistant",
"llm.input_messages.2.message.tool_calls.0.tool_call.id": "call_Kud0j0DxWSmLzv9W5m6qVqXn",
"llm.input_messages.2.message.tool_calls.0.tool_call.function.name": "get_weather",
"llm.input_messages.2.message.tool_calls.0.tool_call.function.arguments": {
"city": "Tokyo"
},
"llm.input_messages.3.message.role": "tool",
"llm.input_messages.3.message.tool_call_id": "call_Kud0j0DxWSmLzv9W5m6qVqXn",
"llm.input_messages.3.message.content": "The weather in Tokyo is sunny.",
"openinference.span.kind": "LLM"
},
"resourceAttributes": {
"telemetry.sdk.language": "python",
"telemetry.sdk.name": "opentelemetry",
"telemetry.sdk.version": "1.37.0",
"service.name": "unknown_service"
},
"scope": {
"name": "openinference.instrumentation.openai_agents",
"version": "1.3.0",
"attributes": {}
}
},
"level": "DEFAULT",
"statusMessage": null,
"version": null,
"modelParameters": {
"id": "resp_0f00ca5b7e22bb4c0068db9d0110d8819da676bd1f2dd03a40",
"created_at": 1759223041,
"instructions": "You are a helpful agent.",
"metadata": "{}",
"model": "gpt-4.1-2025-04-14",
"parallel_tool_calls": "true",
"temperature": 1,
"tool_choice": "auto",
"top_p": 1,
"background": "false",
"reasoning": "{}",
"service_tier": "default",
"text": "{\"format\":{\"type\":\"text\"},\"verbosity\":\"medium\"}",
"top_logprobs": 0,
"truncation": "disabled",
"billing": "{\"payer\":\"developer\"}",
"store": "true"
},
"completionStartTime": null,
"promptId": null,
"createdAt": "2025-09-30T09:04:07.382Z",
"updatedAt": "2025-09-30T09:04:07.382Z",
"usageDetails": {
"output": 29,
"input": 85,
"total": 114,
"prompt_details.cache_read": 0,
"completion_details.reasoning": 0
},
"costDetails": {
"output": 0.000232,
"input": 0.00017,
"total": 0.000402
},
"providedCostDetails": {},
"model": "gpt-4.1-2025-04-14",
"internalModelId": "cm7qahw732891bpmzy45r3x70",
"promptName": null,
"promptVersion": null,
"latency": 1150,
"timeToFirstToken": null,
"inputCost": 0.00017,
"outputCost": 0.000232,
"totalCost": 0.000402,
"inputUsage": 85,
"outputUsage": 29,
"totalUsage": 114,
"input": [
{
"content": "What's the weather in Tokyo?",
"role": "user"
},
{
"arguments": {
"city": "Tokyo"
},
"call_id": "call_Kud0j0DxWSmLzv9W5m6qVqXn",
"name": "get_weather",
"type": "function_call",
"id": "fc_0f00ca5b7e22bb4c0068db9d00d258819d987f4b9b63189e76",
"status": "completed"
},
{
"call_id": "call_Kud0j0DxWSmLzv9W5m6qVqXn",
"output": "The weather in Tokyo is sunny.",
"type": "function_call_output"
}
],
"output": {
"id": "resp_0f00ca5b7e22bb4c0068db9d0110d8819da676bd1f2dd03a40",
"created_at": 1759223041,
"error": null,
"incomplete_details": null,
"instructions": "You are a helpful agent.",
"metadata": {},
"model": "gpt-4.1-2025-04-14",
"object": "response",
"output": [
{
"id": "msg_0f00ca5b7e22bb4c0068db9d019a78819d9fe1e4d3b6c96b68",
"content": [
{
"annotations": [],
"text": "The weather in Tokyo is currently sunny. If you need more details like temperature or forecast for the upcoming days, just let me know!",
"type": "output_text",
"logprobs": []
}
],
"role": "assistant",
"status": "completed",
"type": "message"
}
],
"parallel_tool_calls": true,
"temperature": 1,
"tool_choice": "auto",
"tools": [
{
"name": "get_weather",
"parameters": {
"properties": {
"city": {
"title": "City",
"type": "string"
}
},
"required": ["city"],
"title": "get_weather_args",
"type": "object",
"additionalProperties": false
},
"strict": true,
"type": "function",
"description": null
}
],
"top_p": 1,
"background": false,
"conversation": null,
"max_output_tokens": null,
"max_tool_calls": null,
"previous_response_id": null,
"prompt": null,
"prompt_cache_key": null,
"reasoning": {
"effort": null,
"generate_summary": null,
"summary": null
},
"safety_identifier": null,
"service_tier": "default",
"status": "completed",
"text": {
"format": {
"type": "text"
},
"verbosity": "medium"
},
"top_logprobs": 0,
"truncation": "disabled",
"usage": {
"input_tokens": 85,
"input_tokens_details": {
"cached_tokens": 0
},
"output_tokens": 29,
"output_tokens_details": {
"reasoning_tokens": 0
},
"total_tokens": 114
},
"user": null,
"billing": {
"payer": "developer"
},
"store": true
}
},
{
"id": "98870087af69bf06",
"traceId": "fee618f96dc31e0ca38b2f7b26eb8b29",
"projectId": "cloramnkj0002jz088vzn1ja4",
"type": "GENERATION",
"environment": "default",
"parentObservationId": "d950b668796240b5",
"startTime": "2025-09-30T09:03:59.963Z",
"endTime": "2025-09-30T09:04:00.999Z",
"name": "response",
"metadata": {
"attributes": {
"llm.system": "openai",
"output.mime_type": "application/json",
"output.value": {
"id": "resp_0f00ca5b7e22bb4c0068db9d000be4819d824c447c54b65bc1",
"created_at": 1759223040,
"error": null,
"incomplete_details": null,
"instructions": "You are a helpful agent.",
"metadata": {},
"model": "gpt-4.1-2025-04-14",
"object": "response",
"output": [
{
"arguments": {
"city": "Tokyo"
},
"call_id": "call_Kud0j0DxWSmLzv9W5m6qVqXn",
"name": "get_weather",
"type": "function_call",
"id": "fc_0f00ca5b7e22bb4c0068db9d00d258819d987f4b9b63189e76",
"status": "completed"
}
],
"parallel_tool_calls": true,
"temperature": 1,
"tool_choice": "auto",
"tools": [
{
"name": "get_weather",
"parameters": {
"properties": {
"city": {
"title": "City",
"type": "string"
}
},
"required": ["city"],
"title": "get_weather_args",
"type": "object",
"additionalProperties": false
},
"strict": true,
"type": "function",
"description": null
}
],
"top_p": 1,
"background": false,
"conversation": null,
"max_output_tokens": null,
"max_tool_calls": null,
"previous_response_id": null,
"prompt": null,
"prompt_cache_key": null,
"reasoning": {
"effort": null,
"generate_summary": null,
"summary": null
},
"safety_identifier": null,
"service_tier": "default",
"status": "completed",
"text": {
"format": {
"type": "text"
},
"verbosity": "medium"
},
"top_logprobs": 0,
"truncation": "disabled",
"usage": {
"input_tokens": 55,
"input_tokens_details": {
"cached_tokens": 0
},
"output_tokens": 15,
"output_tokens_details": {
"reasoning_tokens": 0
},
"total_tokens": 70
},
"user": null,
"billing": {
"payer": "developer"
},
"store": true
},
"llm.tools.0.tool.json_schema": {
"type": "function",
"function": {
"name": "get_weather",
"description": null,
"parameters": {
"properties": {
"city": {
"title": "City",
"type": "string"
}
},
"required": ["city"],
"title": "get_weather_args",
"type": "object",
"additionalProperties": false
},
"strict": true
}
},
"llm.token_count.completion": "15",
"llm.token_count.prompt": "55",
"llm.token_count.total": "70",
"llm.token_count.prompt_details.cache_read": "0",
"llm.token_count.completion_details.reasoning": "0",
"llm.output_messages.0.message.role": "assistant",
"llm.output_messages.0.message.tool_calls.0.tool_call.id": "call_Kud0j0DxWSmLzv9W5m6qVqXn",
"llm.output_messages.0.message.tool_calls.0.tool_call.function.name": "get_weather",
"llm.output_messages.0.message.tool_calls.0.tool_call.function.arguments": {
"city": "Tokyo"
},
"llm.input_messages.0.message.role": "system",
"llm.input_messages.0.message.content": "You are a helpful agent.",
"llm.model_name": "gpt-4.1-2025-04-14",
"llm.invocation_parameters": {
"id": "resp_0f00ca5b7e22bb4c0068db9d000be4819d824c447c54b65bc1",
"created_at": 1759223040,
"instructions": "You are a helpful agent.",
"metadata": {},
"model": "gpt-4.1-2025-04-14",
"parallel_tool_calls": true,
"temperature": 1,
"tool_choice": "auto",
"top_p": 1,
"background": false,
"reasoning": {},
"service_tier": "default",
"text": {
"format": {
"type": "text"
},
"verbosity": "medium"
},
"top_logprobs": 0,
"truncation": "disabled",
"billing": {
"payer": "developer"
},
"store": true
},
"input.mime_type": "application/json",
"input.value": [
{
"content": "What's the weather in Tokyo?",
"role": "user"
}
],
"llm.input_messages.1.message.role": "user",
"llm.input_messages.1.message.content": "What's the weather in Tokyo?",
"openinference.span.kind": "LLM"
},
"resourceAttributes": {
"telemetry.sdk.language": "python",
"telemetry.sdk.name": "opentelemetry",
"telemetry.sdk.version": "1.37.0",
"service.name": "unknown_service"
},
"scope": {
"name": "openinference.instrumentation.openai_agents",
"version": "1.3.0",
"attributes": {}
}
},
"level": "DEFAULT",
"statusMessage": null,
"version": null,
"modelParameters": {
"id": "resp_0f00ca5b7e22bb4c0068db9d000be4819d824c447c54b65bc1",
"created_at": 1759223040,
"instructions": "You are a helpful agent.",
"metadata": "{}",
"model": "gpt-4.1-2025-04-14",
"parallel_tool_calls": "true",
"temperature": 1,
"tool_choice": "auto",
"top_p": 1,
"background": "false",
"reasoning": "{}",
"service_tier": "default",
"text": "{\"format\":{\"type\":\"text\"},\"verbosity\":\"medium\"}",
"top_logprobs": 0,
"truncation": "disabled",
"billing": "{\"payer\":\"developer\"}",
"store": "true"
},
"completionStartTime": null,
"promptId": null,
"createdAt": "2025-09-30T09:04:02.217Z",
"updatedAt": "2025-09-30T09:04:02.217Z",
"usageDetails": {
"output": 15,
"input": 55,
"total": 70,
"prompt_details.cache_read": 0,
"completion_details.reasoning": 0
},
"costDetails": {
"output": 0.00012,
"input": 0.00011,
"total": 0.00023
},
"providedCostDetails": {},
"model": "gpt-4.1-2025-04-14",
"internalModelId": "cm7qahw732891bpmzy45r3x70",
"promptName": null,
"promptVersion": null,
"latency": 1036,
"timeToFirstToken": null,
"inputCost": 0.00011,
"outputCost": 0.00012,
"totalCost": 0.00023,
"inputUsage": 55,
"outputUsage": 15,
"totalUsage": 70,
"input": [
{
"content": "What's the weather in Tokyo?",
"role": "user"
}
],
"output": {
"id": "resp_0f00ca5b7e22bb4c0068db9d000be4819d824c447c54b65bc1",
"created_at": 1759223040,
"error": null,
"incomplete_details": null,
"instructions": "You are a helpful agent.",
"metadata": {},
"model": "gpt-4.1-2025-04-14",
"object": "response",
"output": [
{
"arguments": {
"city": "Tokyo"
},
"call_id": "call_Kud0j0DxWSmLzv9W5m6qVqXn",
"name": "get_weather",
"type": "function_call",
"id": "fc_0f00ca5b7e22bb4c0068db9d00d258819d987f4b9b63189e76",
"status": "completed"
}
],
"parallel_tool_calls": true,
"temperature": 1,
"tool_choice": "auto",
"tools": [
{
"name": "get_weather",
"parameters": {
"properties": {
"city": {
"title": "City",
"type": "string"
}
},
"required": ["city"],
"title": "get_weather_args",
"type": "object",
"additionalProperties": false
},
"strict": true,
"type": "function",
"description": null
}
],
"top_p": 1,
"background": false,
"conversation": null,
"max_output_tokens": null,
"max_tool_calls": null,
"previous_response_id": null,
"prompt": null,
"prompt_cache_key": null,
"reasoning": {
"effort": null,
"generate_summary": null,
"summary": null
},
"safety_identifier": null,
"service_tier": "default",
"status": "completed",
"text": {
"format": {
"type": "text"
},
"verbosity": "medium"
},
"top_logprobs": 0,
"truncation": "disabled",
"usage": {
"input_tokens": 55,
"input_tokens_details": {
"cached_tokens": 0
},
"output_tokens": 15,
"output_tokens_details": {
"reasoning_tokens": 0
},
"total_tokens": 70
},
"user": null,
"billing": {
"payer": "developer"
},
"store": true
}
},
{
"id": "5b99c3e3411ed17b",
"traceId": "fee618f96dc31e0ca38b2f7b26eb8b29",
"projectId": "cloramnkj0002jz088vzn1ja4",
"type": "TOOL",
"environment": "default",
"parentObservationId": "d950b668796240b5",
"startTime": "2025-09-30T09:04:01.000Z",
"endTime": "2025-09-30T09:04:01.000Z",
"name": "get_weather",
"metadata": {
"attributes": {
"llm.system": "openai",
"tool.name": "get_weather",
"input.value": {
"city": "Tokyo"
},
"input.mime_type": "application/json",
"output.value": "The weather in Tokyo is sunny.",
"openinference.span.kind": "TOOL"
},
"resourceAttributes": {
"telemetry.sdk.language": "python",
"telemetry.sdk.name": "opentelemetry",
"telemetry.sdk.version": "1.37.0",
"service.name": "unknown_service"
},
"scope": {
"name": "openinference.instrumentation.openai_agents",
"version": "1.3.0",
"attributes": {}
}
},
"level": "DEFAULT",
"statusMessage": null,
"version": null,
"modelParameters": {},
"completionStartTime": null,
"promptId": null,
"createdAt": "2025-09-30T09:04:02.217Z",
"updatedAt": "2025-09-30T09:04:02.217Z",
"usageDetails": {},
"costDetails": {},
"providedCostDetails": {},
"model": null,
"internalModelId": null,
"promptName": null,
"promptVersion": null,
"latency": 0,
"timeToFirstToken": null,
"inputCost": null,
"outputCost": null,
"totalCost": 0,
"inputUsage": 0,
"outputUsage": 0,
"totalUsage": 0,
"input": {
"city": "Tokyo"
},
"output": "The weather in Tokyo is sunny."
}
]
}
@@ -0,0 +1,338 @@
{
"trace": {
"id": "b3b7b128-5664-4f42-9fab-31999da9e2f1",
"projectId": "cloramnkj0002jz088vzn1ja4",
"name": "run_math_tutor",
"timestamp": "2024-05-29T13:46:10.728Z",
"environment": "default",
"tags": [],
"bookmarked": false,
"release": null,
"version": null,
"userId": null,
"sessionId": null,
"public": true,
"input": "\"{\\\"args\\\":[\\\"I need to solve the equation `3x + 11 = 14`. Can you help me?\\\"],\\\"kwargs\\\":{}}\"",
"output": "\"\\\"Sure, first subtract 11 from both sides to get `3x = 3`, then divide both sides by 3 to solve for `x`. The solution is `x = 1`.\\\"\"",
"metadata": "{}",
"createdAt": "2024-05-29T13:46:11.304Z",
"updatedAt": "2024-05-29T13:46:34.171Z",
"latency": 9.236
},
"observations": [
{
"id": "0d8f5427-c91a-44c8-86c5-e12d806edd57",
"traceId": "b3b7b128-5664-4f42-9fab-31999da9e2f1",
"projectId": "cloramnkj0002jz088vzn1ja4",
"type": "GENERATION",
"environment": "default",
"parentObservationId": "0d6c73e1-be16-436d-a905-b4108912fa93",
"startTime": "2024-05-29T13:46:19.963Z",
"endTime": null,
"name": "",
"level": "DEFAULT",
"statusMessage": null,
"version": null,
"modelParameters": null,
"completionStartTime": null,
"promptId": null,
"createdAt": "2024-05-29T13:46:20.714Z",
"updatedAt": "2024-05-29T13:46:20.714Z",
"usageDetails": {
"input": 49,
"output": 40,
"total": 89
},
"costDetails": {
"input": 0.00147,
"output": 0.0024,
"total": 0.00387
},
"providedCostDetails": {
"input": 0,
"output": 0,
"total": 0
},
"model": "gpt-4",
"internalModelId": "clrntkjgy000f08jx79v9g1xj",
"promptName": null,
"promptVersion": null,
"latency": null,
"timeToFirstToken": null,
"inputCost": 0.00147,
"outputCost": 0.0024,
"totalCost": 0.00387,
"inputUsage": 49,
"outputUsage": 40,
"totalUsage": 89,
"input": [
{
"role": "assistant",
"content": "I am a math tutor that likes to help math students, how can I help?"
},
{
"role": "user",
"content": "I need to solve the equation `3x + 11 = 14`. Can you help me?"
}
],
"output": "Sure, first subtract 11 from both sides to get `3x = 3`, then divide both sides by 3 to solve for `x`. The solution is `x = 1`.",
"metadata": {}
},
{
"id": "0d6c73e1-be16-436d-a905-b4108912fa93",
"traceId": "b3b7b128-5664-4f42-9fab-31999da9e2f1",
"projectId": "cloramnkj0002jz088vzn1ja4",
"type": "SPAN",
"environment": "default",
"parentObservationId": null,
"startTime": "2024-05-29T13:46:17.971Z",
"endTime": "2024-05-29T13:46:19.964Z",
"name": "get_response",
"metadata": "{}",
"level": "DEFAULT",
"statusMessage": null,
"version": null,
"modelParameters": null,
"completionStartTime": null,
"promptId": null,
"createdAt": "2024-05-29T13:46:18.345Z",
"updatedAt": "2024-05-29T13:46:20.796Z",
"usageDetails": {
"input": 0,
"output": 0,
"total": 0
},
"costDetails": {
"input": 0,
"output": 0,
"total": 0
},
"providedCostDetails": {
"input": 0,
"output": 0,
"total": 0
},
"model": null,
"internalModelId": null,
"promptName": null,
"promptVersion": null,
"latency": 1993,
"timeToFirstToken": null,
"inputCost": 0,
"outputCost": 0,
"totalCost": 0,
"inputUsage": 0,
"outputUsage": 0,
"totalUsage": 0,
"input": {
"args": [
"thread_fbFB5caAj0sEpE1c5ICMDs7I",
"run_RsJmiMTrgkBzb3FvyCvRoqpd"
],
"kwargs": {}
},
"output": [
"Sure, first subtract 11 from both sides to get `3x = 3`, then divide both sides by 3 to solve for `x`. The solution is `x = 1`.",
{
"id": "run_AherTwpS29w0GC8zlKDBhhlZ",
"model": "gpt-4",
"tools": [],
"top_p": 1,
"usage": null,
"object": "thread.run",
"status": "queued",
"metadata": {},
"failed_at": null,
"thread_id": "thread_DhsY59BBhtKWN8YYq44cULrH",
"created_at": 1716990330,
"expires_at": 1716990930,
"last_error": null,
"started_at": null,
"temperature": 1,
"tool_choice": "auto",
"assistant_id": "asst_tjgFtRASnPoQaWpks1ezPfSp",
"cancelled_at": null,
"completed_at": null,
"instructions": "You are a personal math tutor. Answer questions briefly, in a sentence or less.",
"tool_resources": {},
"required_action": null,
"response_format": "auto",
"max_prompt_tokens": null,
"incomplete_details": null,
"truncation_strategy": {
"type": "auto",
"last_messages": null
},
"max_completion_tokens": null
}
]
},
{
"id": "e584f95b-e8ef-4d43-bcb1-fd296f57f94b",
"traceId": "b3b7b128-5664-4f42-9fab-31999da9e2f1",
"projectId": "cloramnkj0002jz088vzn1ja4",
"type": "SPAN",
"environment": "default",
"parentObservationId": null,
"startTime": "2024-05-29T13:46:11.057Z",
"endTime": "2024-05-29T13:46:12.959Z",
"name": "run_assistant",
"level": "DEFAULT",
"statusMessage": null,
"version": null,
"modelParameters": null,
"completionStartTime": null,
"promptId": null,
"createdAt": "2024-05-29T13:46:11.480Z",
"updatedAt": "2024-05-29T13:46:13.161Z",
"usageDetails": {
"input": 0,
"output": 0,
"total": 0
},
"costDetails": {
"input": 0,
"output": 0,
"total": 0
},
"providedCostDetails": {
"input": 0,
"output": 0,
"total": 0
},
"model": null,
"internalModelId": null,
"promptName": null,
"promptVersion": null,
"latency": 1902,
"timeToFirstToken": null,
"inputCost": 0,
"outputCost": 0,
"totalCost": 0,
"inputUsage": 0,
"outputUsage": 0,
"totalUsage": 0,
"input": {
"args": [
"asst_01wKwjx3rT7CNprPxzE8azMl",
"I need to solve the equation `3x + 11 = 14`. Can you help me?"
],
"kwargs": {}
},
"output": [
{
"id": "run_RsJmiMTrgkBzb3FvyCvRoqpd",
"model": "gpt-4",
"tools": [],
"top_p": 1,
"usage": null,
"object": "thread.run",
"status": "queued",
"metadata": {},
"failed_at": null,
"thread_id": "thread_fbFB5caAj0sEpE1c5ICMDs7I",
"created_at": 1716990372,
"expires_at": 1716990972,
"last_error": null,
"started_at": null,
"temperature": 1,
"tool_choice": "auto",
"assistant_id": "asst_01wKwjx3rT7CNprPxzE8azMl",
"cancelled_at": null,
"completed_at": null,
"instructions": "You are a personal math tutor. Answer questions briefly, in a sentence or less.",
"tool_resources": {},
"required_action": null,
"response_format": "auto",
"max_prompt_tokens": null,
"incomplete_details": null,
"truncation_strategy": {
"type": "auto",
"last_messages": null
},
"max_completion_tokens": null
},
{
"id": "thread_fbFB5caAj0sEpE1c5ICMDs7I",
"object": "thread",
"metadata": {},
"created_at": 1716990371,
"tool_resources": {
"file_search": null,
"code_interpreter": null
}
}
],
"metadata": {}
},
{
"id": "be59b862-98dd-4b0e-b92e-c150b9777c2f",
"traceId": "b3b7b128-5664-4f42-9fab-31999da9e2f1",
"projectId": "cloramnkj0002jz088vzn1ja4",
"type": "SPAN",
"environment": "default",
"parentObservationId": null,
"startTime": "2024-05-29T13:46:10.728Z",
"endTime": "2024-05-29T13:46:11.056Z",
"name": "create_assistant",
"level": "DEFAULT",
"statusMessage": null,
"version": null,
"modelParameters": null,
"completionStartTime": null,
"promptId": null,
"createdAt": "2024-05-29T13:46:11.350Z",
"updatedAt": "2024-05-29T13:46:11.405Z",
"usageDetails": {
"input": 0,
"output": 0,
"total": 0
},
"costDetails": {
"input": 0,
"output": 0,
"total": 0
},
"providedCostDetails": {
"input": 0,
"output": 0,
"total": 0
},
"model": null,
"internalModelId": null,
"promptName": null,
"promptVersion": null,
"latency": 328,
"timeToFirstToken": null,
"inputCost": 0,
"outputCost": 0,
"totalCost": 0,
"inputUsage": 0,
"outputUsage": 0,
"totalUsage": 0,
"input": {
"args": [],
"kwargs": {}
},
"output": {
"id": "asst_01wKwjx3rT7CNprPxzE8azMl",
"name": "Math Tutor",
"model": "gpt-4",
"tools": [],
"top_p": 1,
"object": "assistant",
"metadata": {},
"created_at": 1716990370,
"description": null,
"temperature": 1,
"instructions": "You are a personal math tutor. Answer questions briefly, in a sentence or less.",
"tool_resources": {
"file_search": null,
"code_interpreter": null
},
"response_format": "auto"
},
"metadata": {}
}
]
}
@@ -0,0 +1,312 @@
{
"trace": {
"id": "25f4bdeebaab60e6e1bee7e8469554bc",
"projectId": "cloramnkj0002jz088vzn1ja4",
"name": "pydantic-ai-qa-trace",
"timestamp": "2025-06-06T14:40:28.562Z",
"environment": "default",
"tags": ["dev", "pydantic-ai"],
"bookmarked": false,
"release": null,
"version": "1.0.0",
"userId": "user_123",
"sessionId": "session_abc",
"public": true,
"input": "\"What is Langfuse?\"",
"output": "\"Langfuse is a tool designed for observability and monitoring of applications that utilize large language models (LLMs). It provides features to trace, log, and visualize requests, helping developers understand the behavior of their LLM-powered applications more effectively. This can be particularly useful for debugging, performance optimization, and ensuring reliable operation of systems that depend on LLMs. Langfuse is designed to integrate easily with existing infrastructure, supporting both self-hosted implementations and cloud-based solutions.\"",
"metadata": "{\"email\":\"user@langfuse.com\",\"resourceAttributes\":{\"telemetry.sdk.language\":\"python\",\"telemetry.sdk.name\":\"opentelemetry\",\"telemetry.sdk.version\":\"1.33.1\",\"service.name\":\"unknown_service\"},\"scope\":{\"name\":\"langfuse-sdk\",\"version\":\"3.0.0\",\"attributes\":{\"public_key\":\"pk-lf-5855d85e-3943-497e-bd10-f50ad414bcba\"}}}",
"createdAt": "2025-06-06T14:40:34.000Z",
"updatedAt": "2025-06-06T14:40:33.635Z",
"latency": 4.795
},
"observations": [
{
"id": "538fed87d11686d3",
"traceId": "25f4bdeebaab60e6e1bee7e8469554bc",
"projectId": "cloramnkj0002jz088vzn1ja4",
"type": "SPAN",
"environment": "default",
"parentObservationId": null,
"startTime": "2025-06-06T14:40:28.562Z",
"endTime": "2025-06-06T14:40:33.357Z",
"name": "pydantic-ai-qa-trace",
"level": "DEFAULT",
"statusMessage": null,
"version": "1.0.0",
"modelParameters": null,
"completionStartTime": null,
"promptId": null,
"createdAt": "2025-06-06T14:40:33.595Z",
"updatedAt": "2025-06-06T14:40:33.630Z",
"usageDetails": {},
"costDetails": {},
"providedCostDetails": {},
"model": null,
"internalModelId": null,
"promptName": null,
"promptVersion": null,
"latency": 4795,
"timeToFirstToken": null,
"inputCost": null,
"outputCost": null,
"totalCost": 0,
"inputUsage": 0,
"outputUsage": 0,
"totalUsage": 0,
"input": null,
"output": null,
"metadata": {
"resourceAttributes": {
"telemetry.sdk.language": "python",
"telemetry.sdk.name": "opentelemetry",
"telemetry.sdk.version": "1.33.1",
"service.name": "unknown_service"
},
"scope": {
"name": "langfuse-sdk",
"version": "3.0.0",
"attributes": {
"public_key": "pk-lf-5855d85e-3943-497e-bd10-f50ad414bcba"
}
}
}
},
{
"id": "0a4fac2553f22f2e",
"traceId": "25f4bdeebaab60e6e1bee7e8469554bc",
"projectId": "cloramnkj0002jz088vzn1ja4",
"type": "SPAN",
"environment": "default",
"parentObservationId": "538fed87d11686d3",
"startTime": "2025-06-06T14:40:28.562Z",
"endTime": "2025-06-06T14:40:33.357Z",
"name": "qa_agent run",
"level": "DEFAULT",
"statusMessage": null,
"version": null,
"modelParameters": null,
"completionStartTime": null,
"promptId": null,
"createdAt": "2025-06-06T14:40:33.596Z",
"updatedAt": "2025-06-06T14:40:33.641Z",
"usageDetails": {},
"costDetails": {},
"providedCostDetails": {},
"model": null,
"internalModelId": null,
"promptName": null,
"promptVersion": null,
"latency": 4795,
"timeToFirstToken": null,
"inputCost": null,
"outputCost": null,
"totalCost": 0,
"inputUsage": 0,
"outputUsage": 0,
"totalUsage": 0,
"input": null,
"output": [
{
"content": "You are a helpful assistant that answers questions clearly and concisely.",
"role": "system",
"gen_ai.message.index": 0,
"event.name": "gen_ai.system.message"
},
{
"content": "What is Langfuse?",
"role": "user",
"gen_ai.message.index": 0,
"event.name": "gen_ai.user.message"
},
{
"role": "assistant",
"content": "Langfuse is a tool designed for observability and monitoring of applications that utilize large language models (LLMs). It provides features to trace, log, and visualize requests, helping developers understand the behavior of their LLM-powered applications more effectively. This can be particularly useful for debugging, performance optimization, and ensuring reliable operation of systems that depend on LLMs. Langfuse is designed to integrate easily with existing infrastructure, supporting both self-hosted implementations and cloud-based solutions.",
"gen_ai.message.index": 1,
"event.name": "gen_ai.assistant.message"
}
],
"metadata": {
"attributes": {
"model_name": "gpt-4o",
"agent_name": "qa_agent",
"logfire.msg": "qa_agent run",
"final_result": "Langfuse is a tool designed for observability and monitoring of applications that utilize large language models (LLMs). It provides features to trace, log, and visualize requests, helping developers understand the behavior of their LLM-powered applications more effectively. This can be particularly useful for debugging, performance optimization, and ensuring reliable operation of systems that depend on LLMs. Langfuse is designed to integrate easily with existing infrastructure, supporting both self-hosted implementations and cloud-based solutions.",
"gen_ai.usage.input_tokens": "31",
"gen_ai.usage.output_tokens": "96",
"all_messages_events": [
{
"content": "You are a helpful assistant that answers questions clearly and concisely.",
"role": "system",
"gen_ai.message.index": 0,
"event.name": "gen_ai.system.message"
},
{
"content": "What is Langfuse?",
"role": "user",
"gen_ai.message.index": 0,
"event.name": "gen_ai.user.message"
},
{
"role": "assistant",
"content": "Langfuse is a tool designed for observability and monitoring of applications that utilize large language models (LLMs). It provides features to trace, log, and visualize requests, helping developers understand the behavior of their LLM-powered applications more effectively. This can be particularly useful for debugging, performance optimization, and ensuring reliable operation of systems that depend on LLMs. Langfuse is designed to integrate easily with existing infrastructure, supporting both self-hosted implementations and cloud-based solutions.",
"gen_ai.message.index": 1,
"event.name": "gen_ai.assistant.message"
}
],
"logfire.json_schema": {
"type": "object",
"properties": {
"all_messages_events": {
"type": "array"
},
"final_result": {
"type": "object"
}
}
}
},
"resourceAttributes": {
"telemetry.sdk.language": "python",
"telemetry.sdk.name": "opentelemetry",
"telemetry.sdk.version": "1.33.1",
"service.name": "unknown_service"
},
"scope": {
"name": "pydantic-ai",
"version": "0.2.15",
"attributes": {}
}
}
},
{
"id": "5cb389a3bdd21a6e",
"traceId": "25f4bdeebaab60e6e1bee7e8469554bc",
"projectId": "cloramnkj0002jz088vzn1ja4",
"type": "GENERATION",
"environment": "default",
"parentObservationId": "0a4fac2553f22f2e",
"startTime": "2025-06-06T14:40:28.562Z",
"endTime": "2025-06-06T14:40:33.356Z",
"name": "chat gpt-4o",
"level": "DEFAULT",
"statusMessage": null,
"version": null,
"modelParameters": {},
"completionStartTime": null,
"promptId": null,
"createdAt": "2025-06-06T14:40:33.440Z",
"updatedAt": "2025-06-06T14:40:33.478Z",
"usageDetails": {
"input": 31,
"output": 96,
"total": 127
},
"costDetails": {
"input": 0.0000775,
"output": 0.00096,
"total": 0.0010375
},
"providedCostDetails": {},
"model": "gpt-4o",
"internalModelId": "b9854a5c92dc496b997d99d20",
"promptName": null,
"promptVersion": null,
"latency": 4794,
"timeToFirstToken": null,
"inputCost": 0.0000775,
"outputCost": 0.00096,
"totalCost": 0.0010375,
"inputUsage": 31,
"outputUsage": 96,
"totalUsage": 127,
"input": [
{
"content": "You are a helpful assistant that answers questions clearly and concisely.",
"role": "system",
"gen_ai.system": "openai",
"gen_ai.message.index": 0,
"event.name": "gen_ai.system.message"
},
{
"content": "What is Langfuse?",
"role": "user",
"gen_ai.system": "openai",
"gen_ai.message.index": 0,
"event.name": "gen_ai.user.message"
}
],
"output": {
"index": 0,
"message": {
"role": "assistant",
"content": "Langfuse is a tool designed for observability and monitoring of applications that utilize large language models (LLMs). It provides features to trace, log, and visualize requests, helping developers understand the behavior of their LLM-powered applications more effectively. This can be particularly useful for debugging, performance optimization, and ensuring reliable operation of systems that depend on LLMs. Langfuse is designed to integrate easily with existing infrastructure, supporting both self-hosted implementations and cloud-based solutions."
},
"gen_ai.system": "openai",
"event.name": "gen_ai.choice"
},
"metadata": {
"attributes": {
"gen_ai.operation.name": "chat",
"gen_ai.system": "openai",
"gen_ai.request.model": "gpt-4o",
"server.address": "api.openai.com",
"model_request_parameters": {
"function_tools": [],
"allow_text_output": true,
"output_tools": []
},
"gen_ai.usage.input_tokens": "31",
"gen_ai.usage.output_tokens": "96",
"gen_ai.response.model": "gpt-4o-2024-08-06",
"events": [
{
"content": "You are a helpful assistant that answers questions clearly and concisely.",
"role": "system",
"gen_ai.system": "openai",
"gen_ai.message.index": 0,
"event.name": "gen_ai.system.message"
},
{
"content": "What is Langfuse?",
"role": "user",
"gen_ai.system": "openai",
"gen_ai.message.index": 0,
"event.name": "gen_ai.user.message"
},
{
"index": 0,
"message": {
"role": "assistant",
"content": "Langfuse is a tool designed for observability and monitoring of applications that utilize large language models (LLMs). It provides features to trace, log, and visualize requests, helping developers understand the behavior of their LLM-powered applications more effectively. This can be particularly useful for debugging, performance optimization, and ensuring reliable operation of systems that depend on LLMs. Langfuse is designed to integrate easily with existing infrastructure, supporting both self-hosted implementations and cloud-based solutions."
},
"gen_ai.system": "openai",
"event.name": "gen_ai.choice"
}
],
"logfire.json_schema": {
"type": "object",
"properties": {
"events": {
"type": "array"
},
"model_request_parameters": {
"type": "object"
}
}
}
},
"resourceAttributes": {
"telemetry.sdk.language": "python",
"telemetry.sdk.name": "opentelemetry",
"telemetry.sdk.version": "1.33.1",
"service.name": "unknown_service"
},
"scope": {
"name": "pydantic-ai",
"version": "0.2.15",
"attributes": {}
}
}
}
]
}
@@ -400,6 +400,177 @@ export const SEED_CHAT_ML_PROMPTS = [
];
export const SEED_PROMPT_VERSIONS = [
{
createdBy: "user-1",
type: "chat",
prompt: [
{
role: "system",
content: `## Role
You are a Langfuse filter generator. Your sole function is to parse user queries about AI traces and output the corresponding filter array in JSON format. Map natural language to appropriate column names, operators, and values.
## Available columns and their types:
- bookmarked (boolean): Starred/bookmarked traces
- id (string): Trace ID
- name (stringOptions): Trace name
- environment (stringOptions): Environment (dev, prod, etc.)
- timestamp (datetime): When the trace occurred
- userId (string): User identifier
- sessionId (string): Session identifier
- metadata (stringObject): Custom metadata
- version (string): Version identifier
- release (string): Release identifier
- level (stringOptions): Log level - DEBUG, DEFAULT, WARNING, ERROR
- tags (arrayOptions): Custom tags
- inputTokens (number): Input token count
- outputTokens (number): Output token count
- totalTokens (number): Total token count
- tokens (number): Alias for total tokens
- errorCount (number): Count of error-level observations
- warningCount (number): Count of warning-level observations
- defaultCount (number): Count of default-level observations
- debugCount (number): Count of debug-level observations
- scores_avg (numberObject): Numeric evaluation scores
- score_categories (categoryOptions): Categorical evaluation scores
- latency (number): Latency in seconds
- inputCost (number): Input cost in USD
- outputCost (number): Output cost in USD
- totalCost (number): Total cost in USD
## Type-Operator Compatibility Rules
**string type** - Use for text matching operations:
- Operators: "=", "contains", "does not contain", "starts with", "ends with"
- Value: Always a single string
- Example: {"type": "string", "column": "userId", "operator": "contains", "value": "john"}
**stringOptions type** - Use ONLY for selecting from predefined options:
- Operators: "any of", "none of" ONLY
- Value: Always an array of strings
- Use when filtering columns like name, environment, level with multiple possible values
- Example: {"type": "stringOptions", "column": "environment", "operator": "any of", "value": ["dev", "staging"]}
**arrayOptions type** - Use for tag filtering:
- Operators: "any of", "none of", "all of"
- Value: Always an array of strings
- Example: {"type": "arrayOptions", "column": "tags", "operator": "any of", "value": ["important", "bug"]}
**number type** - Use for numeric comparisons:
- Operators: "=", ">", "<", ">=", "<="
- Value: Always a number
- Example: {"type": "number", "column": "latency", "operator": ">", "value": 2.5}
**datetime type** - Use for time-based filtering:
- Operators: ">", "<", ">=", "<="
- Value: Always a date string
- Example: {"type": "datetime", "column": "timestamp", "operator": ">", "value": "2024-01-01T00:00:00Z"}
**boolean type** - Use for true/false values:
- Operators: "=", "<>"
- Value: Always true or false
- Example: {"type": "boolean", "column": "bookmarked", "operator": "=", "value": true}
**stringObject type** - Use for metadata key-value searches:
- Operators: "=", "contains", "does not contain", "starts with", "ends with"
- Value: Always a string
- Requires "key" field for the metadata key
- Example: {"type": "stringObject", "column": "metadata", "key": "userId", "operator": "=", "value": "123"}
**numberObject type** - Use for numeric score searches:
- Operators: "=", ">", "<", ">=", "<="
- Value: Always a number
- Requires "key" field for the score name
- Example: {"type": "numberObject", "column": "scores_avg", "key": "quality", "operator": ">", "value": 0.8}
## Output Format
Please respond ONLY with valid JSON. Do not include any explanation or extra text.
The response should look like this without the leading EOF and trailing EOF.
EOF
{
"filters": [
{
"type": "stringOptions|number|string|categoryOptions",
"value": "value or array",
"column": "exact column name",
"operator": "= | > | < | any of"
}
]
}
EOF
## Intent Parsing Guidelines
**Temporal expressions:**
- "today", "yesterday", "last week" → timestamp filters
- "after 2pm", "before noon", "since Monday" → timestamp with appropriate operators
- Relative times: "last 24 hours", "past 3 days" → calculate from current time
**Performance queries:**
- "slow", "high latency", "taking too long" → latency > threshold
- "expensive", "costly", "high cost" → totalCost > threshold
- "many tokens", "token heavy" → totalTokens > threshold
- "cheap", "fast", "quick" → use < operators
**Error/Quality queries:**
- "errors", "failed", "broken" → level = ERROR or errorCount > 0
- "warnings" → level = WARNING or warningCount > 0
- "successful", "working" → level = DEFAULT or errorCount = 0
**User/Session queries:**
- "user john", "by user", "user ID" → userId filters
- "session abc", "in session" → sessionId filters
**Environment queries:**
- "prod", "production" → environment = production
- "dev", "development", "staging" → environment matching
- "live", "deployed" → typically production environment
**Metadata/Tags:**
- "tagged with", "has tag" → tags contains
- "metadata contains", "custom field" → metadata object queries
**Comparison operators:**
- "more than", "over", "above", "greater" → >
- "less than", "under", "below", "fewer" → <
- "at least", "minimum" → >=
- "at most", "maximum" → <=
- "exactly", "equal to" → =
- "not", "except", "excluding" → not_equals or not_contains
**Text search operations:**
- "contains", "includes", "has" → use "string" type with "contains" operator
- "equals", "is exactly" → use "string" type with "=" operator
- "starts with", "begins with" → use "string" type with "starts with" operator
**Multi-option selections:**
- "environment is dev or staging" → use "stringOptions" type with "any of" operator
- "name is one of X, Y, Z" → use "stringOptions" type with "any of" operator
- "level is ERROR or WARNING" → use "stringOptions" type with "any of" operator
## Current DateTime
This is the current datetime: {{currentDatetime}}
Use it as a reference for datetime based queries when needed.
## Examples
Input: "I want to see all dev traces"
Output: {"filters":[{"type":"stringOptions","value":["development","dev"],"column":"environment","operator":"any of"}]}
Input: "Show traces with version starting with v2"
Output: {"filters":[{"type":"string","value":"v2","column":"version","operator":"starts with"}]}`,
},
{
role: "user",
content: "{{userPrompt}}",
},
],
name: "get-filter-conditions-from-query",
version: 1,
labels: ["production", "latest"],
},
{
createdBy: "user-1",
prompt: "Prompt 4 version 1 content with {{variable}}",
@@ -11,9 +11,8 @@ export const generateDatasetItemId = (
datasetName: string,
itemIndex: number,
projectId: string,
runNumber: number,
) => {
return `dataset-item-${datasetName}-${itemIndex}-${projectId.slice(-8)}-${runNumber}`;
return `dataset-item-${datasetName}-${itemIndex}-${projectId.slice(-8)}-0`;
};
export const generateDatasetRunTraceId = (
@@ -1,16 +1,26 @@
import { FileContent, SeederOptions } from "./types";
import { DataGenerator } from "./data-generators";
import { ClickHouseQueryBuilder } from "./clickhouse-builder";
import { FrameworkTraceLoader } from "./framework-traces/framework-trace-loader";
import { EVAL_TRACE_COUNT, SEED_DATASETS } from "./postgres-seed-constants";
import {
clickhouseClient,
DatasetRunItemRecordInsertType,
logger,
ObservationRecordInsertType,
ScoreRecordInsertType,
TraceRecordInsertType,
} from "../../../src/server";
import path from "path";
import { readFileSync } from "fs";
const DATASET_SCORE_NAMES = ["score-1", "score-2", "score-3"];
const DATASET_RUN_SCORE_NAMES = [
"dataset-run-score-1",
"dataset-run-score-2",
"dataset-run-score-3",
];
/**
* Orchestrates seeding operations across ClickHouse and PostgreSQL.
*
@@ -92,25 +102,26 @@ export class SeederOrchestrator {
logger.info(
`Processing run ${runNumber + 1}/${numberOfRuns} for project ${projectId}`,
);
// const now = Date.now();
const now = Date.now();
const traces: TraceRecordInsertType[] = [];
const observations: ObservationRecordInsertType[] = [];
// const datasetRunItems: DatasetRunItemRecordInsertType[] = [];
const datasetRunItems: DatasetRunItemRecordInsertType[] = [];
const scores: ScoreRecordInsertType[] = [];
for (const seedDataset of SEED_DATASETS) {
for (const [itemIndex, datasetItem] of seedDataset.items.entries()) {
// // Generate dataset run item data
// const datasetRunItem = this.dataGenerator.generateDatasetRunItem(
// {
// datasetName: seedDataset.name,
// itemIndex,
// item: datasetItem,
// runNumber,
// runCreatedAt: now,
// },
// projectId,
// );
// Generate dataset run item data
const datasetRunItem = this.dataGenerator.generateDatasetRunItem(
{
datasetName: seedDataset.name,
itemIndex,
item: datasetItem,
runNumber,
runCreatedAt: now,
},
projectId,
);
// Generate trace data
const trace = this.dataGenerator.generateDatasetTrace(
@@ -135,16 +146,43 @@ export class SeederOrchestrator {
projectId,
);
// Generate score data
const score = this.dataGenerator.generateDatasetScore(
trace,
{
datasetName: seedDataset.name,
itemIndex,
item: datasetItem,
runNumber,
},
projectId,
DATASET_SCORE_NAMES,
);
traces.push(trace);
observations.push(observation);
// datasetRunItems.push(datasetRunItem);
datasetRunItems.push(datasetRunItem);
scores.push(score);
}
// create dataset run level scores
const datasetRunScore = this.dataGenerator.generateDatasetRunScore(
`${seedDataset.name}-${projectId.slice(-8)}`,
{
datasetName: seedDataset.name,
runNumber,
},
projectId,
DATASET_RUN_SCORE_NAMES,
);
scores.push(datasetRunScore);
}
try {
await this.queryBuilder.executeTracesInsert(traces);
await this.queryBuilder.executeObservationsInsert(observations);
// await this.queryBuilder.executeDatasetRunItemsInsert(datasetRunItems);
await this.queryBuilder.executeDatasetRunItemsInsert(datasetRunItems);
await this.queryBuilder.executeScoresInsert(scores);
} catch (error) {
logger.error(`✗ Insert failed:`, error);
throw error;
@@ -284,6 +322,12 @@ export class SeederOrchestrator {
// Create synthetic data
await this.createSyntheticData(projectIds, opts);
// Create traces for a realistic chat session
await this.createSupportChatSessionTraces(projectIds);
// create traces from real examples for each framework source
await this.createFrameworkTraces(projectIds);
// Log completion statistics (commented out to reduce terminal noise)
await this.logStatistics();
@@ -341,4 +385,57 @@ export class SeederOrchestrator {
}
}
}
async createSupportChatSessionTraces(projectIds: string[]): Promise<void> {
logger.info(
`Creating support chat session data for ${projectIds.length} projects.`,
);
for (const projectId of projectIds) {
logger.info(`Processing support chat session for project ${projectId}`);
// Generate data using the data generator
const { traces, observations, scores } =
this.dataGenerator.generateSupportChatSessionData(projectId);
try {
await this.queryBuilder.executeTracesInsert(traces);
await this.queryBuilder.executeObservationsInsert(observations);
await this.queryBuilder.executeScoresInsert(scores);
} catch (error) {
logger.error(`✗ Support chat session insert failed:`, error);
throw error;
}
}
}
// create traces from real examples for each framework source
// useful for testing rendering
async createFrameworkTraces(projectIds: string[]): Promise<void> {
logger.info(`Creating framework traces for ${projectIds.length} projects.`);
const loader = new FrameworkTraceLoader();
for (const projectId of projectIds) {
logger.info(`Processing framework traces for project ${projectId}`);
const { traces, observations, scores } =
loader.loadTracesForProject(projectId);
try {
if (traces.length > 0) {
await this.queryBuilder.executeTracesInsert(traces);
}
if (observations.length > 0) {
await this.queryBuilder.executeObservationsInsert(observations);
}
if (scores.length > 0) {
await this.queryBuilder.executeScoresInsert(scores);
}
} catch (error) {
logger.error(`✗ Framework traces insert failed:`, error);
throw error;
}
}
}
}
+1 -1
View File
@@ -51,7 +51,7 @@ export const RequestHeaderSchema = z.object({
export const WebhookActionConfigSchema = z.object({
type: z.literal("WEBHOOK"),
url: z.url(),
url: z.url(), // Basic URL validation only - manual security validation called separately
headers: z.record(z.string(), z.string()).optional(), // deprecated field, use requestHeaders instead
requestHeaders: z.record(z.string(), RequestHeaderSchema).optional(), // might not exist on legacy webhooks
displayHeaders: z.record(z.string(), RequestHeaderSchema).optional(), // might not exist on legacy webhooks
@@ -25,4 +25,14 @@ export const DatasetRunItemSchema = z.object({
datasetItemMetadata: MetadataDomain,
});
export type DatasetRunItemDomain = z.infer<typeof DatasetRunItemSchema>;
// Conditional type for dataset run item domain with optional IO
export type DatasetRunItemDomain<WithIO extends boolean = true> =
WithIO extends true
? z.infer<typeof DatasetRunItemSchema>
: Omit<
z.infer<typeof DatasetRunItemSchema>,
| "datasetRunMetadata"
| "datasetItemInput"
| "datasetItemExpectedOutput"
| "datasetItemMetadata"
>;
+121
View File
@@ -0,0 +1,121 @@
import { z } from "zod/v4";
import { isPresent } from "../utils/typeChecks";
// Category type, used for categorical and boolean configs
export const ScoreConfigCategory = z.object({
label: z.string().min(1),
value: z.number(),
});
// Numeric config fields
export const NumericConfigFields = z.object({
maxValue: z.number().nullish(),
minValue: z.number().nullish(),
dataType: z.literal("NUMERIC"),
categories: z.undefined().nullish(),
});
// Boolean config fields
export const BooleanConfigFields = z.object({
dataType: z.literal("BOOLEAN"),
maxValue: z.number().nullish(),
minValue: z.number().nullish(),
categories: z
.array(ScoreConfigCategory)
.length(2, "Boolean data type must have exactly 2 categories.")
.refine((categories) => {
const expectedCategories = [
{ label: "True", value: 1 },
{ label: "False", value: 0 },
];
return categories.every(
(category, index) =>
category.label === expectedCategories[index].label &&
category.value === expectedCategories[index].value,
);
}),
});
// Category config fields and types
export const validateCategories = (
categories: z.infer<typeof ScoreConfigCategory>[],
ctx: z.RefinementCtx,
) => {
const uniqueNames = new Set<string>();
const uniqueValues = new Set<number>();
for (const category of categories) {
if (uniqueNames.has(category.label)) {
ctx.addIssue({
code: z.ZodIssueCode.custom,
message: `Duplicate category label: ${category.label}, category labels must be unique`,
});
return;
}
uniqueNames.add(category.label);
if (uniqueValues.has(category.value)) {
ctx.addIssue({
code: z.ZodIssueCode.custom,
message: `Duplicate category value: ${category.value}, category values must be unique`,
});
return;
}
uniqueValues.add(category.value);
}
};
export const CategoricalConfigFields = z.object({
maxValue: z.undefined().nullish(),
minValue: z.undefined().nullish(),
dataType: z.literal("CATEGORICAL"),
categories: z.array(ScoreConfigCategory).superRefine(validateCategories),
});
const ScoreConfigBase = z.object({
id: z.string(),
name: z.string().min(1).max(35),
isArchived: z.boolean(),
description: z.string().nullish(),
createdAt: z.coerce.date(),
updatedAt: z.coerce.date(),
projectId: z.string(),
});
export const validateNumericRangeFields = (
data: Pick<ScoreConfigDomain, "maxValue" | "minValue" | "dataType">,
ctx: z.RefinementCtx,
): void | Promise<void> => {
if (data.dataType === "NUMERIC") {
if (
isPresent(data.maxValue) &&
isPresent(data.minValue) &&
data.maxValue <= data.minValue
) {
ctx.addIssue({
code: z.ZodIssueCode.custom,
message: "Maximum value must be greater than Minimum value",
});
}
}
};
export const ScoreConfigSchema = z
.discriminatedUnion("dataType", [
z.object({
...ScoreConfigBase.shape,
...NumericConfigFields.shape,
}),
z.object({
...ScoreConfigBase.shape,
...CategoricalConfigFields.shape,
}),
z.object({
...ScoreConfigBase.shape,
...BooleanConfigFields.shape,
}),
])
.superRefine(validateNumericRangeFields);
export type ScoreConfigDomain = z.infer<typeof ScoreConfigSchema>;
export type ScoreConfigCategoryDomain = z.infer<typeof ScoreConfigCategory>;
+1 -1
View File
@@ -1,6 +1,6 @@
import { ScoreDataType } from "@prisma/client";
import z from "zod/v4";
import { MetadataDomain } from "./traces";
import { ScoreDataType } from "@prisma/client";
export const ScoreSource = {
ANNOTATION: "ANNOTATION",
+25 -42
View File
@@ -38,6 +38,7 @@ const EnvSchema = z.object({
LANGFUSE_CACHE_PROMPT_ENABLED: z.enum(["true", "false"]).default("true"),
LANGFUSE_CACHE_PROMPT_TTL_SECONDS: z.coerce.number().default(300), // 5 minutes
CLICKHOUSE_URL: z.string().url(),
CLICKHOUSE_READ_ONLY_URL: z.string().url().optional(),
CLICKHOUSE_CLUSTER_NAME: z.string().default("default"),
CLICKHOUSE_DB: z.string().default("default"),
CLICKHOUSE_USER: z.string(),
@@ -50,6 +51,10 @@ const EnvSchema = z.object({
.nonnegative()
.default(15_000),
LANGFUSE_INGESTION_QUEUE_SHARD_COUNT: z.coerce.number().positive().default(1),
LANGFUSE_OTEL_INGESTION_QUEUE_SHARD_COUNT: z.coerce
.number()
.positive()
.default(1),
LANGFUSE_TRACE_UPSERT_QUEUE_SHARD_COUNT: z.coerce
.number()
.positive()
@@ -126,48 +131,6 @@ const EnvSchema = z.object({
LANGFUSE_CLICKHOUSE_DELETION_TIMEOUT_MS: z.coerce.number().default(600_000), // 10 minutes
LANGFUSE_CLICKHOUSE_QUERY_MAX_ATTEMPTS: z.coerce.number().default(3), // Maximum attempts for socket hang up errors
LANGFUSE_SKIP_S3_LIST_FOR_OBSERVATIONS_PROJECT_IDS: z.string().optional(),
// Dataset Run Items Migration Environment Variables
LANGFUSE_EXPERIMENT_DATASET_RUN_ITEMS_WRITE_CH: z
.enum(["true", "false"])
.default("true"),
LANGFUSE_EXPERIMENT_DATASET_RUN_ITEMS_READ_CH: z
.enum(["true", "false"])
.default("true"),
LANGFUSE_EXPERIMENT_COMPARE_READ_FROM_AGGREGATING_MERGE_TREES: z
.enum(["true", "false"])
.default("false"),
LANGFUSE_EXPERIMENT_ADD_QUERY_RESULT_TO_SPAN_PROJECT_IDS: z
.string()
.optional()
.transform((s) =>
s ? s.split(",").map((s) => s.toLowerCase().trim()) : [],
),
LANGFUSE_EXPERIMENT_SAMPLING_RATE: z.coerce
.number()
.min(0)
.max(1)
.default(0.1),
LANGFUSE_EXPERIMENT_WHITELISTED_PROJECT_IDS: z
.string()
.optional()
.transform((s) =>
s ? s.split(",").map((s) => s.toLowerCase().trim()) : [],
),
LANGFUSE_EXPERIMENT_RETURN_NEW_RESULT: z
.enum(["true", "false"])
.default("false"),
LANGFUSE_EXPERIMENT_RETURN_NEW_RESULT_SHORT_TERM: z
.enum(["true", "false"])
.default("false"),
LANGFUSE_EXPERIMENT_INSERT_INTO_AGGREGATING_MERGE_TREES: z
.enum(["true", "false"])
.default("false"),
LANGFUSE_EXPERIMENT_WHITELISTED_AMT_TABLES: z
.string()
.optional()
.transform((s) =>
s ? s.split(",").map((s) => s.toLowerCase().trim()) : [],
),
LANGFUSE_INGESTION_PROCESSING_SAMPLED_PROJECTS: z
.string()
.optional()
@@ -200,6 +163,12 @@ const EnvSchema = z.object({
return new Map<string, number>();
}
}),
LANGFUSE_WEBHOOK_WHITELISTED_IPS: z
.string()
.optional()
.transform((s) =>
s ? s.split(",").map((s) => s.toLowerCase().trim()) : [],
),
SLACK_CLIENT_ID: z.string().optional(),
SLACK_CLIENT_SECRET: z.string().optional(),
SLACK_STATE_SECRET: z.string().optional(),
@@ -224,6 +193,20 @@ const EnvSchema = z.object({
.int()
.positive()
.default(600_000), // 10 minutes
LANGFUSE_FETCH_LLM_COMPLETION_TIMEOUT_MS: z.coerce
.number()
.int()
.positive()
.default(120_000), // 2 minutes
LANGFUSE_AWS_BEDROCK_REGION: z.string().optional(),
// Langfuse AI Features
LANGFUSE_AI_FEATURES_PUBLIC_KEY: z.string().optional(),
LANGFUSE_AI_FEATURES_SECRET_KEY: z.string().optional(),
LANGFUSE_AI_FEATURES_HOST: z.string().optional(),
LANGFUSE_AI_FEATURES_PROJECT_ID: z.string().optional(),
});
export const env: z.infer<typeof EnvSchema> =
@@ -7,4 +7,5 @@ export const QUEUE_ERROR_MESSAGES = {
TOO_LOW_MAX_TOKENS_ERROR: "Error: Unterminated string in JSON at position",
OUTPUT_TOKENS_TOO_LONG_ERROR:
"Could not parse response content as the length limit was reached",
TIMEOUT_ERROR: "Request timed out",
};
@@ -0,0 +1 @@
export * from "./validation";
@@ -0,0 +1,49 @@
import { z } from "zod/v4";
import { ScoreConfig as ScoreConfigDbType } from "@prisma/client";
import {
ScoreConfigDomain,
ScoreConfigSchema,
} from "../../domain/score-configs";
/**
* Use this function when pulling a list of score configs from the database before using in the application to ensure type safety.
* All score configs are expected to pass the validation. If a score fails validation, it will be logged to Otel.
* @param scoreConfigs
* @returns list of validated score configs
*/
export const filterAndValidateDbScoreConfigList = (
scoreConfigs: ScoreConfigDbType[],
onParseError?: (error: z.ZodError) => void, // eslint-disable-line no-unused-vars
): ScoreConfigDomain[] =>
scoreConfigs.reduce((acc, ts) => {
const result = ScoreConfigSchema.safeParse(ts);
if (result.success) {
acc.push(result.data);
} else {
onParseError?.(result.error);
}
return acc;
}, [] as ScoreConfigDomain[]);
/**
* Use this function when pulling a single score config from the database before using in the application to ensure type safety.
* The score is expected to pass the validation. If a score fails validation, an error will be thrown.
* @param scoreConfig
* @returns validated score config
* @throws error if score fails validation
*/
export const validateDbScoreConfig = (
scoreConfig: ScoreConfigDbType,
): ScoreConfigDomain => ScoreConfigSchema.parse(scoreConfig);
/**
* Use this function when pulling a single score config from the database before using in the application to ensure type safety.
* This function will NOT throw an error by default. The score is expected to pass the validation.
* @param scoreConfig
* @returns score config validation object:
* - success: true if the score config passes validation
* - data: the validated score config if success is true
* - error: the error object if success is false
*/
export const validateDbScoreConfigSafe = (scoreConfig: ScoreConfigDbType) =>
ScoreConfigSchema.safeParse(scoreConfig);
@@ -1,2 +1 @@
export * from "./interfaces";
export * from "./scoreConfigTypes";
@@ -8,7 +8,9 @@ export const GetScoreQueryV2 = GetScoreQuery;
export const GetScoreResponseV2 = APIScoreSchemaV2;
// GET /scores v2
export const GetScoresQueryV2 = GetScoresQuery;
export const GetScoresQueryV2 = GetScoresQuery.extend({
sessionId: z.string().nullish(),
});
export const GetScoreResponseDataV2 = z.intersection(
APIScoreSchemaV2,
z.object({
@@ -2,7 +2,7 @@ import z from "zod/v4";
import { applyScoreValidation } from "../../../../utils/scores";
import { PostScoreBodyFoundationSchema } from "../shared";
import { isPresent } from "../../../../utils/typeChecks";
import { Category as ConfigCategory } from "../../scoreConfigTypes";
import { ScoreConfigCategory } from "../../../../domain/score-configs";
export const ScoreBodyWithoutConfig = applyScoreValidation(
z.discriminatedUnion("dataType", [
@@ -54,7 +54,7 @@ const ScorePropsAgainstConfigNumeric = z
const ScorePropsAgainstConfigCategorical = z
.object({
value: z.string(),
categories: z.array(ConfigCategory),
categories: z.array(ScoreConfigCategory),
dataType: z.literal("CATEGORICAL"),
})
.superRefine((data, ctx) => {
@@ -19,10 +19,9 @@ export type NumericAggregate = BaseAggregate & {
average: number;
};
export type ScoreAggregate = Record<
string,
CategoricalAggregate | NumericAggregate
>;
export type AggregatedScoreData = CategoricalAggregate | NumericAggregate;
export type ScoreAggregate = Record<string, AggregatedScoreData>;
export type ScoreSimplified = {
id: string;
@@ -1,236 +0,0 @@
import { z } from "zod/v4";
import { ScoreConfig as ScoreConfigDbType } from "@prisma/client";
import { isPresent } from "../../utils/typeChecks";
import {
jsonSchema,
paginationMetaResponseZod,
publicApiPaginationZod,
} from "../../utils/zod";
/**
* Types to use across codebase
*/
export type ConfigCategory = z.infer<typeof Category>;
export type ValidatedScoreConfig = z.infer<typeof ValidatedScoreConfigSchema>;
const validateCategories = (
categories: ConfigCategory[],
ctx: z.RefinementCtx,
) => {
const uniqueNames = new Set<string>();
const uniqueValues = new Set<number>();
for (const category of categories) {
if (uniqueNames.has(category.label)) {
ctx.addIssue({
code: z.ZodIssueCode.custom,
message: `Duplicate category label: ${category.label}, category labels must be unique`,
});
return;
}
uniqueNames.add(category.label);
if (uniqueValues.has(category.value)) {
ctx.addIssue({
code: z.ZodIssueCode.custom,
message: `Duplicate category value: ${category.value}, category values must be unique`,
});
return;
}
uniqueValues.add(category.value);
}
};
/**
* Objects
*/
export const Category = z.object({
label: z.string().min(1),
value: z.number(),
});
const Categories = z.array(Category);
const NumericScoreConfig = z.object({
maxValue: z.number().nullish(),
minValue: z.number().nullish(),
dataType: z.literal("NUMERIC"),
categories: z.undefined().nullish(),
});
const CategoricalScoreConfig = z.object({
maxValue: z.undefined().nullish(),
minValue: z.undefined().nullish(),
dataType: z.literal("CATEGORICAL"),
categories: jsonSchema.superRefine((categories, ctx) => {
const parseResult = Categories.safeParse(categories);
if (!parseResult.success) {
ctx.addIssue({
code: "custom",
message:
"Category must be an array of objects with label value pairs, where labels and values are unique.",
} as z.core.$ZodIssueCustom);
return;
}
validateCategories(categories as ConfigCategory[], ctx);
}),
});
const BooleanScoreConfig = z.object({
maxValue: z.undefined().nullish(),
minValue: z.undefined().nullish(),
dataType: z.literal("BOOLEAN"),
categories: z
.array(Category)
.length(2, "Boolean data type must have exactly 2 categories.")
.refine((categories) => {
const expectedCategories = [
{ label: "True", value: 1 },
{ label: "False", value: 0 },
];
return categories.every(
(category, index) =>
category.label === expectedCategories[index].label &&
category.value === expectedCategories[index].value,
);
}),
});
const ScoreConfigBase = z.object({
id: z.string(),
name: z.string().min(1).max(35),
isArchived: z.boolean(),
description: z.string().nullish(),
createdAt: z.coerce.date(),
updatedAt: z.coerce.date(),
projectId: z.string(),
});
const ScoreConfigPostBase = z.object({
name: z.string(),
description: z.string().optional(),
});
const ValidatedScoreConfigSchema = z
.union([
ScoreConfigBase.merge(NumericScoreConfig),
ScoreConfigBase.merge(
z.object({
maxValue: z.undefined().nullish(),
minValue: z.undefined().nullish(),
dataType: z.literal("CATEGORICAL"),
categories: Categories.superRefine(validateCategories),
}),
),
ScoreConfigBase.merge(BooleanScoreConfig),
])
.superRefine((data, ctx) => {
if (data.dataType === "NUMERIC") {
if (
isPresent(data.maxValue) &&
isPresent(data.minValue) &&
data.maxValue <= data.minValue
) {
ctx.addIssue({
code: z.ZodIssueCode.custom,
message: "Maximum value must be greater than Minimum value",
});
}
}
});
/**
* Use this function when pulling a list of score configs from the database before using in the application to ensure type safety.
* All score configs are expected to pass the validation. If a score fails validation, it will be logged to Otel.
* @param scoreConfigs
* @returns list of validated score configs
*/
export const filterAndValidateDbScoreConfigList = (
scoreConfigs: ScoreConfigDbType[],
onParseError?: (error: z.ZodError) => void, // eslint-disable-line no-unused-vars
): ValidatedScoreConfig[] =>
scoreConfigs.reduce((acc, ts) => {
const result = ValidatedScoreConfigSchema.safeParse(ts);
if (result.success) {
acc.push(result.data);
} else {
onParseError?.(result.error);
}
return acc;
}, [] as ValidatedScoreConfig[]);
/**
* Use this function when pulling a single score config from the database before using in the application to ensure type safety.
* The score is expected to pass the validation. If a score fails validation, an error will be thrown.
* @param scoreConfig
* @returns validated score config
* @throws error if score fails validation
*/
export const validateDbScoreConfig = (
scoreConfig: ScoreConfigDbType,
): ValidatedScoreConfig => ValidatedScoreConfigSchema.parse(scoreConfig);
/**
* Use this function when pulling a single score config from the database before using in the application to ensure type safety.
* This function will NOT throw an error by default. The score is expected to pass the validation.
* @param scoreConfig
* @returns score config validation object:
* - success: true if the score config passes validation
* - data: the validated score config if success is true
* - error: the error object if success is false
*/
export const validateDbScoreConfigSafe = (scoreConfig: ScoreConfigDbType) =>
ValidatedScoreConfigSchema.safeParse(scoreConfig);
/**
* Endpoints
*/
// GET /score-configs/{configId}
export const GetScoreConfigQuery = z.object({
configId: z.string(),
});
export const GetScoreConfigResponse = ValidatedScoreConfigSchema;
// POST /score-configs
export const PostScoreConfigBody = z
.discriminatedUnion("dataType", [
ScoreConfigPostBase.merge(CategoricalScoreConfig),
ScoreConfigPostBase.merge(NumericScoreConfig),
ScoreConfigPostBase.merge(
z.object({
dataType: z.literal("BOOLEAN"),
categories: z.undefined().nullish(),
}),
),
])
.superRefine((data, ctx) => {
if (data.dataType === "NUMERIC") {
if (
isPresent(data.maxValue) &&
isPresent(data.minValue) &&
data.maxValue <= data.minValue
) {
ctx.addIssue({
code: z.ZodIssueCode.custom,
message: "Maximum value must be greater than Minimum value",
});
}
}
});
export const PostScoreConfigResponse = ValidatedScoreConfigSchema;
// GET /score-configs
export const GetScoreConfigsQuery = z.object({
...publicApiPaginationZod,
});
export const GetScoreConfigsResponse = z.object({
data: z.array(ValidatedScoreConfigSchema),
meta: paginationMetaResponseZod,
});
+4
View File
@@ -19,6 +19,7 @@ export * from "./interfaces/rate-limits";
export * from "./tableDefinitions/typeHelpers";
export * from "./domain/webhooks";
export * from "./domain/dataset-run-items";
export * from "./domain/score-configs";
// llm api
export * from "./server/llm/types";
@@ -37,6 +38,9 @@ export * from "./features/annotation/types";
// scores
export * from "./features/scores";
// score configs
export * from "./features/scoreConfigs";
// comments
export * from "./features/comments/types";
@@ -13,26 +13,18 @@ export const CloudConfigSchema = z.object({
customerId: z.string().optional(),
activeSubscriptionId: z.string().optional(),
activeProductId: z.string().optional(),
activeUsageProductId: z.string().optional(),
})
.transform((data) => ({
...data,
isLegacySubscription:
data?.activeProductId !== undefined &&
data?.activeUsageProductId === undefined,
}))
.optional(),
// custom rate limits for an organization
rateLimitOverrides: CloudConfigRateLimit.optional(),
// billing alert configuration
usageAlerts: z
.object({
enabled: z.boolean().default(true),
type: z.enum(["STRIPE"]).default("STRIPE"),
threshold: z.number().int().positive(),
alertId: z.string(), // Alert ID for tracking
meterId: z.string(), // Meter ID for usage tracking
notifications: z.object({
email: z.boolean().default(true),
recipients: z.array(z.string().email()).default([]),
}),
})
.optional(),
});
export type CloudConfigSchema = z.infer<typeof CloudConfigSchema>;
+2 -2
View File
@@ -1,11 +1,11 @@
import { type Organization } from "@prisma/client";
import { CloudConfigSchema } from "./cloudConfigSchema";
type parsedOrg = Omit<Organization, "cloudConfig"> & {
export type ParsedOrganization = Omit<Organization, "cloudConfig"> & {
cloudConfig: CloudConfigSchema | null;
};
export function parseDbOrg(dbOrg: Organization): parsedOrg {
export function parseDbOrg(dbOrg: Organization): ParsedOrganization {
const { cloudConfig, ...org } = dbOrg;
const parsedCloudConfig = CloudConfigSchema.safeParse(cloudConfig);
@@ -0,0 +1,119 @@
import { prisma } from "../../db";
import { redis, safeMultiDel } from "..";
import { logger } from "../logger";
import { type ApiKey } from "../../db";
/**
* Invalidate cached API keys from Redis cache
*
* Utility used by higher-level helpers to remove individual API keys from the cache,
* e.g. after key rotation, revocation, or entitlement/plan changes.
*
* Note: This only invalidates the Redis cache, not the API keys themselves in the database.
*
* Behavior:
* - Skips keys without a `fastHashedSecretKey`
* - No-ops when Redis is not configured
*
* @param apiKeys - List of API key records to invalidate from cache
* @param identifier - Context string for logging (e.g., org or project identifier)
*/
export async function invalidateCachedApiKeys(
apiKeys: ApiKey[],
identifier: string,
) {
const hashKeys = apiKeys.map((key) => key.fastHashedSecretKey);
const filteredHashKeys = hashKeys.filter((hash): hash is string =>
Boolean(hash),
);
if (filteredHashKeys.length === 0) {
logger.info("No valid keys to invalidate");
return;
}
if (redis) {
logger.info(`Invalidating API keys in redis for ${identifier}`);
const keysToDelete = filteredHashKeys.map((hash) => `api-key:${hash}`);
await safeMultiDel(redis, keysToDelete);
}
}
/**
* Invalidate all cached API keys for an organization from Redis cache
*
* This function is used when organization-level changes occur that affect API key validity,
* such as:
* - Plan changes (subscription created/updated/deleted)
* - Usage threshold state changes (blocking/unblocking)
* - Billing cycle changes
*
* Note: This only invalidates the Redis cache, not the API keys themselves in the database.
*
* @param orgId - The organization ID whose API keys should be invalidated from cache
*/
export async function invalidateCachedOrgApiKeys(orgId: string): Promise<void> {
const apiKeys = await prisma.apiKey.findMany({
where: {
OR: [
{
project: {
orgId,
},
},
{ orgId },
],
},
});
const hashKeys = apiKeys
.map((key) => key.fastHashedSecretKey)
.filter((hash): hash is string => Boolean(hash));
if (hashKeys.length === 0) {
logger.info(`No valid API keys to invalidate for org ${orgId}`);
return;
}
if (redis) {
logger.info(`Invalidating API keys in redis for org ${orgId}`);
const keysToDelete = hashKeys.map((hash) => `api-key:${hash}`);
await safeMultiDel(redis, keysToDelete);
}
}
/**
* Invalidate all cached API keys for a project from Redis cache
*
* This function is used when project-level changes occur that affect API key validity.
*
* Note: This only invalidates the Redis cache, not the API keys themselves in the database.
*
* @param projectId - The project ID whose API keys should be invalidated from cache
*/
export async function invalidateCachedProjectApiKeys(
projectId: string,
): Promise<void> {
const apiKeys = await prisma.apiKey.findMany({
where: {
projectId: projectId,
scope: "PROJECT",
},
});
const hashKeys = apiKeys
.map((key) => key.fastHashedSecretKey)
.filter((hash): hash is string => Boolean(hash));
if (hashKeys.length === 0) {
logger.info(`No valid API keys to invalidate for project ${projectId}`);
return;
}
if (redis) {
logger.info(`Invalidating API keys in redis for project ${projectId}`);
const keysToDelete = hashKeys.map((hash) => `api-key:${hash}`);
await safeMultiDel(redis, keysToDelete);
}
}
+2
View File
@@ -16,6 +16,7 @@ const ApiKeyBaseSchema = z.object({
orgId: z.string(),
plan: z.enum(plans as unknown as [string, ...string[]]),
rateLimitOverrides: CloudConfigRateLimit.nullish(),
isIngestionSuspended: z.boolean().nullish(),
});
export const OrgEnrichedApiKey = z.discriminatedUnion("scope", [
@@ -64,6 +65,7 @@ type ApiAccessScopeMetadata = {
rateLimitOverrides: z.infer<typeof CloudConfigRateLimit>;
apiKeyId: string;
publicKey: string;
isIngestionSuspended: boolean | null | undefined;
};
export type ApiAccessScopeIngestion = BaseApiAccessScope &
+42 -19
View File
@@ -6,6 +6,8 @@ import { propagation, context } from "@opentelemetry/api";
export type ClickhouseClientType = ReturnType<typeof createClient>;
export type PreferredClickhouseService = "ReadWrite" | "ReadOnly";
/**
* ClickHouseClientManager provides a singleton pattern for managing ClickHouse clients.
* It creates and reuses clients based on their configuration to avoid creating
@@ -35,22 +37,40 @@ export class ClickHouseClientManager {
* @param opts Client parameters
* @returns String hash key
*/
private generateClientSettingsKey(
private generateClientSettings(
opts: NodeClickHouseClientConfigOptions,
): string {
preferredClickhouseService: PreferredClickhouseService = "ReadWrite",
): NodeClickHouseClientConfigOptions {
const keyParams = {
url: env.CLICKHOUSE_URL,
url: this.getClickhouseUrl(preferredClickhouseService),
username: env.CLICKHOUSE_USER,
password: env.CLICKHOUSE_PASSWORD,
database: env.CLICKHOUSE_DB,
http_headers: opts?.http_headers,
http_headers: opts?.http_headers ?? {},
settings: opts?.clickhouse_settings,
request_timeout: opts?.request_timeout ?? 30000,
...(opts.request_timeout
? { request_timeout: opts.request_timeout }
: {}),
// Include any other relevant config options
};
return JSON.stringify(keyParams);
return keyParams;
}
private generateClientSettingsKey(
settings: NodeClickHouseClientConfigOptions,
): string {
return JSON.stringify(settings);
}
private getClickhouseUrl = (
preferredClickhouseService: PreferredClickhouseService,
) => {
return preferredClickhouseService === "ReadWrite"
? env.CLICKHOUSE_URL
: env.CLICKHOUSE_READ_ONLY_URL || env.CLICKHOUSE_URL;
};
/**
* Get or create a client based on the provided parameters
* @param opts Client configuration parameters
@@ -58,13 +78,17 @@ export class ClickHouseClientManager {
*/
public getClient(
opts: NodeClickHouseClientConfigOptions,
preferredClickhouseService: PreferredClickhouseService = "ReadWrite",
): ClickhouseClientType {
const key = this.generateClientSettingsKey(opts);
const settings = this.generateClientSettings(
opts,
preferredClickhouseService,
);
const key = this.generateClientSettingsKey(settings);
if (!this.clientMap.has(key)) {
const headers = opts?.http_headers ?? {};
const activeSpan = getCurrentSpan();
if (activeSpan) {
propagation.inject(context.active(), headers);
propagation.inject(context.active(), settings.http_headers);
}
const cloudOptions: Record<string, unknown> = {};
@@ -78,11 +102,7 @@ export class ClickHouseClientManager {
const client = createClient({
...opts,
url: env.CLICKHOUSE_URL,
username: env.CLICKHOUSE_USER,
password: env.CLICKHOUSE_PASSWORD,
database: env.CLICKHOUSE_DB,
http_headers: headers,
...settings,
keep_alive: {
idle_socket_ttl: env.CLICKHOUSE_KEEP_ALIVE_IDLE_SOCKET_TTL,
},
@@ -99,9 +119,6 @@ export class ClickHouseClientManager {
}
: {}),
},
...(opts.request_timeout
? { request_timeout: opts.request_timeout }
: {}),
});
this.clientMap.set(key, client);
@@ -122,8 +139,14 @@ export class ClickHouseClientManager {
}
}
export const clickhouseClient = (opts?: NodeClickHouseClientConfigOptions) => {
return ClickHouseClientManager.getInstance().getClient(opts ?? {});
export const clickhouseClient = (
opts?: NodeClickHouseClientConfigOptions,
preferredClickhouseService: PreferredClickhouseService = "ReadWrite",
) => {
return ClickHouseClientManager.getInstance().getClient(
opts ?? {},
preferredClickhouseService,
);
};
/**
@@ -1,7 +1,5 @@
import { instrumentAsync, recordDistribution } from "../instrumentation";
import * as opentelemetry from "@opentelemetry/api";
import { env } from "../../env";
import { logger } from "../logger";
import { instrumentAsync } from "../instrumentation";
const executionWrapper = async <T, Y>(
input: T,
@@ -20,16 +18,15 @@ const executionWrapper = async <T, Y>(
};
/**
* Measures the execution time of two functions and returns the result based on the experiment configuration.
* This is used to compare the execution of AggregatingMergeTrees with the existing ReplacingMergeTree execution.
* Measures the execution time of a query functions and returns the result based on the experiment configuration.
* Presently this is but a simple wrapper around single execution, but it is designed to be
* extended for A/B testing or canary releases.
*/
export const measureAndReturn = async <T, Y>(args: {
operationName: string;
projectId: string;
input: T;
existingExecution: (input: T) => Promise<Y>; // eslint-disable-line no-unused-vars
newExecution: (input: T) => Promise<Y>; // eslint-disable-line no-unused-vars
minStartTime?: Date;
fn: (input: T) => Promise<Y>; // eslint-disable-line no-unused-vars
}): Promise<Y> => {
return instrumentAsync(
{
@@ -37,112 +34,17 @@ export const measureAndReturn = async <T, Y>(args: {
spanKind: opentelemetry.SpanKind.CLIENT,
},
async (currentSpan) => {
const { input, existingExecution, newExecution, minStartTime } = args;
const { input, fn } = args;
if (
env.LANGFUSE_EXPERIMENT_COMPARE_READ_FROM_AGGREGATING_MERGE_TREES !==
"true"
) {
currentSpan.setAttribute(`langfuse.experiment.amts.run`, "disabled");
// When we want do to multiple executions for A/B testing or canary releases,
// or some other form of a more complex wrapper we used to try-catch
// using the wrapper function and fallback to a simple f(input) when it failed.
const [[existingResult, _existingDuration]] = // eslint-disable-line no-unused-vars
await Promise.all([
executionWrapper(input, fn, currentSpan, "existing"),
]);
// Check for short-term new result experiment
if (
env.LANGFUSE_EXPERIMENT_RETURN_NEW_RESULT_SHORT_TERM === "true" &&
minStartTime
) {
const thirtyDaysAgo = new Date();
thirtyDaysAgo.setDate(thirtyDaysAgo.getDate() - 30);
if (minStartTime >= thirtyDaysAgo) {
currentSpan.setAttribute(
`langfuse.experiment.amts.short-term`,
"true",
);
return newExecution(input);
}
}
return env.LANGFUSE_EXPERIMENT_RETURN_NEW_RESULT === "true"
? newExecution(input)
: existingExecution(input);
}
// If not whitelisted, apply sampling logic
if (
!env.LANGFUSE_EXPERIMENT_WHITELISTED_PROJECT_IDS.includes(
args.projectId,
) &&
Math.random() > env.LANGFUSE_EXPERIMENT_SAMPLING_RATE
) {
currentSpan.setAttribute(`langfuse.experiment.amts.run`, "sampled-out");
return existingExecution(input);
}
currentSpan.setAttribute(`langfuse.experiment.amts.run`, "true");
try {
const [[existingResult, existingDuration], [newResult, newDuration]] =
await Promise.all([
executionWrapper(input, existingExecution, currentSpan, "existing"),
executionWrapper(input, newExecution, currentSpan, "new"),
]);
// Positive duration difference means new is faster
const durationDifference = existingDuration - newDuration;
currentSpan?.setAttribute(
"langfuse.experiment.amts.execution-time-difference",
durationDifference,
);
recordDistribution(
"langfuse.experiment.amts.duration_difference_distribution",
durationDifference,
{
operation: args.operationName,
},
);
if (
env.LANGFUSE_EXPERIMENT_ADD_QUERY_RESULT_TO_SPAN_PROJECT_IDS.some(
(p) => p === args.projectId,
)
) {
currentSpan?.setAttribute(
"langfuse.experiment.amts.existing-result",
JSON.stringify(existingResult),
);
currentSpan?.setAttribute(
"langfuse.experiment.amts.new-result",
JSON.stringify(newResult),
);
}
// Check for short-term new result experiment
if (
env.LANGFUSE_EXPERIMENT_RETURN_NEW_RESULT_SHORT_TERM === "true" &&
minStartTime
) {
const thirtyDaysAgo = new Date();
thirtyDaysAgo.setDate(thirtyDaysAgo.getDate() - 30);
if (minStartTime >= thirtyDaysAgo) {
currentSpan.setAttribute(
`langfuse.experiment.amts.short-term`,
"true",
);
return newResult;
}
}
return env.LANGFUSE_EXPERIMENT_RETURN_NEW_RESULT === "true"
? newResult
: existingResult;
} catch (e) {
logger.error(
"Failed to run experiment wrapper. Retrying existing query",
e,
);
return existingExecution(input);
}
return existingResult;
},
);
};
@@ -1,81 +0,0 @@
import { env } from "../../env";
import { logger } from "../../server/logger";
import {
DatasetRunItemsExecutionStrategy,
DatasetRunItemsOperationType,
} from "./types";
/**
* Returns the execution strategy for dataset run items based on environment variables.
*
* Two-phase migration approach:
* 1. Dual-write phase: DATASET_RUN_ITEMS_WRITE_TO_CLICKHOUSE=true (write to both databases)
* 2. Read migration phase: DATASET_RUN_ITEMS_READ_FROM_CLICKHOUSE=true (read from ClickHouse)
*/
function getDatasetRunItemsExecutionStrategy(): DatasetRunItemsExecutionStrategy {
return {
shouldWriteToClickHouse:
env.LANGFUSE_EXPERIMENT_DATASET_RUN_ITEMS_WRITE_CH === "true",
shouldReadFromClickHouse:
env.LANGFUSE_EXPERIMENT_DATASET_RUN_ITEMS_READ_CH === "true",
};
}
// Re-export the enum for backward compatibility
/**
* Executes the appropriate database operation based on the execution strategy.
*
* @param postgresExecution - Function to execute PostgreSQL operation
* @param clickhouseExecution - Function to execute ClickHouse operation
* @param operationType - Type of operation ("read" or "write")
* @returns Result from the selected execution strategy
*/
export async function executeWithDatasetRunItemsStrategy<TInput, TOutput>({
input,
operationType,
postgresExecution,
clickhouseExecution,
}: {
input: TInput;
operationType: DatasetRunItemsOperationType;
// eslint-disable-next-line no-unused-vars
postgresExecution: (input: TInput) => Promise<TOutput>;
// eslint-disable-next-line no-unused-vars
clickhouseExecution: (input: TInput) => Promise<TOutput>;
}): Promise<TOutput> {
const strategy = getDatasetRunItemsExecutionStrategy();
if (operationType === DatasetRunItemsOperationType.WRITE) {
// For write operations, implement dual-write strategy
if (strategy.shouldWriteToClickHouse) {
// Dual-write phase: write to both databases
const postgresResult = await postgresExecution(input);
try {
const clickhouseResult = await clickhouseExecution(input);
logger.debug("Successfully wrote to both PostgreSQL and ClickHouse", {
operation: `dataset_run_items_${operationType}`,
});
return clickhouseResult;
} catch (error) {
logger.error("ClickHouse write failed during dual-write phase", {
error: error instanceof Error ? error.message : String(error),
operation: `dataset_run_items_${operationType}`,
});
// Continue with PostgreSQL result since it succeeded
}
return postgresResult;
} else {
// Write only to PostgreSQL
return await postgresExecution(input);
}
} else {
if (strategy.shouldReadFromClickHouse) {
return await clickhouseExecution(input);
} else {
// Read from PostgreSQL
return await postgresExecution(input);
}
}
}
@@ -1,16 +0,0 @@
/**
* Types and enums for dataset run items execution.
* This file is frontend-safe and doesn't import server-side dependencies.
*/
export enum DatasetRunItemsOperationType {
// eslint-disable-next-line no-unused-vars
READ = "read",
// eslint-disable-next-line no-unused-vars
WRITE = "write",
}
export type DatasetRunItemsExecutionStrategy = {
shouldWriteToClickHouse: boolean;
shouldReadFromClickHouse: boolean;
};

Some files were not shown because too many files have changed in this diff Show More