Compare commits

...
194 Commits
Author SHA1 Message Date
Marlies Mayerhofer 736855b5fa chore: release v2.84.1
CI/CD / lint (push) Waiting to run
CI/CD / test-docker-build (push) Waiting to run
CI/CD / tests-web (node20, pg12) (push) Waiting to run
CI/CD / tests-web (node20, pg15) (push) Waiting to run
CI/CD / tests-worker (node20, pg12) (push) Waiting to run
CI/CD / tests-worker (node20, pg15) (push) Waiting to run
CI/CD / e2e-tests (push) Waiting to run
CI/CD / e2e-server-tests (push) Waiting to run
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
2024-10-09 23:42:05 -07:00
3fa2cc8957 fix: show add to annotation queue only when flag is set (#3693)
---------

Co-authored-by: Marc Klingen <git@marcklingen.com>
2024-10-10 08:04:07 +02:00
Marc Klingen 33e6be3215 docs: add readme to feature flags 2024-10-09 22:48:11 -07:00
Marc KlingenandGitHub 9c2884a56f fix(ui): md-view, ordered lists with many items (#3694) 2024-10-10 07:44:11 +02:00
dependabot[bot]GitHubdependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
5734a9619a chore(deps-dev): bump eslint-config-next from 14.2.6 to 14.2.15 in the next group across 1 directory (#3682)
chore(deps-dev): bump eslint-config-next

Bumps the next group with 1 update in the / directory: [eslint-config-next](https://github.com/vercel/next.js/tree/HEAD/packages/eslint-config-next).


Updates `eslint-config-next` from 14.2.6 to 14.2.15
- [Release notes](https://github.com/vercel/next.js/releases)
- [Changelog](https://github.com/vercel/next.js/blob/canary/release.js)
- [Commits](https://github.com/vercel/next.js/commits/v14.2.15/packages/eslint-config-next)

---
updated-dependencies:
- dependency-name: eslint-config-next
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: next
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-10-10 05:42:29 +00:00
marliessophieandGitHub a3d58f6727 Revert "chore(deps): bump react-hook-form from 7.51.5 to 7.53.0" (#3692) 2024-10-10 05:19:13 +00:00
Marc KlingenandGitHub 408fb64c69 feat: add posthog event for tracing_check_active (#3691) 2024-10-10 06:58:57 +02:00
dependabot[bot]GitHubdependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
5200390427 chore(deps): bump @headlessui/react from 1.7.18 to 2.1.9 (#3683)
Bumps [@headlessui/react](https://github.com/tailwindlabs/headlessui/tree/HEAD/packages/@headlessui-react) from 1.7.18 to 2.1.9.
- [Release notes](https://github.com/tailwindlabs/headlessui/releases)
- [Changelog](https://github.com/tailwindlabs/headlessui/blob/main/packages/@headlessui-react/CHANGELOG.md)
- [Commits](https://github.com/tailwindlabs/headlessui/commits/@headlessui/react@v2.1.9/packages/@headlessui-react)

---
updated-dependencies:
- dependency-name: "@headlessui/react"
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-10-10 04:47:30 +00:00
dependabot[bot]GitHubdependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
6f2be5225b chore(deps-dev): bump postcss from 8.4.38 to 8.4.47 (#3684)
Bumps [postcss](https://github.com/postcss/postcss) from 8.4.38 to 8.4.47.
- [Release notes](https://github.com/postcss/postcss/releases)
- [Changelog](https://github.com/postcss/postcss/blob/main/CHANGELOG.md)
- [Commits](https://github.com/postcss/postcss/compare/8.4.38...8.4.47)

---
updated-dependencies:
- dependency-name: postcss
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-10-10 04:33:26 +00:00
dependabot[bot]GitHubdependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
f01a110bce chore(deps): bump @langchain/core from 0.3.7 to 0.3.9 (#3690)
Bumps [@langchain/core](https://github.com/langchain-ai/langchainjs) from 0.3.7 to 0.3.9.
- [Release notes](https://github.com/langchain-ai/langchainjs/releases)
- [Changelog](https://github.com/langchain-ai/langchainjs/blob/main/release_workspace.js)
- [Commits](https://github.com/langchain-ai/langchainjs/commits)

---
updated-dependencies:
- dependency-name: "@langchain/core"
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-10-09 21:21:23 -07:00
Max Deichmann b6e49b440e chore: release v2.84.0
CI/CD / lint (push) Waiting to run
CI/CD / test-docker-build (push) Waiting to run
CI/CD / tests-web (node20, pg12) (push) Waiting to run
CI/CD / tests-web (node20, pg15) (push) Waiting to run
CI/CD / tests-worker (node20, pg12) (push) Waiting to run
CI/CD / tests-worker (node20, pg15) (push) Waiting to run
CI/CD / e2e-tests (push) Waiting to run
CI/CD / e2e-server-tests (push) Waiting to run
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
2024-10-09 20:57:25 -07:00
marliessophieandGitHub ff6e0648f3 chore: allow updating annotation queue name (#3687) 2024-10-09 23:01:14 +00:00
c4fa31be9f feat(annotation): add annotation queue in feature flagged beta release (#3499)
Co-authored-by: Marc Klingen <git@marcklingen.com>
Co-authored-by: Max Deichmann <m.deichmann@tum.de>
2024-10-09 23:59:24 +02:00
e68394abc6 chore: upgrade nextjs to 14.2.14 (#3643)
Co-authored-by: Max Deichmann <m.deichmann@tum.de>
2024-10-09 13:30:54 -07:00
Max DeichmannandGitHub c24032391d fix: do not retry when api key is missing for evals (#3680) 2024-10-09 18:28:18 +00:00
Max DeichmannandGitHub 6c7faf61f8 fix: adjust queue wait time metrics (#3677)
fix
2024-10-09 14:47:47 +00:00
Marc KlingenandGitHub 43bedf2afe fix: encode all links to objects with user-provided ids (#3672) 2024-10-09 08:59:01 +00:00
Marc KlingenandGitHub 674bdbd976 fix(ui): disable sign-in and sso buttons while email/pw are empty (#3670) 2024-10-09 08:42:41 +00:00
dependabot[bot]GitHubdependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
ed1f6d8563 chore(deps): bump the observability group with 3 updates (#3657)
Bumps the observability group with 3 updates: [@opentelemetry/resource-detector-aws](https://github.com/open-telemetry/opentelemetry-js-contrib), [@opentelemetry/resource-detector-container](https://github.com/open-telemetry/opentelemetry-js-contrib) and [@opentelemetry/instrumentation-express](https://github.com/open-telemetry/opentelemetry-js-contrib).


Updates `@opentelemetry/resource-detector-aws` from 1.6.1 to 1.6.2
- [Release notes](https://github.com/open-telemetry/opentelemetry-js-contrib/releases)
- [Changelog](https://github.com/open-telemetry/opentelemetry-js-contrib/blob/main/CHANGELOG.md)
- [Commits](https://github.com/open-telemetry/opentelemetry-js-contrib/compare/resource-detector-aws-v1.6.1...resource-detector-aws-v1.6.2)

Updates `@opentelemetry/resource-detector-container` from 0.4.1 to 0.4.2
- [Release notes](https://github.com/open-telemetry/opentelemetry-js-contrib/releases)
- [Changelog](https://github.com/open-telemetry/opentelemetry-js-contrib/blob/main/CHANGELOG.md)
- [Commits](https://github.com/open-telemetry/opentelemetry-js-contrib/compare/instrumentation-tedious-v0.4.1...resource-detector-instana-v0.4.2)

Updates `@opentelemetry/instrumentation-express` from 0.42.0 to 0.43.0
- [Release notes](https://github.com/open-telemetry/opentelemetry-js-contrib/releases)
- [Changelog](https://github.com/open-telemetry/opentelemetry-js-contrib/blob/main/CHANGELOG.md)
- [Commits](https://github.com/open-telemetry/opentelemetry-js-contrib/compare/instrumentation-pg-v0.42.0...instrumentation-pg-v0.43.0)

---
updated-dependencies:
- dependency-name: "@opentelemetry/resource-detector-aws"
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: observability
- dependency-name: "@opentelemetry/resource-detector-container"
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: observability
- dependency-name: "@opentelemetry/instrumentation-express"
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: observability
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-10-09 06:52:58 +00:00
dependabot[bot]GitHubdependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
b49d335eb0 chore(deps): bump @aws-sdk/lib-storage from 3.645.0 to 3.667.0 (#3661)
Bumps [@aws-sdk/lib-storage](https://github.com/aws/aws-sdk-js-v3/tree/HEAD/lib/lib-storage) from 3.645.0 to 3.667.0.
- [Release notes](https://github.com/aws/aws-sdk-js-v3/releases)
- [Changelog](https://github.com/aws/aws-sdk-js-v3/blob/main/lib/lib-storage/CHANGELOG.md)
- [Commits](https://github.com/aws/aws-sdk-js-v3/commits/v3.667.0/lib/lib-storage)

---
updated-dependencies:
- dependency-name: "@aws-sdk/lib-storage"
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-10-09 06:36:58 +00:00
dependabot[bot]GitHubdependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
e9bc76fe89 chore(deps): bump @heroicons/react from 2.1.3 to 2.1.5 (#3662)
Bumps [@heroicons/react](https://github.com/tailwindlabs/heroicons) from 2.1.3 to 2.1.5.
- [Release notes](https://github.com/tailwindlabs/heroicons/releases)
- [Changelog](https://github.com/tailwindlabs/heroicons/blob/master/CHANGELOG.md)
- [Commits](https://github.com/tailwindlabs/heroicons/compare/v2.1.3...v2.1.5)

---
updated-dependencies:
- dependency-name: "@heroicons/react"
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-10-09 06:24:35 +00:00
Marc KlingenandGitHub 7cdbc7f464 feat: add crisp events in posthog (#3667)
* feat: add crisp events in posthog

* fix dep

* add comment
2024-10-09 06:12:03 +00:00
237e539c4c fix: add Gitlab Issuer and Account.created_at col (#3651)
---------

Co-authored-by: Marc Klingen <git@marcklingen.com>
2024-10-09 06:46:17 +02:00
Max DeichmannandGitHub b1357a0bae feat: front-end cloud admin flag for clickhouse (#3666) 2024-10-09 02:37:45 +00:00
Max DeichmannandGitHub fa51789ac2 chore: add projectid to ingestion logs (#3665) 2024-10-09 00:23:27 +00:00
Max DeichmannandGitHub 894ffdda69 chore: remove metrics by users datadog (#3664) 2024-10-08 17:04:31 -07:00
Max DeichmannandGitHub 0c2e6a5b5d chore: add orgid and projectid to ingestion event metric (#3654) 2024-10-08 15:27:07 -07:00
Max DeichmannandGitHub 0d7e5bfcca feat: invalidate API keys via cloud admin api (#3653) 2024-10-08 22:16:48 +00:00
Max DeichmannandGitHub 7cc5a9e229 chore: reduce auth logging (#3652) 2024-10-08 21:54:48 +00:00
629b30e42d fix: properly split tags when passed as a string (#3621) (#3650)
Co-authored-by: Joris ANDRADE <joris@front.com>
2024-10-08 19:00:25 +00:00
Steffen SchmitzandGitHub 3e98df566c chore: update metric name for eval exec queue length (#3649) 2024-10-08 15:50:43 +00:00
Steffen SchmitzandGitHub 0a88294202 chore: use s3 event store to avoid event data in redis (#3537) 2024-10-08 11:05:25 +00:00
Steffen SchmitzandGitHub 6e9f5ace41 chore: update bullmq instrumentation (#3644) 2024-10-08 12:53:48 +02:00
Marc Klingen 1b662c95e5 chore: release v2.83.1
CI/CD / lint (push) Waiting to run
CI/CD / test-docker-build (push) Waiting to run
CI/CD / tests-web (node20, pg12) (push) Waiting to run
CI/CD / tests-web (node20, pg15) (push) Waiting to run
CI/CD / tests-worker (node20, pg12) (push) Waiting to run
CI/CD / tests-worker (node20, pg15) (push) Waiting to run
CI/CD / e2e-tests (push) Waiting to run
CI/CD / e2e-server-tests (push) Waiting to run
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
2024-10-08 02:42:25 -07:00
Marc KlingenandGitHub 87e94289ff revert: next upgrade due to incompatibility issues (#3640) 2024-10-08 11:25:41 +02:00
Steffen SchmitzandGitHub ed08331927 chore: remove explicit context propagation for queues (#3622) 2024-10-08 09:16:29 +02:00
dependabot[bot]GitHubdependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
02e5c0119a chore(deps-dev): bump prettier from 3.2.5 to 3.3.3 (#3626)
Bumps [prettier](https://github.com/prettier/prettier) from 3.2.5 to 3.3.3.
- [Release notes](https://github.com/prettier/prettier/releases)
- [Changelog](https://github.com/prettier/prettier/blob/main/CHANGELOG.md)
- [Commits](https://github.com/prettier/prettier/compare/3.2.5...3.3.3)

---
updated-dependencies:
- dependency-name: prettier
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-10-08 05:27:30 +00:00
dependabot[bot]GitHubdependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
adc39d58e4 chore(deps-dev): bump tsx from 4.19.0 to 4.19.1 (#3627)
Bumps [tsx](https://github.com/privatenumber/tsx) from 4.19.0 to 4.19.1.
- [Release notes](https://github.com/privatenumber/tsx/releases)
- [Changelog](https://github.com/privatenumber/tsx/blob/master/release.config.cjs)
- [Commits](https://github.com/privatenumber/tsx/compare/v4.19.0...v4.19.1)

---
updated-dependencies:
- dependency-name: tsx
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-10-08 05:15:27 +00:00
dependabot[bot]GitHubdependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
19987e9816 chore(deps): bump @langchain/core from 0.3.3 to 0.3.7 (#3628)
Bumps [@langchain/core](https://github.com/langchain-ai/langchainjs) from 0.3.3 to 0.3.7.
- [Release notes](https://github.com/langchain-ai/langchainjs/releases)
- [Changelog](https://github.com/langchain-ai/langchainjs/blob/main/release_workspace.js)
- [Commits](https://github.com/langchain-ai/langchainjs/commits)

---
updated-dependencies:
- dependency-name: "@langchain/core"
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-10-08 05:03:19 +00:00
dependabot[bot]GitHubdependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
746299431f chore(deps): bump lucide-react from 0.436.0 to 0.447.0 (#3630)
Bumps [lucide-react](https://github.com/lucide-icons/lucide/tree/HEAD/packages/lucide-react) from 0.436.0 to 0.447.0.
- [Release notes](https://github.com/lucide-icons/lucide/releases)
- [Commits](https://github.com/lucide-icons/lucide/commits/0.447.0/packages/lucide-react)

---
updated-dependencies:
- dependency-name: lucide-react
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-10-08 04:51:00 +00:00
dependabot[bot]GitHubdependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
74224a04cd chore(deps-dev): bump msw from 2.4.1 to 2.4.9 (#3631)
Bumps [msw](https://github.com/mswjs/msw) from 2.4.1 to 2.4.9.
- [Release notes](https://github.com/mswjs/msw/releases)
- [Changelog](https://github.com/mswjs/msw/blob/main/CHANGELOG.md)
- [Commits](https://github.com/mswjs/msw/compare/v2.4.1...v2.4.9)

---
updated-dependencies:
- dependency-name: msw
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-10-08 04:38:56 +00:00
dependabot[bot]GitHubdependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
874d574a9e chore(deps-dev): bump nodemon from 3.1.3 to 3.1.7 (#3632)
Bumps [nodemon](https://github.com/remy/nodemon) from 3.1.3 to 3.1.7.
- [Release notes](https://github.com/remy/nodemon/releases)
- [Commits](https://github.com/remy/nodemon/compare/v3.1.3...v3.1.7)

---
updated-dependencies:
- dependency-name: nodemon
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-10-08 04:22:00 +00:00
Max Deichmann 13655515a2 chore: release v2.83.0
CI/CD / lint (push) Waiting to run
CI/CD / test-docker-build (push) Waiting to run
CI/CD / tests-web (node20, pg12) (push) Waiting to run
CI/CD / tests-web (node20, pg15) (push) Waiting to run
CI/CD / tests-worker (node20, pg12) (push) Waiting to run
CI/CD / tests-worker (node20, pg15) (push) Waiting to run
CI/CD / e2e-tests (push) Waiting to run
CI/CD / e2e-server-tests (push) Waiting to run
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
2024-10-07 17:45:14 -07:00
Max DeichmannandGitHub 204ce02bd1 chore: reduce log level (#3636) 2024-10-07 17:41:12 -07:00
Max DeichmannandGitHub b87f84bf67 chore: upgrade webpack (#3633) 2024-10-07 15:03:41 -07:00
Max DeichmannandGitHub e139b40167 chore: upgrade next (#3603) 2024-10-07 19:41:33 +00:00
Steffen SchmitzandGitHub f3596fac10 chore: update GHA checkout and setup-node actions (#3620) 2024-10-07 15:47:22 +02:00
Steffen SchmitzandGitHub f1d15ea268 chore: correlate logs with traces (#3619) 2024-10-07 11:57:19 +00:00
dependabot[bot]GitHubdependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
5183cf4859 chore(deps): bump the radix-ui group with 15 updates (#3609)
Bumps the radix-ui group with 15 updates:

| Package | From | To |
| --- | --- | --- |
| [@radix-ui/react-accordion](https://github.com/radix-ui/primitives) | `1.2.0` | `1.2.1` |
| [@radix-ui/react-alert-dialog](https://github.com/radix-ui/primitives) | `1.1.1` | `1.1.2` |
| [@radix-ui/react-avatar](https://github.com/radix-ui/primitives) | `1.1.0` | `1.1.1` |
| [@radix-ui/react-checkbox](https://github.com/radix-ui/primitives) | `1.1.1` | `1.1.2` |
| [@radix-ui/react-collapsible](https://github.com/radix-ui/primitives) | `1.1.0` | `1.1.1` |
| [@radix-ui/react-dialog](https://github.com/radix-ui/primitives) | `1.1.1` | `1.1.2` |
| [@radix-ui/react-dropdown-menu](https://github.com/radix-ui/primitives) | `2.1.1` | `2.1.2` |
| [@radix-ui/react-hover-card](https://github.com/radix-ui/primitives) | `1.1.1` | `1.1.2` |
| [@radix-ui/react-popover](https://github.com/radix-ui/primitives) | `1.1.1` | `1.1.2` |
| [@radix-ui/react-scroll-area](https://github.com/radix-ui/primitives) | `1.1.0` | `1.2.0` |
| [@radix-ui/react-select](https://github.com/radix-ui/primitives) | `2.1.1` | `2.1.2` |
| [@radix-ui/react-slider](https://github.com/radix-ui/primitives) | `1.2.0` | `1.2.1` |
| [@radix-ui/react-switch](https://github.com/radix-ui/primitives) | `1.1.0` | `1.1.1` |
| [@radix-ui/react-tabs](https://github.com/radix-ui/primitives) | `1.1.0` | `1.1.1` |
| [@radix-ui/react-tooltip](https://github.com/radix-ui/primitives) | `1.1.2` | `1.1.3` |


Updates `@radix-ui/react-accordion` from 1.2.0 to 1.2.1
- [Changelog](https://github.com/radix-ui/primitives/blob/main/release-process.md)
- [Commits](https://github.com/radix-ui/primitives/commits)

Updates `@radix-ui/react-alert-dialog` from 1.1.1 to 1.1.2
- [Changelog](https://github.com/radix-ui/primitives/blob/main/release-process.md)
- [Commits](https://github.com/radix-ui/primitives/commits)

Updates `@radix-ui/react-avatar` from 1.1.0 to 1.1.1
- [Changelog](https://github.com/radix-ui/primitives/blob/main/release-process.md)
- [Commits](https://github.com/radix-ui/primitives/commits)

Updates `@radix-ui/react-checkbox` from 1.1.1 to 1.1.2
- [Changelog](https://github.com/radix-ui/primitives/blob/main/release-process.md)
- [Commits](https://github.com/radix-ui/primitives/commits)

Updates `@radix-ui/react-collapsible` from 1.1.0 to 1.1.1
- [Changelog](https://github.com/radix-ui/primitives/blob/main/release-process.md)
- [Commits](https://github.com/radix-ui/primitives/commits)

Updates `@radix-ui/react-dialog` from 1.1.1 to 1.1.2
- [Changelog](https://github.com/radix-ui/primitives/blob/main/release-process.md)
- [Commits](https://github.com/radix-ui/primitives/commits)

Updates `@radix-ui/react-dropdown-menu` from 2.1.1 to 2.1.2
- [Changelog](https://github.com/radix-ui/primitives/blob/main/release-process.md)
- [Commits](https://github.com/radix-ui/primitives/commits)

Updates `@radix-ui/react-hover-card` from 1.1.1 to 1.1.2
- [Changelog](https://github.com/radix-ui/primitives/blob/main/release-process.md)
- [Commits](https://github.com/radix-ui/primitives/commits)

Updates `@radix-ui/react-popover` from 1.1.1 to 1.1.2
- [Changelog](https://github.com/radix-ui/primitives/blob/main/release-process.md)
- [Commits](https://github.com/radix-ui/primitives/commits)

Updates `@radix-ui/react-scroll-area` from 1.1.0 to 1.2.0
- [Changelog](https://github.com/radix-ui/primitives/blob/main/release-process.md)
- [Commits](https://github.com/radix-ui/primitives/commits)

Updates `@radix-ui/react-select` from 2.1.1 to 2.1.2
- [Changelog](https://github.com/radix-ui/primitives/blob/main/release-process.md)
- [Commits](https://github.com/radix-ui/primitives/commits)

Updates `@radix-ui/react-slider` from 1.2.0 to 1.2.1
- [Changelog](https://github.com/radix-ui/primitives/blob/main/release-process.md)
- [Commits](https://github.com/radix-ui/primitives/commits)

Updates `@radix-ui/react-switch` from 1.1.0 to 1.1.1
- [Changelog](https://github.com/radix-ui/primitives/blob/main/release-process.md)
- [Commits](https://github.com/radix-ui/primitives/commits)

Updates `@radix-ui/react-tabs` from 1.1.0 to 1.1.1
- [Changelog](https://github.com/radix-ui/primitives/blob/main/release-process.md)
- [Commits](https://github.com/radix-ui/primitives/commits)

Updates `@radix-ui/react-tooltip` from 1.1.2 to 1.1.3
- [Changelog](https://github.com/radix-ui/primitives/blob/main/release-process.md)
- [Commits](https://github.com/radix-ui/primitives/commits)

---
updated-dependencies:
- dependency-name: "@radix-ui/react-accordion"
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: radix-ui
- dependency-name: "@radix-ui/react-alert-dialog"
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: radix-ui
- dependency-name: "@radix-ui/react-avatar"
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: radix-ui
- dependency-name: "@radix-ui/react-checkbox"
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: radix-ui
- dependency-name: "@radix-ui/react-collapsible"
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: radix-ui
- dependency-name: "@radix-ui/react-dialog"
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: radix-ui
- dependency-name: "@radix-ui/react-dropdown-menu"
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: radix-ui
- dependency-name: "@radix-ui/react-hover-card"
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: radix-ui
- dependency-name: "@radix-ui/react-popover"
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: radix-ui
- dependency-name: "@radix-ui/react-scroll-area"
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: radix-ui
- dependency-name: "@radix-ui/react-select"
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: radix-ui
- dependency-name: "@radix-ui/react-slider"
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: radix-ui
- dependency-name: "@radix-ui/react-switch"
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: radix-ui
- dependency-name: "@radix-ui/react-tabs"
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: radix-ui
- dependency-name: "@radix-ui/react-tooltip"
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: radix-ui
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-10-07 07:46:40 +00:00
dependabot[bot]GitHubdependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
604ff3203e chore(deps): bump js-tiktoken from 1.0.12 to 1.0.15 (#3610)
Bumps [js-tiktoken](https://github.com/dqbd/tiktoken) from 1.0.12 to 1.0.15.
- [Release notes](https://github.com/dqbd/tiktoken/releases)
- [Changelog](https://github.com/dqbd/tiktoken/blob/main/CHANGELOG.md)
- [Commits](https://github.com/dqbd/tiktoken/compare/js-tiktoken@1.0.12...js-tiktoken@1.0.15)

---
updated-dependencies:
- dependency-name: js-tiktoken
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-10-07 07:25:05 +00:00
dependabot[bot]GitHubdependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
ca8ba2f233 chore(deps): bump the observability group with 2 updates (#3608)
Bumps the observability group with 2 updates: [@appsignal/opentelemetry-instrumentation-bullmq](https://github.com/appsignal/opentelemetry-instrumentation-bullmq) and [@opentelemetry/api](https://github.com/open-telemetry/opentelemetry-js).


Updates `@appsignal/opentelemetry-instrumentation-bullmq` from 0.7.1 to 0.7.2
- [Changelog](https://github.com/appsignal/opentelemetry-instrumentation-bullmq/blob/main/CHANGELOG.md)
- [Commits](https://github.com/appsignal/opentelemetry-instrumentation-bullmq/commits/v0.7.2)

Updates `@opentelemetry/api` from 1.8.0 to 1.9.0
- [Release notes](https://github.com/open-telemetry/opentelemetry-js/releases)
- [Changelog](https://github.com/open-telemetry/opentelemetry-js/blob/main/CHANGELOG.md)
- [Commits](https://github.com/open-telemetry/opentelemetry-js/compare/v1.8.0...v1.9.0)

---
updated-dependencies:
- dependency-name: "@appsignal/opentelemetry-instrumentation-bullmq"
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: observability
- dependency-name: "@opentelemetry/api"
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: observability
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-10-07 07:10:03 +00:00
Max DeichmannandGitHub f9c5534a20 chore: remove auto merge (#3616) 2024-10-06 23:28:57 -07:00
dependabot[bot]GitHubdependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
f652c75304 chore(deps): bump @uiw/codemirror-theme-tokyo-night from 4.22.2 to 4.23.5 (#3611)
chore(deps): bump @uiw/codemirror-theme-tokyo-night

Bumps [@uiw/codemirror-theme-tokyo-night](https://github.com/uiwjs/react-codemirror) from 4.22.2 to 4.23.5.
- [Release notes](https://github.com/uiwjs/react-codemirror/releases)
- [Commits](https://github.com/uiwjs/react-codemirror/compare/v4.22.2...v4.23.5)

---
updated-dependencies:
- dependency-name: "@uiw/codemirror-theme-tokyo-night"
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-10-06 23:23:17 -07:00
dependabot[bot]GitHubdependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
879a72b821 chore(deps): bump react-hook-form from 7.51.5 to 7.53.0 (#3613)
Bumps [react-hook-form](https://github.com/react-hook-form/react-hook-form) from 7.51.5 to 7.53.0.
- [Release notes](https://github.com/react-hook-form/react-hook-form/releases)
- [Changelog](https://github.com/react-hook-form/react-hook-form/blob/master/CHANGELOG.md)
- [Commits](https://github.com/react-hook-form/react-hook-form/compare/v7.51.5...v7.53.0)

---
updated-dependencies:
- dependency-name: react-hook-form
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-10-07 06:08:35 +00:00
dependabot[bot]GitHubdependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
d3cb8469c0 chore(deps-dev): bump @playwright/test from 1.43.1 to 1.47.2 (#3612)
Bumps [@playwright/test](https://github.com/microsoft/playwright) from 1.43.1 to 1.47.2.
- [Release notes](https://github.com/microsoft/playwright/releases)
- [Commits](https://github.com/microsoft/playwright/compare/v1.43.1...v1.47.2)

---
updated-dependencies:
- dependency-name: "@playwright/test"
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-10-07 02:00:06 +00:00
dependabot[bot]GitHubdependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>Max Deichmann
b5b93cf710 chore(deps-dev): bump @types/lodash from 4.17.9 to 4.17.10 in the lodash group (#3606)
chore(deps-dev): bump @types/lodash in the lodash group

Bumps the lodash group with 1 update: [@types/lodash](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/lodash).


Updates `@types/lodash` from 4.17.9 to 4.17.10
- [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases)
- [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/lodash)

---
updated-dependencies:
- dependency-name: "@types/lodash"
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: lodash
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Max Deichmann <m.deichmann@tum.de>
2024-10-07 00:27:53 +00:00
Max DeichmannandGitHub 27caeb1536 chore: fix auto merging (#3614) 2024-10-06 17:13:13 -07:00
dependabot[bot]GitHubdependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>Max Deichmann
3b40c67aae chore(deps): bump the prisma group with 2 updates (#3604)
* chore(deps): bump the prisma group with 2 updates

Bumps the prisma group with 2 updates: [prisma](https://github.com/prisma/prisma/tree/HEAD/packages/cli) and [@prisma/client](https://github.com/prisma/prisma/tree/HEAD/packages/client).


Updates `prisma` from 5.13.0 to 5.20.0
- [Release notes](https://github.com/prisma/prisma/releases)
- [Commits](https://github.com/prisma/prisma/commits/5.20.0/packages/cli)

Updates `@prisma/client` from 5.17.0 to 5.20.0
- [Release notes](https://github.com/prisma/prisma/releases)
- [Commits](https://github.com/prisma/prisma/commits/5.20.0/packages/client)

---
updated-dependencies:
- dependency-name: prisma
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: prisma
- dependency-name: "@prisma/client"
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: prisma
...

Signed-off-by: dependabot[bot] <support@github.com>

* also upgrade docker

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Max Deichmann <m.deichmann@tum.de>
2024-10-06 16:58:13 -07:00
Max DeichmannandGitHub 79a6bc115f chore: upgrade tailwind and kysely codegen (#3601) 2024-10-06 22:40:16 +00:00
Max DeichmannandGitHub 8aa1e0e1b8 chore: upgrade svelte for ai sdk (#3600) 2024-10-06 11:16:20 -07:00
Max DeichmannandGitHub 217926b049 chore: upgrade mermaid-js (#3599) 2024-10-06 17:56:47 +00:00
dependabot[bot]GitHubdependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
8ed92de662 chore(deps): bump nodemailer and @types/nodemailer (#3596)
Bumps [nodemailer](https://github.com/nodemailer/nodemailer) and [@types/nodemailer](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/nodemailer). These dependencies needed to be updated together.

Updates `nodemailer` from 6.9.13 to 6.9.15
- [Release notes](https://github.com/nodemailer/nodemailer/releases)
- [Changelog](https://github.com/nodemailer/nodemailer/blob/master/CHANGELOG.md)
- [Commits](https://github.com/nodemailer/nodemailer/compare/v6.9.13...v6.9.15)

Updates `@types/nodemailer` from 6.4.15 to 6.4.16
- [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases)
- [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/nodemailer)

---
updated-dependencies:
- dependency-name: nodemailer
  dependency-type: direct:production
  update-type: version-update:semver-patch
- dependency-name: "@types/nodemailer"
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-10-06 10:23:27 -07:00
Max DeichmannandGitHub 2a9421674b chore: fix dependabot automerge (#3597) 2024-10-06 10:19:45 -07:00
Max DeichmannandGitHub 0b2bf8535d chore: automerge dependabot patch PRs (#3595) 2024-10-06 09:02:18 -07:00
dependabot[bot]GitHubdependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
b89e2423c3 chore(deps): bump pg and @types/pg (#3493)
Bumps [pg](https://github.com/brianc/node-postgres/tree/HEAD/packages/pg) and [@types/pg](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/pg). These dependencies needed to be updated together.

Updates `pg` from 8.11.5 to 8.13.0
- [Changelog](https://github.com/brianc/node-postgres/blob/master/CHANGELOG.md)
- [Commits](https://github.com/brianc/node-postgres/commits/pg@8.13.0/packages/pg)

Updates `@types/pg` from 8.11.6 to 8.11.10
- [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases)
- [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/pg)

---
updated-dependencies:
- dependency-name: pg
  dependency-type: direct:production
  update-type: version-update:semver-minor
- dependency-name: "@types/pg"
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-10-06 08:51:48 -07:00
Max DeichmannandGitHub 43340dd6ff chore: setup codeql manually (#3594) 2024-10-06 08:33:37 -07:00
dependabot[bot]GitHubdependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
e529d887ba chore(deps): bump @mui/x-tree-view from 7.6.2 to 7.19.0 (#3593)
Bumps [@mui/x-tree-view](https://github.com/mui/mui-x/tree/HEAD/packages/x-tree-view) from 7.6.2 to 7.19.0.
- [Release notes](https://github.com/mui/mui-x/releases)
- [Changelog](https://github.com/mui/mui-x/blob/master/CHANGELOG.md)
- [Commits](https://github.com/mui/mui-x/commits/v7.19.0/packages/x-tree-view)

---
updated-dependencies:
- dependency-name: "@mui/x-tree-view"
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-10-05 23:41:42 -07:00
dependabot[bot]GitHubdependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
d7d79df5cf chore(deps): bump @tanstack/react-table from 8.13.2 to 8.20.5 (#3523)
Bumps [@tanstack/react-table](https://github.com/TanStack/table/tree/HEAD/packages/react-table) from 8.13.2 to 8.20.5.
- [Release notes](https://github.com/TanStack/table/releases)
- [Commits](https://github.com/TanStack/table/commits/v8.20.5/packages/react-table)

---
updated-dependencies:
- dependency-name: "@tanstack/react-table"
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-10-05 08:18:00 -07:00
Vansh ChopraandGitHub 59de947865 feat: Gitlab Auth Provider (#3564) 2024-10-05 03:37:48 +02:00
Max DeichmannandGitHub c5ab61b356 chore: upgrade vitest (#3588) 2024-10-05 01:12:55 +00:00
Max DeichmannandGitHub 13db6781e2 chore: upgrade sentry (#3587) 2024-10-04 17:59:50 -07:00
Max DeichmannandGitHub 5442429f47 chore: upgrade axios (#3586) 2024-10-04 17:50:34 -07:00
Max DeichmannandGitHub c01600b2b8 chore: upgrade dd-trace (#3584) 2024-10-04 17:32:58 -07:00
Max DeichmannandGitHub 19101723b3 chore: upgrade express (#3583)
something
2024-10-04 17:30:34 -07:00
dependabot[bot]GitHubdependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
989522b22a chore(deps-dev): bump vite from 5.2.13 to 5.2.14 (#3572)
Bumps [vite](https://github.com/vitejs/vite/tree/HEAD/packages/vite) from 5.2.13 to 5.2.14.
- [Release notes](https://github.com/vitejs/vite/releases)
- [Changelog](https://github.com/vitejs/vite/blob/v5.2.14/packages/vite/CHANGELOG.md)
- [Commits](https://github.com/vitejs/vite/commits/v5.2.14/packages/vite)

---
updated-dependencies:
- dependency-name: vite
  dependency-type: direct:development
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-10-04 17:00:08 -07:00
Marc Klingen 8b4a2893d5 chore: release v2.82.0
CI/CD / lint (push) Waiting to run
CI/CD / test-docker-build (push) Waiting to run
CI/CD / tests-web (node20, pg12) (push) Waiting to run
CI/CD / tests-web (node20, pg15) (push) Waiting to run
CI/CD / tests-worker (node20, pg12) (push) Waiting to run
CI/CD / tests-worker (node20, pg15) (push) Waiting to run
CI/CD / e2e-tests (push) Waiting to run
CI/CD / e2e-server-tests (push) Waiting to run
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
2024-10-03 22:35:10 -07:00
Marc KlingenandGitHub 6719dd4158 feat: init envs via docker compose (#3568)
* feat: allow for use of init envs via docker compose

* add to docker compose build
2024-10-03 08:40:55 +00:00
Marc KlingenandGitHub 94d87f2a42 feat(ui): improve cost and latency rendering on trace tree and preview (#3567) 2024-10-03 02:06:31 +00:00
Hassieb PakzadandGitHub e06272c4df fix(prisma): HMR support (#3547) 2024-10-02 20:45:09 +02:00
Hassieb PakzadandGitHub f6512d7a8e feat(generations-table): show tokens per second (#3511) 2024-10-02 13:42:02 +00:00
Hassieb PakzadandGitHub 5e67d897b7 fix(exports): download link valid few hours only (#3560) 2024-10-02 12:31:34 +00:00
Steffen SchmitzandGitHub 9adcfde4cc chore: consolidate naming pattern for dogstatsd metrics (#3534) 2024-10-01 17:15:26 +02:00
Steffen SchmitzandGitHub bc8f8656a1 chore: disable ddtracing in favour of otel (#3461) 2024-10-01 17:14:45 +02:00
Marc KlingenandGitHub 85a327ef26 fix(ui): tag state inconsistent when navigating between traces (#3544) 2024-10-01 00:00:17 +00:00
Marc Klingen 46b20f4107 chore: release v2.81.2
CI/CD / lint (push) Waiting to run
CI/CD / test-docker-build (push) Waiting to run
CI/CD / tests-web (node20, pg12) (push) Waiting to run
CI/CD / tests-web (node20, pg15) (push) Waiting to run
CI/CD / tests-worker (node20, pg12) (push) Waiting to run
CI/CD / tests-worker (node20, pg15) (push) Waiting to run
CI/CD / e2e-tests (push) Waiting to run
CI/CD / e2e-server-tests (push) Waiting to run
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
2024-09-30 16:23:40 -07:00
Marc KlingenandGitHub fb8f26a00e feat(ee): add customizable default model adapter base urls (#3542) 2024-09-30 23:22:45 +00:00
Marc KlingenandGitHub 3d7ba475e2 chore(auth): reduce maxAge of password reset link to 10 minutes (#3541) 2024-09-30 21:40:18 +00:00
Marc KlingenandGitHub 03028a92b4 chore: upgrade fern openapi generator and cli (#3536) 2024-09-30 18:18:53 +02:00
Marc Klingen 8a96290f03 chore: release v2.81.1
CI/CD / lint (push) Waiting to run
CI/CD / test-docker-build (push) Waiting to run
CI/CD / tests-web (node20, pg12) (push) Waiting to run
CI/CD / tests-web (node20, pg15) (push) Waiting to run
CI/CD / tests-worker (node20, pg12) (push) Waiting to run
CI/CD / tests-worker (node20, pg15) (push) Waiting to run
CI/CD / e2e-tests (push) Waiting to run
CI/CD / e2e-server-tests (push) Waiting to run
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
2024-09-27 17:05:22 +02:00
Marc KlingenandGitHub bca09bdefe fix(security): only allow redirect via target to internal urls (#3512) 2024-09-27 16:52:58 +02:00
Steffen SchmitzandGitHub 2d314250e9 chore: make credentials for export bucket optional (#3509) 2024-09-27 16:41:54 +02:00
Marc KlingenandGitHub 4a87f402ec fix: favicon reference when using custom basepath (#3506) 2024-09-27 13:10:21 +00:00
dependabot[bot]GitHubdependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
800b2136b9 chore(deps-dev): bump @types/lodash from 4.17.7 to 4.17.9 in the lodash group (#3492)
chore(deps-dev): bump @types/lodash in the lodash group

Bumps the lodash group with 1 update: [@types/lodash](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/lodash).


Updates `@types/lodash` from 4.17.7 to 4.17.9
- [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases)
- [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/lodash)

---
updated-dependencies:
- dependency-name: "@types/lodash"
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: lodash
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-09-27 10:25:35 +02:00
Steffen SchmitzandGitHub 278df5ab3c feat: optionally upload all incoming events to S3 (#3488) 2024-09-27 06:58:34 +00:00
Marc KlingenandGitHub c7f9e18768 fix(ui): include non-generation warnings/error in rolled-up trace status (#3494) 2024-09-27 00:31:53 +00:00
Max DeichmannandGitHub 2121b40cca security: upgrade snyk (#3482) 2024-09-25 23:10:57 +02:00
Max DeichmannandGitHub 5ba440f2de chore: rate limit legacy endpoints (#3473) 2024-09-25 12:58:13 +00:00
Max DeichmannandGitHub 657a3275de chore: mark admin api as internal and cloud only (#3464) 2024-09-24 12:47:47 +00:00
Steffen SchmitzandGitHub 56bb9c8dcf chore: remove porter configs from CI (#3463) 2024-09-24 11:36:06 +00:00
Steffen SchmitzandGitHub 66e15bcf0d chore: drop redundant indexes on postgres (#3377) 2024-09-24 11:35:04 +02:00
Marc KlingenandGitHub 5f94ca8e82 fix: skip entitlement check for instance admins (#3452) 2024-09-23 16:55:06 +02:00
Steffen SchmitzandGitHub e8561fb105 chore: move web-ee logic from console.log to logger (#3400) 2024-09-23 16:54:45 +02:00
marliessophieandGitHub 18e84c6498 fix(ui_markdown): whitelist images with valid ipv6 or ipv4 hostname (#3444) 2024-09-23 14:12:37 +00:00
Marc KlingenandGitHub 05152ac8d6 fix(auth): email/password sign-up with custom base-path (#3450)
fix: email/password sign-up with custom base-path
2024-09-23 13:48:59 +00:00
marliessophieandGitHub 6cb9e24ba2 chore(ui): support larger step sizes in Lanfguse Trace Timeline View (#3449) 2024-09-23 12:53:33 +00:00
Marc Klingen 484fa424fb docs: update comments on stripe util functions 2024-09-23 14:18:37 +02:00
Hassieb Pakzad 555aebe8c7 chore: release v2.81.0
CI/CD / lint (push) Waiting to run
CI/CD / test-docker-build (push) Waiting to run
CI/CD / tests-web (node20, pg12) (push) Waiting to run
CI/CD / tests-web (node20, pg15) (push) Waiting to run
CI/CD / tests-worker (node20, pg12) (push) Waiting to run
CI/CD / tests-worker (node20, pg15) (push) Waiting to run
CI/CD / e2e-tests (push) Waiting to run
CI/CD / e2e-server-tests (push) Waiting to run
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
2024-09-23 13:59:21 +02:00
Max DeichmannandGitHub af29424b48 feat: delete API keys via API (#3448) 2024-09-23 12:30:41 +02:00
Max DeichmannandGitHub 42f1c63bbf chore: set sentry config on build time (#3445) 2024-09-23 11:27:15 +02:00
Hassieb PakzadandGitHub 663ded0743 chore(evals): move to tool calling instead of function calling (#3430) 2024-09-23 11:08:23 +02:00
marliessophieandGitHub d7ced99d16 feat(ui): export generations async on cloud (#3368) 2024-09-23 06:03:03 +00:00
Max DeichmannandGitHub c979cae620 chore: add worker logs for tokenization (#3440) 2024-09-21 16:07:18 +00:00
marliessophieandGitHub c483293b10 style(prompts): display all prompt tags on versions, not just on hover (#3422) 2024-09-20 16:33:10 +00:00
Steffen SchmitzandGitHub 932913cd76 fix: use actual singletons for db clients (#3427) 2024-09-20 13:27:46 +02:00
Steffen SchmitzandGitHub 5875537132 fix: keep more failed jobs in bullmq (#3424) 2024-09-20 13:26:14 +02:00
Marc KlingenandGitHub 1a53981a7a docs: add comment on max aspect ratio of custom logo (#3426) 2024-09-20 09:48:25 +00:00
Hassieb PakzadandGitHub 0e6b676b19 fix(playground): parse complex messages when jumping to playground (#3425) 2024-09-20 11:03:49 +02:00
Marc KlingenandGitHub 955e25555f fix(ui): performance of optimistic updates of bookmarked traces (#3418) 2024-09-19 22:20:11 +00:00
Marc KlingenandGitHub 2a11f1e8fb fix(ui): truncate long model names in dashboard (#3417) 2024-09-19 21:40:54 +00:00
Marc KlingenandGitHub 143a461f0c chore(ui): add link to support and tracing faq to onboarding (#3416) 2024-09-19 19:58:53 +00:00
Max DeichmannandGitHub c8544690f7 chore: remove event io from dd traces (#3413) 2024-09-19 18:06:00 +00:00
Marc Klingen 05a533dd7e chore: release v2.80.0
CI/CD / lint (push) Waiting to run
CI/CD / test-docker-build (push) Waiting to run
CI/CD / tests-web (node20, pg12) (push) Waiting to run
CI/CD / tests-web (node20, pg15) (push) Waiting to run
CI/CD / tests-worker (node20, pg12) (push) Waiting to run
CI/CD / tests-worker (node20, pg15) (push) Waiting to run
CI/CD / e2e-tests (push) Waiting to run
CI/CD / e2e-server-tests (push) Waiting to run
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
2024-09-19 16:37:23 +02:00
Steffen SchmitzandGitHub f3d15bb36e feat: optimize daily metrics query (#3371) 2024-09-19 14:10:10 +00:00
Hassieb PakzadandGitHub 24c321f632 fix(dataset-table): cost and latency queries (#3411) 2024-09-19 12:14:42 +00:00
Steffen SchmitzandGitHub 0e9cf548b2 chore: ecs production deploy automation (#3408) 2024-09-19 12:42:21 +02:00
Marc KlingenandGitHub 7158a41fe9 feat(ee): add additional logo (#3404) 2024-09-19 11:45:53 +02:00
Max DeichmannandGitHub bc384a525a chore: adjust sentry sample rate (#3403)
adjust autoscaling eval periods
2024-09-18 22:40:22 +00:00
Max DeichmannandGitHub 3f70828df6 chore: fix dev server speed (#3388) 2024-09-18 17:10:50 +00:00
marliessophieandGitHub 0988635427 fix: prompt tag menu disappears when out of focus (#3394) 2024-09-18 18:39:55 +02:00
Max DeichmannandGitHub 41ed6fed5e fix: allow to eval with azure models (#3387) 2024-09-17 16:54:22 +00:00
Hassieb PakzadandGitHub eb40659e44 fix(playground): save to prompt list (#3384) 2024-09-17 13:54:39 +00:00
Steffen SchmitzandGitHub a22bcb1188 build: add deploy config for eu (#3366) 2024-09-17 10:04:46 +00:00
Marc KlingenandGitHub 392fbe6c8c chore(ui): improve posthog api key error message (#3373)
* chore(ui): improve posthog api key error message

* Empty commit
2024-09-16 21:58:06 +00:00
marliessophieandGitHub 48d5a8f5a1 fix: cutoff filter for sessions export (#3370) 2024-09-16 15:11:32 +00:00
marliessophieandGitHub 44428870f7 style: adjust success toast for dark mode (#3362) 2024-09-16 07:46:10 +00:00
Hassieb PakzadandGitHub 724a4f5d9a fix(ui/prompts): variable parsing for chat prompts (#3360) 2024-09-15 13:55:01 +00:00
Marc KlingenandGitHub 2358264b66 fix(auth): unknown argument refresh_token_expires_in with Github connection (#3356)
fix(auth): unknown argument `refresh_token_expires_in` with Github connection

Fixes langfuse/langfuse#3355
2024-09-13 19:10:44 +00:00
Marc Klingen 65e3b4c5ca chore: release v2.79.0
CI/CD / lint (push) Waiting to run
CI/CD / test-docker-build (push) Waiting to run
CI/CD / tests-web (node20, pg12) (push) Waiting to run
CI/CD / tests-web (node20, pg15) (push) Waiting to run
CI/CD / tests-worker (node20, pg12) (push) Waiting to run
CI/CD / tests-worker (node20, pg15) (push) Waiting to run
CI/CD / e2e-tests (push) Waiting to run
CI/CD / e2e-server-tests (push) Waiting to run
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
2024-09-13 20:37:58 +02:00
62c413faf5 fix(ui): ensure table footer is fixed on the bottom of the page (#3354)
* style(tables): ensure footer is fixed on the bottom

* fix

---------

Co-authored-by: Marc Klingen <git@marcklingen.com>
2024-09-13 20:13:36 +02:00
Marc KlingenandGitHub 8ea14ac537 feat(cloud): alert when free plan limit is approaching or reached (#3350)
* feat(cloud): alert when free plan limit is approaching or reached

* check entitlement
2024-09-13 20:08:32 +02:00
marliessophieandGitHub c32b84663c feat(ui): export traces (#3332) 2024-09-13 17:51:22 +00:00
Marc KlingenandGitHub 23ab79ba42 fix: custom base path nullable (#3353) 2024-09-13 17:17:13 +00:00
Hassieb PakzadandGitHub 6ffec3337f feat(playground): add support for OpenAI o1 beta models (#3349) 2024-09-13 18:47:57 +02:00
steffen911 34a2a9cddf chore: remove pipeline concurrency limiter 2024-09-13 16:24:24 +02:00
Steffen SchmitzandGitHub 0854429850 feat: add env variables to toggle queue consumers (#3339) 2024-09-13 14:16:48 +00:00
Steffen SchmitzandGitHub 05fd690486 chore: deploy to staging ECS environment (#3327) 2024-09-13 13:49:27 +00:00
marliessophieandGitHub bc3f39272b style(evals): apply sticky headers to eval logs and eval config tables (#3347) 2024-09-13 13:27:22 +00:00
Marc KlingenandGitHub 96ace71f9a feat(self-host): support for custom base path via NEXT_PUBLIC_BASE_PATH (#3346) 2024-09-13 13:02:36 +00:00
marliessophieandGitHub 70415dcab5 fix: Time to first token filter in Generations table (#3345) 2024-09-13 11:53:33 +00:00
Hassieb PakzadandGitHub 6a18f249aa feat(cost-tracking): add OpenAI o1 models (#3343) 2024-09-13 12:00:10 +02:00
Marc KlingenandGitHub cc60d506a7 feat(cloud): improve upgrade ui (#3331)
* add link to billing settings to project settings

* add plan label to org switcher

* add upgrade menu item on free cloud plans
2024-09-12 16:49:49 +00:00
Steffen SchmitzandGitHub a31a7f238e chore: remove db query from healthcheck (#3330) 2024-09-12 17:24:40 +02:00
Marc KlingenandGitHub 41ebc77f86 feat(cloud): improve crisp integration (#3329)
* chore: update crisp sdk

* feat(cloud): improve crisp integration
2024-09-12 14:33:51 +00:00
marliessophieandGitHub cdc41cf573 chore(ui_markdown): whitelist images with valid ipv6 or ipv4 hostname (#3318) 2024-09-12 08:04:44 +00:00
Max DeichmannandGitHub c86e32f756 chore: improve client side sentry (#3312) 2024-09-12 09:19:07 +02:00
Max DeichmannandGitHub 85576c162a chore: downgrade codegen (#3319) 2024-09-11 21:58:05 +02:00
Max DeichmannandGitHub 93ad1e6880 perf: get observation I/O only if required (#3316) 2024-09-11 18:47:31 +00:00
marliessophieandGitHub 97b17fc8bb style(ui): fix hover effect on groups in column reordering (#3317) 2024-09-11 18:22:23 +00:00
marliessophieandGitHub 04e2d9249f fix(ui): populate default prompt data for chat prompts with custom role (#3295) 2024-09-11 12:36:59 +00:00
marliessophieandGitHub cb94a54f0c feat(ui): type to find column name in filter builder (#3290) 2024-09-11 08:31:18 +00:00
marliessophieandGitHub 7c90bbed0c fix(ui): persist dateRange filter state of dashboard (#3298) 2024-09-11 08:11:21 +00:00
Max DeichmannandGitHub f133526ba6 chore: create dependabot group for express (#3303)
fix
2024-09-11 09:24:45 +02:00
dependabot[bot]GitHubdependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
dc014de54c chore(deps-dev): bump kysely-codegen from 0.11.0 to 0.16.6 (#3301)
Bumps [kysely-codegen](https://github.com/RobinBlomberg/kysely-codegen) from 0.11.0 to 0.16.6.
- [Release notes](https://github.com/RobinBlomberg/kysely-codegen/releases)
- [Commits](https://github.com/RobinBlomberg/kysely-codegen/commits)

---
updated-dependencies:
- dependency-name: kysely-codegen
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-09-11 09:12:04 +02:00
dependabot[bot]GitHubdependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
c6beec1ad5 chore(deps): bump @aws-sdk/lib-storage from 3.614.0 to 3.645.0 (#3235)
Bumps [@aws-sdk/lib-storage](https://github.com/aws/aws-sdk-js-v3/tree/HEAD/lib/lib-storage) from 3.614.0 to 3.645.0.
- [Release notes](https://github.com/aws/aws-sdk-js-v3/releases)
- [Changelog](https://github.com/aws/aws-sdk-js-v3/blob/main/lib/lib-storage/CHANGELOG.md)
- [Commits](https://github.com/aws/aws-sdk-js-v3/commits/v3.645.0/lib/lib-storage)

---
updated-dependencies:
- dependency-name: "@aws-sdk/lib-storage"
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-09-10 23:17:43 +02:00
dependabot[bot]GitHubdependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
a810f7750f chore(deps): bump posthog-js from 1.154.6 to 1.161.3 (#3297)
Bumps [posthog-js](https://github.com/PostHog/posthog-js) from 1.154.6 to 1.161.3.
- [Release notes](https://github.com/PostHog/posthog-js/releases)
- [Changelog](https://github.com/PostHog/posthog-js/blob/main/CHANGELOG.md)
- [Commits](https://github.com/PostHog/posthog-js/compare/v1.154.6...v1.161.3)

---
updated-dependencies:
- dependency-name: posthog-js
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-09-10 20:02:47 +02:00
marliessophieandGitHub 2b34c82e98 feat(ui): render 0 latency as such in trace timeline view (#3288) 2024-09-10 13:10:24 +00:00
marliessophieandGitHub fe283284ef feat(ui): move columns via drag and drop (#3266) 2024-09-10 12:40:59 +00:00
Steffen SchmitzandGitHub 307f3d18ce chore: add trace info to web logs and context to queue events (#3282) 2024-09-10 12:26:07 +00:00
Hassieb PakzadandGitHub 46fff7148e fix(ui/prompts): parse prompt variables correctly (#3286) 2024-09-10 12:05:52 +02:00
Steffen SchmitzandGitHub 9cce111be6 chore: add worker entrypoint to handle alternative database config (#3283) 2024-09-10 10:58:50 +02:00
Max DeichmannandGitHub dbd7f810c1 chore: remove sentry tunnel (#3274) 2024-09-09 17:22:53 +02:00
Max DeichmannandGitHub abc3ea2b5a chore: remove explicit profiling settings worker (#3272)
fix
2024-09-09 14:44:51 +00:00
Steffen SchmitzandGitHub 3d8a4f9d75 chore: downgrade winston instrumentation and fix worker import (#3268) 2024-09-09 13:35:05 +02:00
Max DeichmannandGitHub 7b4bef7c2e fix: revert async zod (#3264) 2024-09-07 09:24:59 +02:00
Marc KlingenandGitHub b10353115b fix(ui): x axis of timeseries charts on dashboard for some time ranges (#3261) 2024-09-06 20:30:58 +00:00
Marc KlingenandGitHub f3235ceccb fix(ui): overflow of long strings in sessions view (#3258) 2024-09-06 17:20:11 +00:00
Marc KlingenandGitHub 92455b4e5a fix(ui): reset feedback form after submit (#3257)
fix(ui): reset feedback form after successfully sending feedback
2024-09-06 16:49:49 +00:00
Max DeichmannandGitHub 217b92d548 perf: parse zod in ingestion pipeline async (#3253) 2024-09-06 16:10:23 +02:00
Marc KlingenandGitHub c6ddd3822f feat(users): add search to users table (#3251) 2024-09-06 01:14:31 +00:00
Marc KlingenandGitHub 67d6c32ec5 chore: Update README.md 2024-09-06 01:50:29 +02:00
Marc KlingenandGitHub d89379e693 chore: Update README.md 2024-09-06 01:48:47 +02:00
Marc KlingenandGitHub 4b8d3a764e chore: Update README.md 2024-09-06 01:26:09 +02:00
Marc Klingen 5757fa6e38 chore: update readme 2024-09-06 00:58:47 +02:00
Marc KlingenandGitHub 34c3e5af08 chore: Update README.md 2024-09-06 00:56:16 +02:00
Steffen SchmitzandGitHub c68ec2fca0 chore: move additional logs to debug level (#3250) 2024-09-05 18:44:18 +00:00
Max DeichmannandGitHub 03dda153e8 chore: remove log line (#3249) 2024-09-05 16:28:33 +00:00
Marc Klingen 63641521d8 chore: add langfuse.com and langfuse.dev to csp headers 2024-09-05 16:13:37 +02:00
Marc Klingen 29d11642c4 chore: update csp headers to include ph.langfuse.dev 2024-09-05 15:52:29 +02:00
Steffen SchmitzandGitHub fb98a404d2 chore: adjust log level for queue infos (#3248) 2024-09-05 14:14:56 +02:00
Max DeichmannandGitHub 032fce56c4 chore: upgrade fern (#3247) 2024-09-05 11:30:10 +00:00
Max DeichmannandGitHub 518b8df2da fix: also track langfuse SDK req headers with underscores on span (#2972) 2024-09-05 11:00:13 +00:00
Max DeichmannandGitHub 70761f7ab0 chore: improve log level (#3244) 2024-09-05 09:01:22 +00:00
Max DeichmannandGitHub a85d4ccf07 chore: improve zod monitoring in ingestion pipeline (#3239) 2024-09-05 08:47:17 +00:00
Steffen SchmitzandGitHub 1b42eae7dd chore: move createAuthedApiRoute logging into metadata (#3238) 2024-09-05 09:09:07 +02:00
275 changed files with 13581 additions and 7546 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
ignore-words-list = afterall,vertx,notIn
+22 -5
View File
@@ -34,11 +34,25 @@ EMAIL_FROM_ADDRESS="" # Defines the email address to use as the from address.
SMTP_CONNECTION_URL="" # Defines the connection url for smtp server.
# S3 storage
S3_ENDPOINT=
S3_ACCESS_KEY_ID=
S3_SECRET_ACCESS_KEY=
S3_BUCKET_NAME=
S3_REGION=
S3_ENDPOINT=http://localhost:9090
S3_ACCESS_KEY_ID=minio
S3_SECRET_ACCESS_KEY=miniosecret
S3_BUCKET_NAME=mybucket
S3_REGION=us-east-1
## Necessary for minio compatibility
S3_FORCE_PATH_STYLE=true
# S3 Event Bucket Upload
## Set to true to test uploading all events to S3
LANGFUSE_S3_EVENT_UPLOAD_ENABLED=false
LANGFUSE_S3_EVENT_UPLOAD_BUCKET=mybucket
LANGFUSE_S3_EVENT_UPLOAD_ACCESS_KEY_ID=minio
LANGFUSE_S3_EVENT_UPLOAD_SECRET_ACCESS_KEY=miniosecret
LANGFUSE_S3_EVENT_UPLOAD_REGION=us-east-1
LANGFUSE_S3_EVENT_UPLOAD_ENDPOINT=http://localhost:9090
## Necessary for minio compatibility
LANGFUSE_S3_EVENT_UPLOAD_FORCE_PATH_STYLE=true
LANGFUSE_S3_EVENT_UPLOAD_PREFIX=events/
# Set during docker build of application
# Used to disable environment verification at build time
@@ -51,3 +65,6 @@ REDIS_AUTH="myredissecret"
LANGFUSE_WORKER_PASSWORD=mybasicauthsecret
# openssl rand -hex 32 used only here
ENCRYPTION_KEY=0000000000000000000000000000000000000000000000000000000000000000
# speeds up local development by not executing init scripts on server startup
NEXT_PUBLIC_LANGFUSE_RUN_NEXT_INIT="false"
+29
View File
@@ -28,11 +28,18 @@ ENCRYPTION_KEY="0000000000000000000000000000000000000000000000000000000000000000
# Use CSP headers to enforce HTTPS, optional
# LANGFUSE_CSP_ENFORCE_HTTPS="true"
# Configure base path for self-hosting, optional
# Note: You need to build the docker image with the base path set and cannot use the pre-built docker image if you set this.
# NEXT_PUBLIC_BASE_PATH="/app"
# Docker only, optional
# PORT=3000
# HOSTNAME=localhost
# Opentelemetry, optional
OTEL_EXPORTER_OTLP_ENDPOINT="http://localhost:4318"
OTEL_SERVICE_NAME="langfuse"
# Default role for users who sign up, optional, can be org or org+project
# LANGFUSE_DEFAULT_ORG_ID=
# LANGFUSE_DEFAULT_ORG_ROLE=
@@ -60,6 +67,10 @@ ENCRYPTION_KEY="0000000000000000000000000000000000000000000000000000000000000000
# AUTH_GITHUB_CLIENT_ID=
# AUTH_GITHUB_CLIENT_SECRET=
# AUTH_GITHUB_ALLOW_ACCOUNT_LINKING=false
# AUTH_GITLAB_CLIENT_ID=
# AUTH_GITLAB_CLIENT_SECRET=
# AUTH_GITLAB_ALLOW_ACCOUNT_LINKING=false
# AUTH_GITLAB_ISSUER=
# AUTH_AZURE_AD_CLIENT_ID=
# AUTH_AZURE_AD_CLIENT_SECRET=
# AUTH_AZURE_AD_TENANT_ID=
@@ -97,6 +108,18 @@ ENCRYPTION_KEY="0000000000000000000000000000000000000000000000000000000000000000
# S3_REGION=
# BATCH_EXPORT_DOWNLOAD_LINK_EXPIRATION_HOURS=
# S3 storage for events, optional, used to persist all incoming events
# LANGFUSE_S3_EVENT_UPLOAD_ENABLED="true"
# LANGFUSE_S3_EVENT_UPLOAD_BUCKET=
# Optional prefix to be used within the bucket. Must end with `/` if set
# LANGFUSE_S3_EVENT_UPLOAD_PREFIX=events/
# The following four options are optional and fallback to the normal SDK credential provider chain if omitted
# See https://docs.aws.amazon.com/sdk-for-javascript/v3/developer-guide/setting-credentials-node.html
# LANGFUSE_S3_EVENT_UPLOAD_ENDPOINT=
# LANGFUSE_S3_EVENT_UPLOAD_REGION=
# LANGFUSE_S3_EVENT_UPLOAD_ACCESS_KEY_ID=
# LANGFUSE_S3_EVENT_UPLOAD_SECRET_ACCESS_KEY=
# Exports are streamed to S3 in pages to avoid memory issues
# The page size can be adjusted if needed to optimize performance
# DB_EXPORT_PAGE_SIZE=1000
@@ -124,6 +147,12 @@ ENCRYPTION_KEY="0000000000000000000000000000000000000000000000000000000000000000
# LANGFUSE_UI_DOCUMENTATION_HREF=https://docs.example.com
# LANGFUSE_UI_SUPPORT_HREF=https://support.example.com
# LANGFUSE_UI_FEEDBACK_HREF=https://feedback.example.com
# LANGFUSE_UI_LOGO_LIGHT_MODE_HREF=https://static.langfuse.com/langfuse-dev/example-logo-light-mode.png
# LANGFUSE_UI_LOGO_DARK_MODE_HREF=https://static.langfuse.com/langfuse-dev/example-logo-dark-mode.png
# LANGFUSE_UI_DEFAULT_MODEL_ADAPTER=Anthropic # OpenAI, Anthropic, Azure
# LANGFUSE_UI_DEFAULT_BASE_URL_OPENAI=https://api.openai.com/v1
# LANGFUSE_UI_DEFAULT_BASE_URL_ANTHROPIC=https://api.anthropic.com
# LANGFUSE_UI_DEFAULT_BASE_URL_AZURE_OPENAI=https://{instanceName}.openai.azure.com/openai/deployments
### END Enterprise Edition Configuration
+4
View File
@@ -32,6 +32,10 @@ updates:
patterns:
- "lodash"
- "@types/lodash"
express:
patterns:
- "express"
- "@types/express"
observability:
patterns:
- "dd-trace"
+75
View File
@@ -0,0 +1,75 @@
name: WorkflowCall - Deploy to ECS Service
on:
workflow_call:
inputs:
environment:
type: string
description: Deployment environment
required: true
service:
type: string
description: Name of the service to be deployed, e.g. web-ingestion, web, or worker.
required: true
jobs:
ecs-deploy:
runs-on: ubuntu-latest
environment: ${{ inputs.environment }}
steps:
- name: Get app name
uses: winterjung/split@v2
id: split
with:
msg: ${{ inputs.service }}
separator: "-"
- name: Checkout code
uses: actions/checkout@v4
- name: Authenticate with AWS
# GitHub/AWS recommend to use OIDC here: https://github.com/aws-actions/configure-aws-credentials?tab=readme-ov-file#oidc
# Probably more painful to configure, but would remove all long-lived credentials.
uses: aws-actions/configure-aws-credentials@v4
with:
aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
aws-region: ${{ vars.AWS_REGION }}
- name: Login to AWS ECR
id: login-ecr
uses: aws-actions/amazon-ecr-login@v2
- name: Build, tag, and push Docker image
env:
REGISTRY: ${{ steps.login-ecr.outputs.registry }}
REPOSITORY: ${{ steps.split.outputs._0 }}
IMAGE_TAG: ${{ github.sha }}
run: |
docker build \
-t $REGISTRY/$REPOSITORY:$IMAGE_TAG \
-f ./${{ steps.split.outputs._0 }}/Dockerfile \
--build-arg NEXT_PUBLIC_LANGFUSE_CLOUD_REGION=${{ vars.NEXT_PUBLIC_LANGFUSE_CLOUD_REGION }} \
--build-arg NEXT_LANGFUSE_TRACING_SAMPLE_RATE=${{ vars.NEXT_LANGFUSE_TRACING_SAMPLE_RATE }} \
--build-arg NEXT_PUBLIC_SENTRY_ENVIRONMENT=${{ vars.NEXT_PUBLIC_SENTRY_ENVIRONMENT }} \
--build-arg NEXT_PUBLIC_DEMO_ORG_ID=${{ vars.NEXT_PUBLIC_DEMO_ORG_ID }} \
--build-arg NEXT_PUBLIC_DEMO_PROJECT_ID=${{ vars.NEXT_PUBLIC_DEMO_PROJECT_ID }} \
--build-arg NEXT_PUBLIC_SENTRY_DSN=${{ vars.NEXT_PUBLIC_SENTRY_DSN }} \
--build-arg NEXT_PUBLIC_BUILD_ID=${{ github.sha }} \
--build-arg NEXT_PUBLIC_POSTHOG_KEY=${{ vars.NEXT_PUBLIC_POSTHOG_KEY }} \
--build-arg NEXT_PUBLIC_POSTHOG_HOST=${{ vars.NEXT_PUBLIC_POSTHOG_HOST }} \
--build-arg NEXT_PUBLIC_CRISP_WEBSITE_ID=${{ vars.NEXT_PUBLIC_CRISP_WEBSITE_ID }} \
--build-arg SENTRY_AUTH_TOKEN=${{ secrets.SENTRY_AUTH_TOKEN }} \
--build-arg SENTRY_ORG=${{ vars.SENTRY_ORG }} \
--build-arg SENTRY_PROJECT=${{ vars.SENTRY_PROJECT }} \
.
docker push $REGISTRY/$REPOSITORY:$IMAGE_TAG
- name: Render AWS ECS Task Definition
id: render-task-definition
uses: aws-actions/amazon-ecs-render-task-definition@v1
with:
container-name: ${{ inputs.service }}
image: ${{ steps.login-ecr.outputs.registry }}/${{ steps.split.outputs._0 }}:${{ github.sha }}
task-definition-family: ${{ inputs.environment }}-${{ inputs.service }}
- name: Update AWS ECS Service
uses: aws-actions/amazon-ecs-deploy-task-definition@v2
with:
task-definition: ${{ steps.render-task-definition.outputs.task-definition }}
service: ${{ inputs.environment }}-${{ inputs.service }}
cluster: ${{ inputs.environment }}-cluster
wait-for-service-stability: true
+2 -2
View File
@@ -22,7 +22,7 @@ jobs:
steps:
- name: Checkout repo
uses: actions/checkout@v3
uses: actions/checkout@v4
- name: Start containers
run: docker compose -f "docker-compose.yml" up -d --build
@@ -31,7 +31,7 @@ jobs:
uses: pnpm/action-setup@v2.2.4
- name: Setup Node 18
uses: actions/setup-node@v3
uses: actions/setup-node@v4
with:
node-version: 18
+92
View File
@@ -0,0 +1,92 @@
# For most projects, this workflow file will not need changing; you simply need
# to commit it to your repository.
#
# You may wish to alter this file to override the set of languages analyzed,
# or to provide custom queries or build logic.
#
# ******** NOTE ********
# We have attempted to detect the languages in your repository. Please check
# the `language` matrix defined below to confirm you have the correct set of
# supported CodeQL languages.
#
name: "CodeQL"
on:
push:
branches: ["main", "production", "v*"]
pull_request:
branches: ["main", "production", "v*"]
schedule:
- cron: "31 13 * * 0"
jobs:
analyze:
name: Analyze (${{ matrix.language }})
# Runner size impacts CodeQL analysis time. To learn more, please see:
# - https://gh.io/recommended-hardware-resources-for-running-codeql
# - https://gh.io/supported-runners-and-hardware-resources
# - https://gh.io/using-larger-runners (GitHub.com only)
# Consider using larger runners or machines with greater resources for possible analysis time improvements.
runs-on: ${{ (matrix.language == 'swift' && 'macos-latest') || 'ubuntu-latest' }}
permissions:
# required for all workflows
security-events: write
# required to fetch internal or private CodeQL packs
packages: read
# only required for workflows in private repositories
actions: read
contents: read
strategy:
fail-fast: false
matrix:
include:
- language: javascript-typescript
build-mode: none
# CodeQL supports the following values keywords for 'language': 'c-cpp', 'csharp', 'go', 'java-kotlin', 'javascript-typescript', 'python', 'ruby', 'swift'
# Use `c-cpp` to analyze code written in C, C++ or both
# Use 'java-kotlin' to analyze code written in Java, Kotlin or both
# Use 'javascript-typescript' to analyze code written in JavaScript, TypeScript or both
# To learn more about changing the languages that are analyzed or customizing the build mode for your analysis,
# see https://docs.github.com/en/code-security/code-scanning/creating-an-advanced-setup-for-code-scanning/customizing-your-advanced-setup-for-code-scanning.
# If you are analyzing a compiled language, you can modify the 'build-mode' for that language to customize how
# your codebase is analyzed, see https://docs.github.com/en/code-security/code-scanning/creating-an-advanced-setup-for-code-scanning/codeql-code-scanning-for-compiled-languages
steps:
- name: Checkout repository
uses: actions/checkout@v4
# Initializes the CodeQL tools for scanning.
- name: Initialize CodeQL
uses: github/codeql-action/init@v3
with:
languages: ${{ matrix.language }}
build-mode: ${{ matrix.build-mode }}
# If you wish to specify custom queries, you can do so here or in a config file.
# By default, queries listed here will override any specified in a config file.
# Prefix the list here with "+" to use these queries and those in the config file.
# For more details on CodeQL's query packs, refer to: https://docs.github.com/en/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/configuring-code-scanning#using-queries-in-ql-packs
# queries: security-extended,security-and-quality
# If the analyze step fails for one of the languages you are analyzing with
# "We were unable to automatically build your code", modify the matrix above
# to set the build mode to "manual" for that language. Then modify this step
# to build your code.
# ️ Command-line programs to run using the OS shell.
# 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun
- if: matrix.build-mode == 'manual'
shell: bash
run: |
echo 'If you are using a "manual" build mode for one or more of the' \
'languages you are analyzing, replace this with the commands to build' \
'your code, for example:'
echo ' make bootstrap'
echo ' make release'
exit 1
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v3
with:
category: "/language:${{matrix.language}}"
+105
View File
@@ -0,0 +1,105 @@
on:
push:
branches:
- main
- production
workflow_dispatch:
inputs:
service:
description: 'Service to be deployed'
type: choice
options:
- all
- web
- web-ingestion
- worker
required: true
environment:
description: 'Environment to deploy to'
type: choice
options:
- staging
- prod-eu-temp
- prod-us
required: true
concurrency:
# Support concurrent `push` and `workflow_dispatch`` actions
group: deploy-${{ github.event_name }}-${{ github.ref }}
cancel-in-progress: true
name: Deploy to ECS
jobs:
affected-services:
runs-on: ubuntu-latest
outputs:
services: ${{ steps.affected-services.outputs.result }}
steps:
- name: Get affected services
id: affected-services
uses: actions/github-script@v7
with:
script: |
if (context.eventName === "workflow_dispatch") {
if (context.payload.inputs.service === "all") {
return `["web", "web-ingestion", "worker"]`
}
return `["${context.payload.inputs.service}"]`
}
if (context.eventName === "push") {
return `["web", "web-ingestion", "worker"]`
}
return "[]"
result-encoding: string
- name: Print services to build
uses: actions/github-script@v7
env:
services: ${{ steps.affected-services.outputs.result }}
with:
result-encoding: string
script: |
console.log('Services', `${process.env.services}` ?? 'n/a');
affected-environments:
runs-on: ubuntu-latest
outputs:
environments: ${{ steps.affected-environments.outputs.result }}
steps:
- name: Get affected environments
id: affected-environments
uses: actions/github-script@v7
with:
script: |
if (context.eventName === "workflow_dispatch") {
return `["${context.payload.inputs.environment}"]`
}
if (context.eventName === "push") {
if (context.ref === "refs/heads/main") {
return `["staging"]`
}
if (context.ref === "refs/heads/production") {
return `["prod-eu-temp", "prod-us"]`
}
}
return "[]"
result-encoding: string
- name: Print environments to build
uses: actions/github-script@v7
env:
environments: ${{ steps.affected-environments.outputs.result }}
with:
result-encoding: string
script: |
console.log('Environments', `${process.env.environments}` ?? 'n/a');
ecs-deploy:
uses: ./.github/workflows/_deploy_ecs_service.yml
needs: [affected-services, affected-environments]
secrets: inherit
strategy:
matrix:
service: ${{ fromJson(needs.affected-services.outputs.services) }}
environment: ${{ fromJson(needs.affected-environments.outputs.environments) }}
with:
service: ${{ matrix.service }}
environment: ${{ matrix.environment }}
+2 -2
View File
@@ -14,10 +14,10 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v2
uses: actions/checkout@v4
- name: Setup node
uses: actions/setup-node@v2
uses: actions/setup-node@v4
with:
node-version: 18
+23 -26
View File
@@ -16,11 +16,11 @@ jobs:
lint:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
- uses: pnpm/action-setup@v3
with:
version: 9.5.0
- uses: actions/setup-node@v3
- uses: actions/setup-node@v4
with:
node-version: 20
cache: "pnpm"
@@ -35,10 +35,11 @@ jobs:
run: pnpm run lint
test-docker-build:
timeout-minutes: 20
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v3
uses: actions/checkout@v4
- name: Set NEXT_PUBLIC_BUILD_ID
run: echo "NEXT_PUBLIC_BUILD_ID=$(git rev-parse --short HEAD)" >> $GITHUB_ENV
@@ -48,14 +49,6 @@ jobs:
docker compose -f docker-compose.build.yml up -d
sleep 5 # Wait for PostgreSQL to accept connections
- name: Check server health
run: |
timeout 10 bash -c 'until curl -f http://localhost:3000/api/public/health; do sleep 2; done'
- name: Check worker health
run: |
timeout 10 bash -c 'until curl -f http://localhost:3030/api/health; do sleep 2; done'
- name: Ensure no unhealthy status
run: |
if docker-compose ps | grep "(unhealthy)"; then
@@ -65,6 +58,14 @@ jobs:
echo "All services are healthy"
fi
- name: Check worker health
run: |
timeout 10 bash -c 'until curl -f http://localhost:3030/api/health; do sleep 2; done'
- name: Check server health
run: |
timeout 10 bash -c 'until curl -f http://localhost:3000/api/public/health; do sleep 2; done'
tests-web:
timeout-minutes: 20
runs-on: ubuntu-latest
@@ -79,12 +80,12 @@ jobs:
with:
swap-size-gb: 10
- uses: actions/checkout@v3
- uses: actions/checkout@v4
- uses: pnpm/action-setup@v3
with:
version: 9.5.0
- name: Use Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v3
uses: actions/setup-node@v4
with:
node-version: ${{ matrix.node-version }}
cache: "pnpm"
@@ -97,7 +98,7 @@ jobs:
- name: Load default env
run: |
cp .env.dev.example .env
grep -v -e '^REDIS_HOST=' .env.dev.example > .env
grep -v -e '^S3_BUCKET_NAME=' -e '^REDIS_HOST=' -e '^NEXT_PUBLIC_LANGFUSE_RUN_NEXT_INIT=' .env.dev.example > .env
- name: Run + migrate
run: |
@@ -113,10 +114,6 @@ jobs:
- name: Build
run: pnpm run build
env:
SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_AUTH_TOKEN }}
SENTRY_ORG: ${{ vars.SENTRY_ORG }}
SENTRY_PROJECT: ${{ vars.SENTRY_PROJECT }}
- name: Start Langfuse
run: (pnpm run start&)
@@ -148,13 +145,13 @@ jobs:
with:
swap-size-gb: 10
- uses: actions/checkout@v3
- uses: actions/checkout@v4
- uses: pnpm/action-setup@v3
with:
version: 9.5.0
- name: Use Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v3
uses: actions/setup-node@v4
with:
node-version: ${{ matrix.node-version }}
cache: "pnpm"
@@ -205,11 +202,11 @@ jobs:
e2e-tests:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
- uses: pnpm/action-setup@v3
with:
version: 9.5.0
- uses: actions/setup-node@v3
- uses: actions/setup-node@v4
with:
node-version: 20
cache: "pnpm"
@@ -247,11 +244,11 @@ jobs:
e2e-server-tests:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
- uses: pnpm/action-setup@v3
with:
version: 9.5.0
- uses: actions/setup-node@v3
- uses: actions/setup-node@v4
with:
node-version: 20
cache: "pnpm"
@@ -325,13 +322,13 @@ jobs:
with:
version: 9.5.0
- name: Setup node
uses: actions/setup-node@v3
uses: actions/setup-node@v4
with:
node-version: 20
cache-dependency-path: "pnpm-lock.yaml"
- name: Checkout
uses: actions/checkout@v3
uses: actions/checkout@v4
- name: Set NEXT_PUBLIC_BUILD_ID
run: echo "NEXT_PUBLIC_BUILD_ID=$(git rev-parse --short HEAD)" >> $GITHUB_ENV
@@ -1,30 +0,0 @@
"on":
push:
branches:
- production
name: Deploy to web
jobs:
porter-deploy:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v3
- name: Set Github tag
id: vars
run: echo "sha_short=$(git rev-parse --short HEAD)" >> $GITHUB_OUTPUT
- name: Setup porter
uses: porter-dev/setup-porter@v0.1.0
- name: Deploy stack
timeout-minutes: 30
run: exec porter apply
env:
PORTER_CLUSTER: "4060"
PORTER_DEPLOYMENT_TARGET_ID: 3be075c0-b121-4123-aec1-5dc571626b0c
PORTER_HOST: https://dashboard.porter.run
PORTER_PR_NUMBER: ${{ github.event.number }}
PORTER_PROJECT: "12565"
PORTER_REPO_NAME: ${{ github.event.repository.name }}
PORTER_STACK_NAME: web
PORTER_TAG: ${{ steps.vars.outputs.sha_short }}
PORTER_TOKEN: ${{ secrets.PORTER_STACK_12565_4060 }}
PORTER_NEXT_PUBLIC_BUILD_ID: ${{ steps.vars.outputs.sha_short }}
@@ -1,30 +0,0 @@
"on":
push:
branches:
- main
name: Deploy to web (Staging)
jobs:
porter-deploy:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v3
- name: Set Github tag
id: vars
run: echo "sha_short=$(git rev-parse --short HEAD)" >> $GITHUB_OUTPUT
- name: Setup porter
uses: porter-dev/setup-porter@v0.1.0
- name: Deploy stack
timeout-minutes: 30
run: exec porter apply
env:
PORTER_CLUSTER: "4037"
PORTER_DEPLOYMENT_TARGET_ID: c22d8fa2-f97c-4e1f-9cae-83cc6bfbad35
PORTER_HOST: https://dashboard.getporter.dev
PORTER_PR_NUMBER: ${{ github.event.number }}
PORTER_PROJECT: "12565"
PORTER_REPO_NAME: ${{ github.event.repository.name }}
PORTER_STACK_NAME: web
PORTER_TAG: ${{ steps.vars.outputs.sha_short }}
PORTER_TOKEN: ${{ secrets.PORTER_STACK_12565_4037 }}
PORTER_NEXT_PUBLIC_BUILD_ID: ${{ steps.vars.outputs.sha_short }}
@@ -1,30 +0,0 @@
"on":
push:
branches:
- production
name: Deploy to web (US temp)
jobs:
porter-deploy:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v3
- name: Set Github tag
id: vars
run: echo "sha_short=$(git rev-parse --short HEAD)" >> $GITHUB_OUTPUT
- name: Setup porter
uses: porter-dev/setup-porter@v0.1.0
- name: Deploy stack
timeout-minutes: 30
run: exec porter apply
env:
PORTER_CLUSTER: "4054"
PORTER_DEPLOYMENT_TARGET_ID: cddc0f2a-bb19-401a-9819-29f59f76b101
PORTER_HOST: https://dashboard.porter.run
PORTER_PR_NUMBER: ${{ github.event.number }}
PORTER_PROJECT: "12565"
PORTER_REPO_NAME: ${{ github.event.repository.name }}
PORTER_STACK_NAME: web
PORTER_TAG: ${{ steps.vars.outputs.sha_short }}
PORTER_TOKEN: ${{ secrets.PORTER_STACK_12565_4054 }}
PORTER_NEXT_PUBLIC_BUILD_ID: ${{ steps.vars.outputs.sha_short }}
@@ -1,29 +0,0 @@
"on":
push:
branches:
- production
name: Deploy to worker (EU temp)
jobs:
porter-deploy:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v3
- name: Set Github tag
id: vars
run: echo "sha_short=$(git rev-parse --short HEAD)" >> $GITHUB_OUTPUT
- name: Setup porter
uses: porter-dev/setup-porter@v0.1.0
- name: Deploy stack
timeout-minutes: 30
run: exec porter apply
env:
PORTER_CLUSTER: "4060"
PORTER_DEPLOYMENT_TARGET_ID: 3be075c0-b121-4123-aec1-5dc571626b0c
PORTER_HOST: https://dashboard.porter.run
PORTER_PR_NUMBER: ${{ github.event.number }}
PORTER_PROJECT: "12565"
PORTER_REPO_NAME: ${{ github.event.repository.name }}
PORTER_STACK_NAME: worker
PORTER_TAG: ${{ steps.vars.outputs.sha_short }}
PORTER_TOKEN: ${{ secrets.PORTER_STACK_12565_4060 }}
@@ -1,29 +0,0 @@
"on":
push:
branches:
- main
name: Deploy to worker (staging)
jobs:
porter-deploy:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v3
- name: Set Github tag
id: vars
run: echo "sha_short=$(git rev-parse --short HEAD)" >> $GITHUB_OUTPUT
- name: Setup porter
uses: porter-dev/setup-porter@v0.1.0
- name: Deploy stack
timeout-minutes: 30
run: exec porter apply
env:
PORTER_CLUSTER: "4037"
PORTER_DEPLOYMENT_TARGET_ID: c22d8fa2-f97c-4e1f-9cae-83cc6bfbad35
PORTER_HOST: https://dashboard.getporter.dev
PORTER_PR_NUMBER: ${{ github.event.number }}
PORTER_PROJECT: "12565"
PORTER_REPO_NAME: ${{ github.event.repository.name }}
PORTER_STACK_NAME: worker
PORTER_TAG: ${{ steps.vars.outputs.sha_short }}
PORTER_TOKEN: ${{ secrets.PORTER_STACK_12565_4037 }}
@@ -1,29 +0,0 @@
"on":
push:
branches:
- production
name: Deploy to worker (US temp)
jobs:
porter-deploy:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v3
- name: Set Github tag
id: vars
run: echo "sha_short=$(git rev-parse --short HEAD)" >> $GITHUB_OUTPUT
- name: Setup porter
uses: porter-dev/setup-porter@v0.1.0
- name: Deploy stack
timeout-minutes: 30
run: exec porter apply
env:
PORTER_CLUSTER: "4054"
PORTER_DEPLOYMENT_TARGET_ID: cddc0f2a-bb19-401a-9819-29f59f76b101
PORTER_HOST: https://dashboard.porter.run
PORTER_PR_NUMBER: ${{ github.event.number }}
PORTER_PROJECT: "12565"
PORTER_REPO_NAME: ${{ github.event.repository.name }}
PORTER_STACK_NAME: worker
PORTER_TAG: ${{ steps.vars.outputs.sha_short }}
PORTER_TOKEN: ${{ secrets.PORTER_STACK_12565_4054 }}
+2 -2
View File
@@ -2,10 +2,10 @@
"version": "0.2.0",
"configurations": [
{
"name": "Next.js: debug server-side",
"name": "Web and Worker: server side",
"type": "node-terminal",
"request": "launch",
"command": "npm run dev"
"command": "pnpm run dev"
},
{
"name": "Next.js: debug client-side",
+29 -30
View File
@@ -1,8 +1,7 @@
![langfuse_logo_1024](https://github.com/user-attachments/assets/f17d8fc1-8206-4afa-a5ec-774826be88a8)
![Langfuse GitHub Banner](https://github.com/langfuse/langfuse/assets/121163007/6035f0f3-d691-4963-b5d0-10cf506e9d42)
<div align="center"><h1> Langfuse: Open Source LLM Observability & Engineering Platform</h4></div>
<div align="center"><h2> Debug and improve your LLM app</h4></div>
<div align="center"><h3> LLM Observability, Prompt Management, LLM Evaluations, Datasets, LLM Metrics and Prompt Playground</h4></div>
<div align="center"><h1>Langfuse: Open Source LLM Engineering Platform</h1></div>
<div align="center"><h4>LLM Observability, Prompt Management, LLM Evaluations,<br/>Datasets, LLM Metrics, and Prompt Playground</h4></div>
<div align="center">
<div>
@@ -28,15 +27,15 @@
</div>
<span>Langfuse uses <a href="https://github.com/orgs/langfuse/discussions"><strong>Github Discussions</strong></a> for Support and Feature Requests.</span>
<br/>
<span>We're hiring. <a href="https://langfuse.com/careers"><strong>Join us</strong></a> in Backend Engineering, Product Engineering, and Developer Relations.</span>
<span>We're hiring. <a href="https://langfuse.com/careers"><strong>Join us</strong></a> in Product Engineering and Developer Relations.</span>
<br/>
<br/>
<div>
<a href="https://github.com/langfuse/langfuse/blob/main/LICENSE"><img src="https://img.shields.io/badge/License-MIT-red.svg?style=flat-square" alt="MIT License"></a>
<a href="https://www.ycombinator.com/companies/langfuse"><img src="https://img.shields.io/badge/Y%20Combinator-W23-orange?style=flat-square" alt="Y Combinator W23"></a>
<a href="https://github.com/langfuse/langfuse/pkgs/container/langfuse"><img alt="Docker Image" src="https://img.shields.io/badge/docker-langfuse-blue?logo=Docker&logoColor=white&style=flat-square"></a>
<a href="https://www.npmjs.com/package/langfuse"><img src="https://img.shields.io/npm/v/langfuse?style=flat-square&label=npm+langfuse" alt="langfuse npm package"></a>
<a href="https://pypi.python.org/pypi/langfuse"><img src="https://img.shields.io/pypi/v/langfuse.svg?style=flat-square&label=pypi+langfuse" alt="langfuse Python package on PyPi"></a>
<a href="https://pypi.python.org/pypi/langfuse"><img src="https://img.shields.io/pypi/dm/langfuse?style=flat-square&logo=python&logoColor=white&label=pypi%20langfuse&color=blue" alt="langfuse Python package on PyPi"></a>
<a href="https://www.npmjs.com/package/langfuse"><img src="https://img.shields.io/npm/dm/langfuse?style=flat-square&logo=npm&logoColor=white&label=npm%20langfuse&color=blue" alt="langfuse npm package"></a>
</div>
</div>
</br>
@@ -93,10 +92,10 @@ docker compose up -d
### Self-host (docker)
Langfuse is simple to self-host and keep updated. It currently requires only a single docker container.
Langfuse is simple to self-host and keep updated. It currently requires only a single docker container and a postgres database.
[→ Self Hosting Instructions](https://langfuse.com/docs/deployment/self-host)
Templated deployments: [Railway, GCP Cloud Run, AWS Fargate, Kubernetes and others](https://langfuse.com/docs/deployment/self-host#platform-specific-information)
Templated deployments: [Railway, GCP, AWS, Azure, Kubernetes and others](https://langfuse.com/docs/deployment/self-host#platform-specific-information)
## Get Started
@@ -108,31 +107,31 @@ You need a Langfuse public and secret key to get started. Sign up [here](https:/
Note: We recommend using our fully async, typed [SDKs](https://langfuse.com/docs/sdk) that allow you to instrument any LLM application with any underlying model. They are available in [Python (Decorators)](https://langfuse.com/docs/sdk/python) & [JS/TS](https://langfuse.com/docs/sdk/typescript). The SDKs will always be the most fully featured and stable way to ingest data into Langfuse.
You may want to use another integration to get started quickly or implement a use case that we do not yet support. However, we recommend to migrate to the Langfuse SDKs over time to ensure performance and stability.
See the [→ Quickstart](https://langfuse.com/docs/get-started) to integrate Langfuse.
### LLM Observability Integrations
| Integration | Supports | Description |
| -------------------------------------------------------- | -------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------ |
| [SDK](/docs/sdk) | Python, JS/TS | Manual instrumentation using the SDKs for full flexibility. |
| [OpenAI](/docs/integrations/openai) | Python, JS/TS | Automated instrumentation using drop-in replacement of OpenAI SDK. |
| [Langchain](/docs/integrations/langchain) | Python, JS/TS | Automated instrumentation by passing callback handler to Langchain application. |
| [LlamaIndex](/docs/integrations/llama-index/get-started) | Python | Automated instrumentation via LlamaIndex callback system. |
| [Haystack](/docs/integrations/haystack) | Python | Automated instrumentation via Haystack content tracing system. |
| [LiteLLM](/docs/integrations/litellm) | Python, JS/TS (proxy only) | Use any LLM as a drop in replacement for GPT. Use Azure, OpenAI, Cohere, Anthropic, Ollama, VLLM, Sagemaker, HuggingFace, Replicate (100+ LLMs). |
| [API](/docs/api) | | Directly call the public API. OpenAPI spec available. |
| Integration | Supports | Description |
| ---------------------------------------------------------------------------- | -------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------ |
| [SDK](https://langfuse.com/docs/sdk) | Python, JS/TS | Manual instrumentation using the SDKs for full flexibility. |
| [OpenAI](https://langfuse.com/docs/integrations/openai) | Python, JS/TS | Automated instrumentation using drop-in replacement of OpenAI SDK. |
| [Langchain](https://langfuse.com/docs/integrations/langchain) | Python, JS/TS | Automated instrumentation by passing callback handler to Langchain application. |
| [LlamaIndex](https://langfuse.com/docs/integrations/llama-index/get-started) | Python | Automated instrumentation via LlamaIndex callback system. |
| [Haystack](https://langfuse.com/docs/integrations/haystack) | Python | Automated instrumentation via Haystack content tracing system. |
| [LiteLLM](https://langfuse.com/docs/integrations/litellm) | Python, JS/TS (proxy only) | Use any LLM as a drop in replacement for GPT. Use Azure, OpenAI, Cohere, Anthropic, Ollama, VLLM, Sagemaker, HuggingFace, Replicate (100+ LLMs). |
| [Vercel AI SDK](https://langfuse.com/docs/integrations/vercel-ai-sdk) | JS/TS | TypeScript toolkit designed to help developers build AI-powered applications with React, Next.js, Vue, Svelte, Node.js. |
| [API](https://langfuse.com/docs/api) | | Directly call the public API. OpenAPI spec available. |
Packages that integrate with Langfuse:
Packages integrated with Langfuse:
| Name | Description |
| ---------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------- |
| [Instructor](/docs/integrations/instructor) | Library to get structured LLM outputs (JSON, Pydantic) |
| [Mirascope](/docs/integrations/mirascope) | Python toolkit for building LLM applications. |
| [AI SDK by Vercel](/docs/sdk/typescript/example-vercel-ai) | Typescript SDK that makes streaming LLM outputs super easy. |
| [Flowise](/docs/integrations/flowise) | JS/TS no-code builder for customized LLM flows. |
| [Langflow](/docs/integrations/langflow) | Python-based UI for LangChain, designed with react-flow to provide an effortless way to experiment and prototype flows. |
| Name | Description |
| --------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------- |
| [Instructor](https://langfuse.com/docs/integrations/instructor) | Library to get structured LLM outputs (JSON, Pydantic) |
| [Dify](https://langfuse.com/docs/integrations/dify) | Open source LLM app development platform with no-code builder. |
| [Ollama](https://langfuse.com/docs/integrations/ollama) | Easily run open source LLMs on your own machine. |
| [Mirascope](https://langfuse.com/docs/integrations/mirascope) | Python toolkit for building LLM applications. |
| [Flowise](https://langfuse.com/docs/integrations/flowise) | JS/TS no-code builder for customized LLM flows. |
| [Langflow](https://langfuse.com/docs/integrations/langflow) | Python-based UI for LangChain, designed with react-flow to provide an effortless way to experiment and prototype flows. |
## Questions and feedback
@@ -140,13 +139,13 @@ Packages that integrate with Langfuse:
- [Roadmap](https://langfuse.com/roadmap)
- [GitHub Discussions](https://github.com/orgs/langfuse/discussions)
- [Feature Requests](https://langfuse.com/idea
- [Feature Requests](https://langfuse.com/ideas)
### Support and feedback
In order of preference the best way to communicate with us:
- [GitHub Discussions](https://github.com/orgs/langfuse/discussions): Contribute [ideas](https://langfuse.com/idea) [support requests](https://github.com/orgs/langfuse/discussions/categories/support) and [report bugs](https://github.com/langfuse/langfuse/issues/new?labels=%F0%9F%90%9E%E2%9D%94+unconfirmed+bug&projects=&template=bug_report.yml&title=bug%3A+) (preferred as we create a permanent, indexed artifact for other community members)
- [GitHub Discussions](https://github.com/orgs/langfuse/discussions) (preferred): Contribute [ideas](https://langfuse.com/ideas), [support requests](https://langfuse.com/gh-support) and [report bugs](https://langfuse.com/issues)
- [Discord](https://langfuse.com/discord): community support
- Privately: contact at langfuse dot com
+9
View File
@@ -22,6 +22,15 @@ services:
- LANGFUSE_ENABLE_EXPERIMENTAL_FEATURES=${LANGFUSE_ENABLE_EXPERIMENTAL_FEATURES:-false}
- LANGFUSE_WORKER_HOST=${LANGFUSE_WORKER_HOST:-worker}
- LANGFUSE_WORKER_PASSWORD=${LANGFUSE_WORKER_PASSWORD:-mybasicauthsecret}
- LANGFUSE_INIT_ORG_ID=${LANGFUSE_INIT_ORG_ID:-}
- LANGFUSE_INIT_ORG_NAME=${LANGFUSE_INIT_ORG_NAME:-}
- LANGFUSE_INIT_PROJECT_ID=${LANGFUSE_INIT_PROJECT_ID:-}
- LANGFUSE_INIT_PROJECT_NAME=${LANGFUSE_INIT_PROJECT_NAME:-}
- LANGFUSE_INIT_PROJECT_PUBLIC_KEY=${LANGFUSE_INIT_PROJECT_PUBLIC_KEY:-}
- LANGFUSE_INIT_PROJECT_SECRET_KEY=${LANGFUSE_INIT_PROJECT_SECRET_KEY:-}
- LANGFUSE_INIT_USER_EMAIL=${LANGFUSE_INIT_USER_EMAIL:-}
- LANGFUSE_INIT_USER_NAME=${LANGFUSE_INIT_USER_NAME:-}
- LANGFUSE_INIT_USER_PASSWORD=${LANGFUSE_INIT_USER_PASSWORD:-}
restart: always
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:3000/api/public/health"]
+34
View File
@@ -17,6 +17,38 @@ services:
depends_on:
- postgres
minio:
image: minio/minio
container_name: minio
command: server /data --console-address ":9001"
environment:
MINIO_ACCESS_KEY: minio
MINIO_SECRET_KEY: miniosecret
ports:
- "9090:9000"
- "9091:9001"
volumes:
- langfuse_minio_data:/data
healthcheck:
test: ["CMD", "mc", "ready", "local"]
interval: 10s
timeout: 5s
retries: 5
start_period: 5s
miniocreatebucket:
image: minio/mc
container_name: miniocreatebucket
entrypoint: ["/bin/sh", "-c"]
command: >
"mc alias set minio http://minio:9000 minio miniosecret &&
mc rm -r --force minio/mybucket || true &&
mc mb minio/mybucket &&
mc policy set download minio/mybucket"
depends_on:
minio:
condition: service_healthy
redis:
image: redis:7.2.4
restart: always
@@ -45,3 +77,5 @@ volumes:
driver: local
langfuse_clickhouse_logs:
driver: local
langfuse_minio_data:
driver: local
+9
View File
@@ -14,6 +14,15 @@ services:
- 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:-}
- LANGFUSE_INIT_ORG_NAME=${LANGFUSE_INIT_ORG_NAME:-}
- LANGFUSE_INIT_PROJECT_ID=${LANGFUSE_INIT_PROJECT_ID:-}
- LANGFUSE_INIT_PROJECT_NAME=${LANGFUSE_INIT_PROJECT_NAME:-}
- LANGFUSE_INIT_PROJECT_PUBLIC_KEY=${LANGFUSE_INIT_PROJECT_PUBLIC_KEY:-}
- LANGFUSE_INIT_PROJECT_SECRET_KEY=${LANGFUSE_INIT_PROJECT_SECRET_KEY:-}
- LANGFUSE_INIT_USER_EMAIL=${LANGFUSE_INIT_USER_EMAIL:-}
- LANGFUSE_INIT_USER_NAME=${LANGFUSE_INIT_USER_NAME:-}
- LANGFUSE_INIT_USER_PASSWORD=${LANGFUSE_INIT_USER_PASSWORD:-}
db:
image: postgres
+4 -3
View File
@@ -25,8 +25,9 @@
},
"dependencies": {
"@langfuse/shared": "workspace:*",
"@opentelemetry/api": ">=1.0.0 <1.9.0",
"next": "^14.2.6",
"@opentelemetry/api": ">=1.0.0 <1.10.0",
"axios": "^1.7.7",
"next": "^14.2.15",
"next-auth": "^4.24.7",
"zod": "^3.23.8"
},
@@ -39,7 +40,7 @@
"eslint-config-prettier": "^9.1.0",
"eslint-config-standard": "^17.1.0",
"eslint-plugin-prettier": "^5.1.3",
"prettier": "^3.2.5",
"prettier": "^3.3.3",
"ts-node": "^10.9.2",
"tsc-watch": "^6.2.0",
"typescript": "^5.4.5"
+5 -2
View File
@@ -1,9 +1,12 @@
import { z } from "zod";
import { env as sharedEnv } from "@langfuse/shared";
import { env as sharedEnv, removeEmptyEnvVariables } from "@langfuse/shared";
const EnvSchema = z.object({
NEXT_PUBLIC_LANGFUSE_CLOUD_REGION: z.string().optional(),
LANGFUSE_EE_LICENSE_KEY: z.string().optional(),
});
export const env = { ...sharedEnv, ...EnvSchema.parse(process.env) };
export const env = {
...sharedEnv,
...EnvSchema.parse(removeEmptyEnvVariables(process.env)),
};
+2 -2
View File
@@ -31,10 +31,10 @@ service:
docs: Optional filter for metrics where traces include all of these tags
fromTimestamp:
type: optional<datetime>
docs: Optional filter to only include traces on or after a certain datetime (ISO 8601)
docs: Optional filter to only include traces and observations on or after a certain datetime (ISO 8601)
toTimestamp:
type: optional<datetime>
docs: Optional filter to only include traces before a certain datetime (ISO 8601)
docs: Optional filter to only include traces and observations before a certain datetime (ISO 8601)
response: DailyMetrics
types:
DailyMetrics:
+3 -13
View File
@@ -3,32 +3,22 @@ groups:
local:
generators:
- name: fernapi/fern-openapi
version: 0.0.28
version: 0.0.31
output:
location: local-file-system
path: ../../../web/public/generated/api
config:
namespaceExport: Langfuse
allowCustomFetcher: true
- name: fernapi/fern-python-sdk
version: 1.1.0-rc2
version: 2.16.0
output:
location: local-file-system
path: ../../../generated/python
config:
client_class_name: FernLangfuse
improved_imports: false
inline_request_params: false
pydantic_config:
require_optional_fields: false
use_str_enums: false
- name: fernapi/fern-typescript-node-sdk
version: 0.7.1
output:
location: local-file-system
path: ../../../generated/typescript-server
config:
namespaceExport: Langfuse
allowCustomFetcher: true
- name: fernapi/fern-postman
version: 0.0.45
output:
+1 -1
View File
@@ -1,4 +1,4 @@
{
"organization": "finto",
"version": "0.30.3"
"version": "0.43.7"
}
+2 -2
View File
@@ -1,6 +1,6 @@
{
"name": "langfuse",
"version": "2.78.0",
"version": "2.84.1",
"author": "engineering@langfuse.com",
"license": "MIT",
"private": true,
@@ -33,7 +33,7 @@
"braces": "3.0.3",
"dotenv-cli": "^7.4.2",
"husky": "^9.0.11",
"prettier": "^3.2.5",
"prettier": "^3.3.3",
"release-it": "^17.3.0",
"turbo": "^1.13.4"
},
+1 -1
View File
@@ -11,7 +11,7 @@
"@typescript-eslint/eslint-plugin": "^7.1.0",
"@typescript-eslint/parser": "^7.12.0",
"@vercel/style-guide": "^6.0.0",
"eslint-config-next": "^14.2.6",
"eslint-config-next": "^14.2.15",
"eslint-config-prettier": "^9.1.0",
"eslint-config-turbo": "^1.13.4",
"eslint-plugin-only-warn": "^1.1.0",
+18 -17
View File
@@ -55,28 +55,29 @@
"dependencies": {
"@anthropic-ai/tokenizer": "^0.0.4",
"@aws-sdk/client-s3": "^3.627.0",
"@aws-sdk/lib-storage": "^3.568.0",
"@aws-sdk/lib-storage": "^3.667.0",
"@aws-sdk/s3-request-presigner": "^3.554.0",
"@clickhouse/client": "^1.4.0",
"@langchain/anthropic": "^0.1.21",
"@langchain/core": "^0.1.61",
"@langchain/openai": "^0.0.33",
"@opentelemetry/api": ">=1.0.0 <1.9.0",
"@prisma/client": "^5.17.0",
"@langchain/anthropic": "^0.3.1",
"@langchain/core": "^0.3.9",
"@langchain/openai": "^0.3.0",
"@opentelemetry/api": ">=1.0.0 <1.10.0",
"@prisma/client": "^5.20.0",
"@react-email/components": "^0.0.19",
"@react-email/render": "^0.0.15",
"@types/bcryptjs": "^2.4.6",
"axios": "^1.7.7",
"bcryptjs": "^2.4.3",
"bullmq": "^5.12.10",
"dd-trace": "^5.19.0",
"dd-trace": "^5.23.1",
"decimal.js": "^10.4.3",
"exponential-backoff": "^3.1.1",
"ioredis": "^5.4.1",
"kysely": "^0.27.4",
"langchain": "^0.2.6",
"langchain": "^0.3.2",
"lodash": "^4.17.21",
"next-auth": "^4.24.7",
"nodemailer": "^6.9.13",
"nodemailer": "^6.9.15",
"prisma-extension-kysely": "^2.1.0",
"uuid": "^9.0.1",
"winston": "^3.14.2",
@@ -86,26 +87,26 @@
"devDependencies": {
"@repo/eslint-config": "workspace:*",
"@repo/typescript-config": "workspace:*",
"@types/lodash": "^4.17.7",
"@types/lodash": "^4.17.10",
"@types/node": "^20.11.29",
"@types/nodemailer": "^6.4.15",
"@types/pg": "^8.11.6",
"@types/nodemailer": "^6.4.16",
"@types/pg": "^8.11.10",
"@types/uuid": "^9.0.8",
"@typescript-eslint/parser": "^7.12.0",
"eslint": "^8.57.0",
"eslint-config-prettier": "^9.1.0",
"eslint-config-standard": "^17.1.0",
"eslint-plugin-prettier": "^5.1.3",
"kysely-codegen": "^0.11.0",
"nodemon": "^3.1.3",
"prettier": "^3.2.5",
"prisma": "^5.17.0",
"kysely-codegen": "^0.16.8",
"nodemon": "^3.1.7",
"prettier": "^3.3.3",
"prisma": "^5.20.0",
"prisma-erd-generator": "^1.11.2",
"prisma-kysely": "^1.8.0",
"ts-node": "^10.9.2",
"tsc-watch": "^6.2.0",
"typescript": "^5.4.5",
"vitest": "^1.5.3"
"vitest": "^2.1.2"
},
"peerDependencies": {
"@types/react": "~18.2.79",
+38
View File
@@ -37,6 +37,16 @@ export const ScoreDataType = {
BOOLEAN: "BOOLEAN"
} as const;
export type ScoreDataType = (typeof ScoreDataType)[keyof typeof ScoreDataType];
export const AnnotationQueueStatus = {
PENDING: "PENDING",
COMPLETED: "COMPLETED"
} as const;
export type AnnotationQueueStatus = (typeof AnnotationQueueStatus)[keyof typeof AnnotationQueueStatus];
export const AnnotationQueueObjectType = {
TRACE: "TRACE",
OBSERVATION: "OBSERVATION"
} as const;
export type AnnotationQueueObjectType = (typeof AnnotationQueueObjectType)[keyof typeof AnnotationQueueObjectType];
export const DatasetStatus = {
ACTIVE: "ACTIVE",
ARCHIVED: "ARCHIVED"
@@ -80,6 +90,31 @@ export type Account = {
scope: string | null;
id_token: string | null;
session_state: string | null;
refresh_token_expires_in: number | null;
created_at: number | null;
};
export type AnnotationQueue = {
id: string;
name: string;
description: string | null;
score_config_ids: Generated<string[]>;
project_id: string;
created_at: Generated<Timestamp>;
updated_at: Generated<Timestamp>;
};
export type AnnotationQueueItem = {
id: string;
queue_id: string;
object_id: string;
object_type: AnnotationQueueObjectType;
status: Generated<AnnotationQueueStatus>;
locked_at: Timestamp | null;
locked_by_user_id: string | null;
annotator_user_id: string | null;
completed_at: Timestamp | null;
project_id: string;
created_at: Generated<Timestamp>;
updated_at: Generated<Timestamp>;
};
export type ApiKey = {
id: string;
@@ -409,6 +444,7 @@ export type Score = {
observation_id: string | null;
config_id: string | null;
string_value: string | null;
queue_id: string | null;
created_at: Generated<Timestamp>;
updated_at: Generated<Timestamp>;
data_type: Generated<ScoreDataType>;
@@ -505,6 +541,8 @@ export type VerificationToken = {
};
export type DB = {
Account: Account;
annotation_queue_items: AnnotationQueueItem;
annotation_queues: AnnotationQueue;
api_keys: ApiKey;
audit_logs: AuditLog;
batch_exports: BatchExport;
@@ -0,0 +1,30 @@
-- For OpenAI reasoning models, manually tokenizing output string does not account for reasoning tokens, thus would lead to incorrect cost
-- So no manual tokenizer config is provided
-- Reference: https://platform.openai.com/docs/guides/reasoning/how-reasoning-works
INSERT INTO models (
id,
project_id,
model_name,
match_pattern,
start_date,
input_price,
output_price,
total_price,
unit,
tokenizer_id,
tokenizer_config
)
VALUES
-- o1-preview
('cm10ivcdp0000gix7lelmbw80', NULL, 'o1-preview', '(?i)^(o1-preview)$', NULL, 0.000015, 0.00006, NULL, 'TOKENS', NULL, NULL),
-- o1-preview-2024-09-12
('cm10ivo130000n8x7qopcjjcg', NULL, 'o1-preview-2024-09-12', '(?i)^(o1-preview-2024-09-12)$', NULL, 0.000015, 0.00006, NULL, 'TOKENS', NULL, NULL),
-- o1-mini
('cm10ivwo40000r1x7gg3syjq0', NULL, 'o1-mini', '(?i)^(o1-mini)$', NULL, 0.000003, 0.000012, NULL, 'TOKENS', NULL, NULL),
-- o1-mini-2024-09-12
('cm10iw6p20000wgx7it1hlb22', NULL, 'o1-mini-2024-09-12', '(?i)^(o1-mini-2024-09-12)$', NULL, 0.000003, 0.000012, NULL, 'TOKENS', NULL, NULL)
@@ -0,0 +1,2 @@
-- AlterTable
ALTER TABLE "Account" ADD COLUMN "refresh_token_expires_in" INTEGER;
@@ -0,0 +1,3 @@
-- DropIndex
-- Removes indexes that are covered by other indexes on the same table, e.g. (project_id) if (project_id, timestamp) exists.
DROP INDEX CONCURRENTLY IF EXISTS "traces_project_id_idx"; -- covered by traces_project_id_timestamp_idx
@@ -0,0 +1,3 @@
-- DropIndex
-- Removes indexes that are covered by other indexes on the same table, e.g. (project_id) if (project_id, timestamp) exists.
DROP INDEX CONCURRENTLY IF EXISTS "datasets_project_id_idx"; -- covered by datasets_project_id_name_key (unique)
@@ -0,0 +1,3 @@
-- DropIndex
-- Removes indexes that are covered by other indexes on the same table, e.g. (project_id) if (project_id, timestamp) exists.
DROP INDEX CONCURRENTLY IF EXISTS "eval_templates_project_id_idx"; -- covered by eval_templates_project_id_id_idx
@@ -0,0 +1,3 @@
-- DropIndex
-- Removes indexes that are covered by other indexes on the same table, e.g. (project_id) if (project_id, timestamp) exists.
DROP INDEX CONCURRENTLY IF EXISTS "job_configurations_project_id_idx"; -- covered by job_configurations_project_id_id_idx
@@ -0,0 +1,3 @@
-- DropIndex
-- Removes indexes that are covered by other indexes on the same table, e.g. (project_id) if (project_id, timestamp) exists.
DROP INDEX CONCURRENTLY IF EXISTS "job_executions_project_id_idx"; -- covered by job_executions_project_id_id_idx
@@ -0,0 +1,3 @@
-- DropIndex
-- Removes indexes that are covered by other indexes on the same table, e.g. (project_id) if (project_id, timestamp) exists.
DROP INDEX CONCURRENTLY IF EXISTS "llm_api_keys_project_id_provider_idx"; -- covered by llm_api_keys_project_id_provider_key (unique)
@@ -0,0 +1,3 @@
-- DropIndex
-- Removes indexes that are covered by other indexes on the same table, e.g. (project_id) if (project_id, timestamp) exists.
DROP INDEX CONCURRENTLY IF EXISTS "models_project_id_model_name_idx"; -- covered by models_project_id_model_name_start_date_unit_key (unique)
@@ -0,0 +1,3 @@
-- DropIndex
-- Removes indexes that are covered by other indexes on the same table, e.g. (project_id) if (project_id, timestamp) exists.
DROP INDEX CONCURRENTLY IF EXISTS "models_project_id_model_name_start_date_unit_idx"; -- covered by models_project_id_model_name_start_date_unit_key (unique)
@@ -0,0 +1,3 @@
-- DropIndex
-- Removes indexes that are covered by other indexes on the same table, e.g. (project_id) if (project_id, timestamp) exists.
DROP INDEX CONCURRENTLY IF EXISTS "observations_project_id_idx"; -- covered by observations_project_id_start_time_type_idx
@@ -0,0 +1,3 @@
-- DropIndex
-- Removes indexes that are covered by other indexes on the same table, e.g. (project_id) if (project_id, timestamp) exists.
DROP INDEX CONCURRENTLY IF EXISTS "observations_trace_id_idx"; -- covered by observations_trace_id_project_id_start_time_idx
@@ -0,0 +1,3 @@
-- DropIndex
-- Removes indexes that are covered by other indexes on the same table, e.g. (project_id) if (project_id, timestamp) exists.
DROP INDEX CONCURRENTLY IF EXISTS "observations_trace_id_project_id_idx"; -- covered by observations_trace_id_project_id_start_time_idx
@@ -0,0 +1,3 @@
-- DropIndex
-- Removes indexes that are covered by other indexes on the same table, e.g. (project_id) if (project_id, timestamp) exists.
DROP INDEX CONCURRENTLY IF EXISTS "organization_memberships_org_id_idx"; -- covered by organization_memberships_org_id_user_id_key (unique)
@@ -0,0 +1,3 @@
-- DropIndex
-- Removes indexes that are covered by other indexes on the same table, e.g. (project_id) if (project_id, timestamp) exists.
DROP INDEX CONCURRENTLY IF EXISTS "posthog_integrations_project_id_idx"; -- covered by posthog_integrations_pkey (unique)
@@ -0,0 +1,3 @@
-- DropIndex
-- Removes indexes that are covered by other indexes on the same table, e.g. (project_id) if (project_id, timestamp) exists.
DROP INDEX CONCURRENTLY IF EXISTS "prompts_project_id_idx"; -- covered by prompts_project_id_name_version_key (unique)
@@ -0,0 +1,3 @@
-- DropIndex
-- Removes indexes that are covered by other indexes on the same table, e.g. (project_id) if (project_id, timestamp) exists.
DROP INDEX CONCURRENTLY IF EXISTS "prompts_project_id_name_version_idx"; -- covered by prompts_project_id_name_version_key (unique)
@@ -0,0 +1,3 @@
-- DropIndex
-- Removes indexes that are covered by other indexes on the same table, e.g. (project_id) if (project_id, timestamp) exists.
DROP INDEX CONCURRENTLY IF EXISTS "scores_project_id_idx"; -- covered by scores_project_id_name_idx
@@ -0,0 +1,2 @@
-- AlterTable
ALTER TABLE "Account" ADD COLUMN "created_at" INTEGER;
@@ -0,0 +1,2 @@
-- AlterTable
ALTER TABLE "scores" ADD COLUMN "queue_id" TEXT;
@@ -0,0 +1,75 @@
-- CreateEnum
CREATE TYPE "AnnotationQueueStatus" AS ENUM ('PENDING', 'COMPLETED');
-- CreateEnum
CREATE TYPE "AnnotationQueueObjectType" AS ENUM ('TRACE', 'OBSERVATION');
-- CreateTable
CREATE TABLE "annotation_queues" (
"id" TEXT NOT NULL,
"name" TEXT NOT NULL,
"description" TEXT,
"score_config_ids" TEXT[] DEFAULT ARRAY[]::TEXT[],
"project_id" TEXT NOT NULL,
"created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updated_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
CONSTRAINT "annotation_queues_pkey" PRIMARY KEY ("id")
);
-- CreateTable
CREATE TABLE "annotation_queue_items" (
"id" TEXT NOT NULL,
"queue_id" TEXT NOT NULL,
"object_id" TEXT NOT NULL,
"object_type" "AnnotationQueueObjectType" NOT NULL,
"status" "AnnotationQueueStatus" NOT NULL DEFAULT 'PENDING',
"locked_at" TIMESTAMP(3),
"locked_by_user_id" TEXT,
"annotator_user_id" TEXT,
"completed_at" TIMESTAMP(3),
"project_id" TEXT NOT NULL,
"created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updated_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
CONSTRAINT "annotation_queue_items_pkey" PRIMARY KEY ("id")
);
-- CreateIndex
CREATE INDEX "annotation_queues_id_project_id_idx" ON "annotation_queues"("id", "project_id");
-- CreateIndex
CREATE INDEX "annotation_queues_project_id_created_at_idx" ON "annotation_queues"("project_id", "created_at");
-- CreateIndex
CREATE UNIQUE INDEX "annotation_queues_project_id_name_key" ON "annotation_queues"("project_id", "name");
-- CreateIndex
CREATE INDEX "annotation_queue_items_id_project_id_idx" ON "annotation_queue_items"("id", "project_id");
-- CreateIndex
CREATE INDEX "annotation_queue_items_project_id_queue_id_status_idx" ON "annotation_queue_items"("project_id", "queue_id", "status");
-- CreateIndex
CREATE INDEX "annotation_queue_items_object_id_object_type_project_id_que_idx" ON "annotation_queue_items"("object_id", "object_type", "project_id", "queue_id");
-- CreateIndex
CREATE INDEX "annotation_queue_items_annotator_user_id_idx" ON "annotation_queue_items"("annotator_user_id");
-- CreateIndex
CREATE INDEX "annotation_queue_items_created_at_idx" ON "annotation_queue_items"("created_at");
-- AddForeignKey
ALTER TABLE "annotation_queues" ADD CONSTRAINT "annotation_queues_project_id_fkey" FOREIGN KEY ("project_id") REFERENCES "projects"("id") ON DELETE CASCADE ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "annotation_queue_items" ADD CONSTRAINT "annotation_queue_items_queue_id_fkey" FOREIGN KEY ("queue_id") REFERENCES "annotation_queues"("id") ON DELETE CASCADE ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "annotation_queue_items" ADD CONSTRAINT "annotation_queue_items_locked_by_user_id_fkey" FOREIGN KEY ("locked_by_user_id") REFERENCES "users"("id") ON DELETE SET NULL ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "annotation_queue_items" ADD CONSTRAINT "annotation_queue_items_annotator_user_id_fkey" FOREIGN KEY ("annotator_user_id") REFERENCES "users"("id") ON DELETE SET NULL ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "annotation_queue_items" ADD CONSTRAINT "annotation_queue_items_project_id_fkey" FOREIGN KEY ("project_id") REFERENCES "projects"("id") ON DELETE CASCADE ON UPDATE CASCADE;
+100 -56
View File
@@ -34,21 +34,23 @@ generator kysely {
// Necessary for Next auth
model Account {
id String @id @default(cuid())
userId String @map("user_id")
type String
provider String
providerAccountId String
refresh_token String? // @db.Text
access_token String? // @db.Text
expires_at Int?
expires_in Int?
ext_expires_in Int?
token_type String?
scope String?
id_token String? // @db.Text
session_state String?
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
id String @id @default(cuid())
userId String @map("user_id")
type String
provider String
providerAccountId String
refresh_token String? // @db.Text
access_token String? // @db.Text
expires_at Int?
expires_in Int?
ext_expires_in Int?
token_type String?
scope String?
id_token String? // @db.Text
session_state String?
refresh_token_expires_in Int?
created_at Int? // GitLab
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
@@unique([provider, providerAccountId])
@@index([userId])
@@ -78,6 +80,8 @@ model User {
createdAt DateTime @default(now()) @map("created_at")
updatedAt DateTime @default(now()) @updatedAt @map("updated_at")
featureFlags String[] @default([]) @map("feature_flags")
annotatedLockedItem AnnotationQueueItem[] @relation("LockedByUser")
annotatedCompletedItem AnnotationQueueItem[] @relation("AnnotatorUser")
@@map("users")
}
@@ -105,31 +109,33 @@ model Organization {
}
model Project {
id String @id @default(cuid())
orgId String @map("org_id")
createdAt DateTime @default(now()) @map("created_at")
updatedAt DateTime @default(now()) @updatedAt @map("updated_at")
name String
projectMembers ProjectMembership[]
organization Organization @relation(fields: [orgId], references: [id], onUpdate: Cascade, onDelete: Cascade)
traces Trace[]
observations Observation[]
apiKeys ApiKey[]
dataset Dataset[]
RawEvents Events[]
invitations MembershipInvitation[]
sessions TraceSession[]
Prompt Prompt[]
Model Model[]
EvalTemplate EvalTemplate[]
JobConfiguration JobConfiguration[]
JobExecution JobExecution[]
LlmApiKeys LlmApiKeys[]
PosthogIntegration PosthogIntegration[]
Score Score[]
scoreConfig ScoreConfig[]
BatchExport BatchExport[]
comment Comment[]
id String @id @default(cuid())
orgId String @map("org_id")
createdAt DateTime @default(now()) @map("created_at")
updatedAt DateTime @default(now()) @updatedAt @map("updated_at")
name String
projectMembers ProjectMembership[]
organization Organization @relation(fields: [orgId], references: [id], onUpdate: Cascade, onDelete: Cascade)
traces Trace[]
observations Observation[]
apiKeys ApiKey[]
dataset Dataset[]
RawEvents Events[]
invitations MembershipInvitation[]
sessions TraceSession[]
Prompt Prompt[]
Model Model[]
EvalTemplate EvalTemplate[]
JobConfiguration JobConfiguration[]
JobExecution JobExecution[]
LlmApiKeys LlmApiKeys[]
PosthogIntegration PosthogIntegration[]
Score Score[]
scoreConfig ScoreConfig[]
BatchExport BatchExport[]
comment Comment[]
annotationQueue AnnotationQueue[]
annotationQueueItem AnnotationQueueItem[]
@@index([orgId])
@@map("projects")
@@ -172,7 +178,6 @@ model LlmApiKeys {
project Project @relation(fields: [projectId], references: [id], onDelete: Cascade)
@@unique([projectId, provider])
@@index([projectId, provider])
@@map("llm_api_keys")
}
@@ -189,7 +194,6 @@ model OrganizationMembership {
@@unique([orgId, userId])
@@index([userId])
@@index([orgId])
@@map("organization_memberships")
}
@@ -282,7 +286,6 @@ model Trace {
JobExecution JobExecution[]
@@index([projectId])
@@index([projectId, timestamp])
@@index([sessionId])
@@index([name])
@@ -373,12 +376,9 @@ model Observation {
@@index([projectId, internalModel, startTime, unit])
@@index([traceId, projectId, type, startTime])
@@index([traceId, projectId, startTime])
@@index([traceId, projectId])
@@index([traceId])
@@index([type])
@@index(startTime)
@@index(createdAt)
@@index(projectId)
@@index(model)
@@index(internalModel)
@@index([projectId, promptId])
@@ -464,6 +464,7 @@ model Score {
observationId String? @map("observation_id")
configId String? @map("config_id")
stringValue String? @map("string_value") // always defined if data type is CATEGORICAL or BOOLEAN, null for NUMERIC
queueId String? @map("queue_id")
createdAt DateTime @default(now()) @map("created_at")
updatedAt DateTime @default(now()) @updatedAt @map("updated_at")
dataType ScoreDataType @default(NUMERIC) @map("data_type")
@@ -473,7 +474,6 @@ model Score {
@@unique([id, projectId]) // used for upserts via prisma
@@index(timestamp)
@@index([value])
@@index([projectId])
@@index([projectId, name])
@@index([authorUserId])
@@index([configId])
@@ -521,6 +521,59 @@ enum ScoreDataType {
BOOLEAN
}
model AnnotationQueue {
id String @id @default(cuid())
name String
description String?
scoreConfigIds String[] @default([]) @map("score_config_ids")
projectId String @map("project_id")
project Project @relation(fields: [projectId], references: [id], onDelete: Cascade)
createdAt DateTime @default(now()) @map("created_at")
updatedAt DateTime @default(now()) @updatedAt @map("updated_at")
annotationQueueItem AnnotationQueueItem[]
@@unique([projectId, name])
@@index([id, projectId])
@@index([projectId, createdAt])
@@map("annotation_queues")
}
model AnnotationQueueItem {
id String @id @default(cuid())
queueId String @map("queue_id")
queue AnnotationQueue @relation(fields: [queueId], references: [id], onDelete: Cascade)
objectId String @map("object_id")
objectType AnnotationQueueObjectType @map("object_type")
status AnnotationQueueStatus @default(PENDING)
lockedAt DateTime? @map("locked_at")
lockedByUserId String? @map("locked_by_user_id")
lockedByUser User? @relation("LockedByUser", fields: [lockedByUserId], references: [id], onDelete: SetNull)
annotatorUserId String? @map("annotator_user_id")
annotatorUser User? @relation("AnnotatorUser", fields: [annotatorUserId], references: [id], onDelete: SetNull)
completedAt DateTime? @map("completed_at")
projectId String @map("project_id")
project Project @relation(fields: [projectId], references: [id], onDelete: Cascade)
createdAt DateTime @default(now()) @map("created_at")
updatedAt DateTime @default(now()) @updatedAt @map("updated_at")
@@index([id, projectId])
@@index([projectId, queueId, status])
@@index([objectId, objectType, projectId, queueId])
@@index([annotatorUserId])
@@index([createdAt])
@@map("annotation_queue_items")
}
enum AnnotationQueueStatus {
PENDING
COMPLETED
}
enum AnnotationQueueObjectType {
TRACE
OBSERVATION
}
model CronJobs {
name String @id
lastRun DateTime? @map("last_run")
@@ -544,7 +597,6 @@ model Dataset {
@@id([id, projectId])
@@unique([projectId, name])
@@index([projectId], type: Hash)
@@index([createdAt])
@@index([updatedAt])
@@map("datasets")
@@ -678,9 +730,7 @@ model Prompt {
labels String[] @default([])
@@unique([projectId, name, version])
@@index([projectId, name, version])
@@index([projectId, id])
@@index([projectId])
@@index([createdAt])
@@index([updatedAt])
@@index([tags(ops: ArrayOps)], type: Gin)
@@ -707,8 +757,6 @@ model Model {
tokenizerConfig Json? @map("tokenizer_config")
@@unique([projectId, modelName, startDate, unit])
@@index([projectId, modelName, startDate, unit])
@@index([projectId, modelName])
@@index(modelName)
@@map("models")
}
@@ -757,7 +805,6 @@ model EvalTemplate {
@@unique([projectId, name, version])
@@index([projectId, id])
@@index([projectId])
@@map("eval_templates")
}
@@ -792,7 +839,6 @@ model JobConfiguration {
JobExecution JobExecution[]
@@index([projectId, id])
@@index([projectId])
@@map("job_configurations")
}
@@ -827,7 +873,6 @@ model JobExecution {
@@index([projectId, status])
@@index([projectId, id])
@@index([projectId])
@@index([jobConfigurationId])
@@index([jobOutputScoreId])
@@index([jobInputTraceId])
@@ -860,7 +905,6 @@ model PosthogIntegration {
enabled Boolean
createdAt DateTime @default(now()) @map("created_at")
@@index([projectId])
@@map("posthog_integrations")
}
+171 -41
View File
@@ -5,6 +5,7 @@ import {
ObservationType,
ScoreSource,
ScoreDataType,
AnnotationQueueObjectType,
} from "../src/index";
import { hash } from "bcryptjs";
import { parseArgs } from "node:util";
@@ -77,6 +78,9 @@ async function main() {
create: {
id: seedOrgId,
name: "Seed Org",
cloudConfig: {
plan: "Team",
},
},
});
@@ -250,6 +254,11 @@ async function main() {
project2,
]);
const queueIds = await generateQueuesForProject(
[project1, project2],
configIdsAndNames
);
const promptIds = await generatePromptsForProject([project1, project2]);
const envTags = [null, "development", "staging", "production"];
@@ -257,19 +266,27 @@ async function main() {
const traceVolume = environment === "load" ? LOAD_TRACE_VOLUME : 100;
const { traces, observations, scores, sessions, events, comments } =
createObjects(
traceVolume,
envTags,
colorTags,
project1,
project2,
promptIds,
configIdsAndNames,
);
const {
traces,
observations,
scores,
sessions,
events,
comments,
queueItems,
} = createObjects(
traceVolume,
envTags,
colorTags,
project1,
project2,
promptIds,
queueIds,
configIdsAndNames
);
logger.info(
`Seeding ${traces.length} traces, ${observations.length} observations, and ${scores.length} scores`,
`Seeding ${traces.length} traces, ${observations.length} observations, and ${scores.length} scores`
);
await uploadObjects(
@@ -279,6 +296,7 @@ async function main() {
sessions,
events,
comments,
queueItems
);
// If openai key is in environment, add it to the projects LLM API keys
@@ -296,7 +314,7 @@ async function main() {
});
} else {
logger.warn(
"No OPENAI_API_KEY found in environment. Skipping seeding LLM API key.",
"No OPENAI_API_KEY found in environment. Skipping seeding LLM API key."
);
}
@@ -431,7 +449,7 @@ async function main() {
for (const datasetItemId of datasetItemIds) {
const relevantObservations = observations.filter(
(o) => o.projectId === project2.id,
(o) => o.projectId === project2.id
);
const observation =
relevantObservations[
@@ -474,6 +492,7 @@ async function uploadObjects(
sessions: Prisma.TraceSessionCreateManyInput[],
events: Prisma.ObservationCreateManyInput[],
comments: Prisma.CommentCreateManyInput[],
queueItems: Prisma.AnnotationQueueItemCreateManyInput[]
) {
let promises: Prisma.PrismaPromise<unknown>[] = [];
@@ -487,14 +506,14 @@ async function uploadObjects(
},
create: chunk[0]!,
update: {},
}),
})
);
});
for (let i = 0; i < promises.length; i++) {
if (i + 1 >= promises.length || i % Math.ceil(promises.length / 10) === 0)
logger.info(
`Seeding of Sessions ${((i + 1) / promises.length) * 100}% complete`,
`Seeding of Sessions ${((i + 1) / promises.length) * 100}% complete`
);
await promises[i];
}
@@ -505,13 +524,13 @@ async function uploadObjects(
promises.push(
prisma.trace.createMany({
data: chunk,
}),
})
);
});
for (let i = 0; i < promises.length; i++) {
if (i + 1 >= promises.length || i % Math.ceil(promises.length / 10) === 0)
logger.info(
`Seeding of Traces ${((i + 1) / promises.length) * 100}% complete`,
`Seeding of Traces ${((i + 1) / promises.length) * 100}% complete`
);
await promises[i];
}
@@ -521,14 +540,14 @@ async function uploadObjects(
promises.push(
prisma.observation.createMany({
data: chunk,
}),
})
);
});
for (let i = 0; i < promises.length; i++) {
if (i + 1 >= promises.length || i % Math.ceil(promises.length / 10) === 0)
logger.info(
`Seeding of Observations ${((i + 1) / promises.length) * 100}% complete`,
`Seeding of Observations ${((i + 1) / promises.length) * 100}% complete`
);
await promises[i];
}
@@ -538,14 +557,14 @@ async function uploadObjects(
promises.push(
prisma.observation.createMany({
data: chunk,
}),
})
);
});
for (let i = 0; i < promises.length; i++) {
if (i + 1 >= promises.length || i % Math.ceil(promises.length / 10) === 0)
logger.info(
`Seeding of Events ${((i + 1) / promises.length) * 100}% complete`,
`Seeding of Events ${((i + 1) / promises.length) * 100}% complete`
);
await promises[i];
}
@@ -555,13 +574,13 @@ async function uploadObjects(
promises.push(
prisma.score.createMany({
data: chunk,
}),
})
);
});
for (let i = 0; i < promises.length; i++) {
if (i + 1 >= promises.length || i % Math.ceil(promises.length / 10) === 0)
logger.info(
`Seeding of Scores ${((i + 1) / promises.length) * 100}% complete`,
`Seeding of Scores ${((i + 1) / promises.length) * 100}% complete`
);
await promises[i];
}
@@ -571,13 +590,29 @@ async function uploadObjects(
promises.push(
prisma.comment.createMany({
data: chunk,
}),
})
);
});
for (let i = 0; i < promises.length; i++) {
if (i + 1 >= promises.length || i % Math.ceil(promises.length / 10) === 0)
logger.info(
`Seeding of Comments ${((i + 1) / promises.length) * 100}% complete`,
`Seeding of Comments ${((i + 1) / promises.length) * 100}% complete`
);
await promises[i];
}
promises = [];
chunk(queueItems, chunkSize).forEach((chunk) => {
promises.push(
prisma.annotationQueueItem.createMany({
data: chunk,
})
);
});
for (let i = 0; i < promises.length; i++) {
if (i + 1 >= promises.length || i % Math.ceil(promises.length / 10) === 0)
logger.info(
`Seeding of Annotation Queue Items ${((i + 1) / promises.length) * 100}% complete`
);
await promises[i];
}
@@ -590,6 +625,7 @@ function createObjects(
project1: Project,
project2: Project,
promptIds: Map<string, string[]>,
queueIds: Map<string, string[]>,
configParams: Map<
string,
{
@@ -598,7 +634,7 @@ function createObjects(
dataType: ScoreDataType;
categories: ConfigCategory[] | null;
}[]
>,
>
) {
const traces: Prisma.TraceCreateManyInput[] = [];
const observations: Prisma.ObservationCreateManyInput[] = [];
@@ -607,12 +643,13 @@ function createObjects(
const events: Prisma.ObservationCreateManyInput[] = [];
const configs: Prisma.ScoreConfigCreateManyInput[] = [];
const comments: Prisma.CommentCreateManyInput[] = [];
const queueItems: Prisma.AnnotationQueueItemCreateManyInput[] = [];
for (let i = 0; i < traceVolume; i++) {
// print progress to console with a progress bar that refreshes every 10 iterations
// random date within last 90 days, with a linear bias towards more recent dates
const traceTs = new Date(
Date.now() - Math.floor(Math.random() ** 1.5 * 90 * 24 * 60 * 60 * 1000),
Date.now() - Math.floor(Math.random() ** 1.5 * 90 * 24 * 60 * 60 * 1000)
);
const envTag = envTags[Math.floor(Math.random() * envTags.length)];
@@ -689,6 +726,23 @@ function createObjects(
}),
};
const queueItem = [
...(Math.random() > 0.9
? [
{
queueId: queueIds.get(projectId)?.[
Math.floor(Math.random() * 2)
] as string,
objectId: trace.id,
objectType: AnnotationQueueObjectType.TRACE,
projectId,
},
]
: []),
];
queueItems.push(...queueItem);
const traceScores = [
...(Math.random() > 0.5
? [
@@ -753,11 +807,11 @@ function createObjects(
for (let j = 0; j < Math.floor(Math.random() * 10) + 1; j++) {
// add between 1 and 30 ms to trace timestamp
const spanTsStart = new Date(
traceTs.getTime() + Math.floor(Math.random() * 30),
traceTs.getTime() + Math.floor(Math.random() * 30)
);
// random duration of upto 5000ms
const spanTsEnd = new Date(
spanTsStart.getTime() + Math.floor(Math.random() * 5000),
spanTsStart.getTime() + Math.floor(Math.random() * 5000)
);
const span = {
@@ -792,22 +846,22 @@ function createObjects(
const generationTsStart = new Date(
spanTsStart.getTime() +
Math.floor(
Math.random() * (spanTsEnd.getTime() - spanTsStart.getTime()),
),
Math.random() * (spanTsEnd.getTime() - spanTsStart.getTime())
)
);
const generationTsEnd = new Date(
generationTsStart.getTime() +
Math.floor(
Math.random() *
(spanTsEnd.getTime() - generationTsStart.getTime()),
),
(spanTsEnd.getTime() - generationTsStart.getTime())
)
);
// somewhere in the middle
const generationTsCompletionStart = new Date(
generationTsStart.getTime() +
Math.floor(
(generationTsEnd.getTime() - generationTsStart.getTime()) / 3,
),
(generationTsEnd.getTime() - generationTsStart.getTime()) / 3
)
);
const promptTokens = Math.floor(Math.random() * 1000) + 300;
@@ -828,7 +882,7 @@ function createObjects(
const promptId =
promptIds.get(projectId)![
Math.floor(
Math.random() * Math.floor(promptIds.get(projectId)!.length / 2),
Math.random() * Math.floor(promptIds.get(projectId)!.length / 2)
)
];
@@ -910,8 +964,8 @@ function createObjects(
const eventTs = new Date(
spanTsStart.getTime() +
Math.floor(
Math.random() * (spanTsEnd.getTime() - spanTsStart.getTime()),
),
Math.random() * (spanTsEnd.getTime() - spanTsStart.getTime())
)
);
events.push({
@@ -933,7 +987,7 @@ function createObjects(
}
// find unique sessions by id and projectid
const uniqueSessions: Prisma.TraceSessionCreateManyInput[] = Array.from(
new Set(sessions.map((session) => JSON.stringify(session))),
new Set(sessions.map((session) => JSON.stringify(session)))
).map((session) => JSON.parse(session) as Prisma.TraceSessionCreateManyInput);
return {
@@ -941,6 +995,7 @@ function createObjects(
observations,
scores,
configs,
queueItems,
sessions: uniqueSessions,
events,
comments,
@@ -954,7 +1009,7 @@ async function generatePromptsForProject(projects: Project[]) {
projects.map(async (project) => {
const promptIdsForProject = await generatePrompts(project);
promptIds.set(project.id, promptIdsForProject);
}),
})
);
return promptIds;
}
@@ -1140,7 +1195,7 @@ async function generateConfigsForProject(projects: Project[]) {
projects.map(async (project) => {
const configNameAndId = await generateConfigs(project);
projectIdsToConfigs.set(project.id, configNameAndId);
}),
})
);
return projectIdsToConfigs;
}
@@ -1218,6 +1273,7 @@ async function generateConfigs(project: Project) {
return configNameAndId;
}
function getGenerationInputOutput(): {
input: Prisma.InputJsonValue;
output: Prisma.InputJsonValue;
@@ -1278,3 +1334,77 @@ function getGenerationInputOutput(): {
return { input, output };
}
async function generateQueuesForProject(
projects: Project[],
configIdsAndNames: Map<
string,
{
name: string;
id: string;
dataType: ScoreDataType;
categories: ConfigCategory[] | null;
}[]
>
) {
const projectIdsToQueues: Map<string, string[]> = new Map();
await Promise.all(
projects.map(async (project) => {
const queueIds = await generateQueues(
project,
configIdsAndNames.get(project.id) ?? []
);
projectIdsToQueues.set(project.id, queueIds);
})
);
return projectIdsToQueues;
}
async function generateQueues(
project: Project,
configIdsAndNames: {
name: string;
id: string;
dataType: ScoreDataType;
categories: ConfigCategory[] | null;
}[]
) {
const queueIds: string[] = [];
const queues = [
{
id: `queue-${v4()}`,
name: "Default",
description: "Default queue",
scoreConfigIds: configIdsAndNames.map((config) => config.id),
projectId: project.id,
},
{
id: `queue-${v4()}`,
name: "Correction",
scoreConfigIds: [configIdsAndNames[0].id],
projectId: project.id,
},
];
for (const queue of queues) {
await prisma.annotationQueue.upsert({
where: {
projectId_name: {
projectId: queue.projectId,
name: queue.name,
},
},
create: {
...queue,
},
update: {
id: queue.id,
},
});
queueIds.push(queue.id);
}
return queueIds;
}
+72 -36
View File
@@ -13,40 +13,81 @@ import {
import { DB } from ".";
import { logger } from "./server";
// Instantiated according to the Prisma documentation
// https://www.prisma.io/docs/orm/more/help-and-troubleshooting/help-articles/nextjs-prisma-client-dev-practices
export class PrismaClientSingleton {
private static instance: PrismaClient;
const prismaClientSingleton = () => {
const client = new PrismaClient<
Prisma.PrismaClientOptions,
"warn" | "error" | "query"
>({
log: [
{ emit: "event", level: "query" },
{ emit: "event", level: "error" },
{ emit: "event", level: "warn" },
],
});
public static getInstance(): PrismaClient {
if (PrismaClientSingleton.instance) {
return PrismaClientSingleton.instance;
}
if (env.NODE_ENV === "development") {
client.$on("query", (event) => {
logger.info(`prisma:query ${event.query}, ${event.duration}ms`);
const client = new PrismaClient<
Prisma.PrismaClientOptions,
"warn" | "error" | "query"
>({
log: [
{ emit: "event", level: "query" },
{ emit: "event", level: "error" },
{ emit: "event", level: "warn" },
],
});
if (env.NODE_ENV === "development") {
client.$on("query", (event) => {
logger.info(`prisma:query ${event.query}, ${event.duration}ms`);
});
}
client.$on("warn", (event) => {
logger.warn(`prisma:warn ${event.message}`);
});
client.$on("error", (event) => {
logger.error(`prisma:error ${event.message}`);
});
PrismaClientSingleton.instance = client;
return PrismaClientSingleton.instance;
}
}
client.$on("warn", (event) => {
logger.warn(`prisma:warn ${event.message}`);
});
export class KyselySingleton {
private static instance: { $kysely: Kysely<DB> };
client.$on("error", (event) => {
logger.error(`prisma:error ${event.message}`);
});
public static getInstance() {
if (KyselySingleton.instance) {
return KyselySingleton.instance;
}
return client;
};
KyselySingleton.instance = PrismaClientSingleton.getInstance().$extends(
kyselyExtension({
kysely: (driver) =>
new Kysely<DB>({
dialect: {
// This is where the magic happens!
createDriver: () => driver,
// Don't forget to customize these to match your database!
createAdapter: () => new PostgresAdapter(),
createIntrospector: (db) => new PostgresIntrospector(db),
createQueryCompiler: () => new PostgresQueryCompiler(),
},
}),
})
);
const kyselySingleton = (prismaClient: PrismaClient) => {
return prismaClient.$extends(
return KyselySingleton.instance;
}
}
declare const globalThis: {
prismaGlobal: PrismaClient | undefined;
kyselyPrismaGlobal: { $kysely: Kysely<DB> } | undefined;
} & typeof global;
if (process.env.NODE_ENV === "development") {
globalThis.prismaGlobal ??= new PrismaClient(); // regular instantiation
globalThis.kyselyPrismaGlobal ??= globalThis.prismaGlobal.$extends(
kyselyExtension({
kysely: (driver) =>
new Kysely<DB>({
@@ -59,18 +100,13 @@ const kyselySingleton = (prismaClient: PrismaClient) => {
createQueryCompiler: () => new PostgresQueryCompiler(),
},
}),
}),
})
);
};
declare global {
// eslint-disable-next-line no-var
var prisma: undefined | ReturnType<typeof prismaClientSingleton>;
var kyselyPrisma: undefined | ReturnType<typeof kyselySingleton>;
}
export const prisma = globalThis.prisma ?? prismaClientSingleton();
export const kyselyPrisma = globalThis.kyselyPrisma ?? kyselySingleton(prisma);
export const prisma =
globalThis.prismaGlobal ?? PrismaClientSingleton.getInstance();
export const kyselyPrisma =
globalThis.kyselyPrismaGlobal ?? KyselySingleton.getInstance();
export * from "@prisma/client";
if (process.env.NODE_ENV !== "production") globalThis.prisma = prisma;
+3 -2
View File
@@ -1,4 +1,5 @@
import { z } from "zod";
import { removeEmptyEnvVariables } from "./utils/environment";
const EnvSchema = z.object({
NODE_ENV: z
@@ -21,7 +22,7 @@ const EnvSchema = z.object({
.string()
.length(
64,
"ENCRYPTION_KEY must be 256 bits, 64 string characters in hex format, generate via: openssl rand -hex 32",
"ENCRYPTION_KEY must be 256 bits, 64 string characters in hex format, generate via: openssl rand -hex 32"
)
.optional(),
LANGFUSE_CACHE_PROMPT_ENABLED: z.enum(["true", "false"]).default("false"),
@@ -45,4 +46,4 @@ const EnvSchema = z.object({
LANGFUSE_LOG_FORMAT: z.enum(["text", "json"]).default("text"),
});
export const env = EnvSchema.parse(process.env);
export const env = EnvSchema.parse(removeEmptyEnvVariables(process.env));
@@ -32,6 +32,7 @@ const CreateAnnotationScoreBase = z.object({
configId: z.string().optional(),
observationId: z.string().optional(),
comment: z.string().nullish(),
queueId: z.string().nullish(),
});
const UpdateAnnotationScoreBase = CreateAnnotationScoreBase.extend({
@@ -57,3 +58,15 @@ export const UpdateAnnotationScoreData = z.discriminatedUnion("dataType", [
UpdateAnnotationScoreBase.merge(CategoricalData),
UpdateAnnotationScoreBase.merge(BooleanData),
]);
// annotation queues
export const CreateQueueData = z.object({
name: z.string().min(1).max(35),
description: z.string().max(1000).optional(),
scoreConfigIds: z.array(z.string()).min(1, {
message: "At least 1 score config must be selected",
}),
});
export type CreateQueue = z.infer<typeof CreateQueueData>;
@@ -19,6 +19,8 @@ export enum BatchExportFileFormat {
export enum BatchExportTableName {
Sessions = "sessions",
Traces = "traces",
Generations = "generations",
}
export const exportOptions: Record<
+2
View File
@@ -40,3 +40,5 @@ export { type DB } from "../prisma/generated/types";
// errors
export * from "./errors/index";
export * from "./utils/environment";
@@ -6,6 +6,7 @@ export const RateLimitResource = z.enum([
"public-api",
"public-api-metrics",
"prompts",
"legacy-ingestion",
]);
// result of the rate limit check.
+5 -4
View File
@@ -52,7 +52,8 @@ export const observationsTableCols: ColumnDefinition[] = [
name: "Time To First Token (s)",
id: "timeToFirstToken",
type: "number",
internal: 'o."completion_start_time" - o."start_time"',
internal:
'EXTRACT(EPOCH FROM (o."completion_start_time" - o."start_time"))',
nullable: true,
},
{
@@ -62,10 +63,10 @@ export const observationsTableCols: ColumnDefinition[] = [
internal: '"latency"',
},
{
name: "Time Per Output Token (s)",
id: "timePerOutputToken",
name: "Tokens per second",
id: "tokensPerSecond",
type: "number",
internal: '"latency" / o."completion_tokens"',
internal: 'o."completion_tokens" / "latency"',
nullable: true,
},
{
+13 -13
View File
@@ -26,7 +26,7 @@ const arrayOperatorReplacements = {
export function tableColumnsToSqlFilterAndPrefix(
filters: FilterState,
tableColumns: ColumnDefinition[],
table: TableNames,
table: TableNames
): Prisma.Sql {
const sql = tableColumnsToSqlFilter(filters, tableColumns, table);
if (sql === Prisma.empty) {
@@ -42,14 +42,14 @@ export function tableColumnsToSqlFilterAndPrefix(
export function tableColumnsToSqlFilter(
filters: FilterState,
tableColumns: ColumnDefinition[],
table: TableNames,
table: TableNames
): Prisma.Sql {
const internalFilters = filters.map((filter) => {
// Get column definition to map column to internal name, e.g. "t.id"
const col = tableColumns.find(
(c) =>
// TODO: Only use id instead of name
c.name === filter.column || c.id === filter.column,
c.name === filter.column || c.id === filter.column
);
if (!col) {
logger.error("Invalid filter column", filter.column);
@@ -71,13 +71,13 @@ export function tableColumnsToSqlFilter(
? Prisma.raw(
arrayOperatorReplacements[
filter.operator as keyof typeof arrayOperatorReplacements
],
]
)
: filter.operator in operatorReplacements
? Prisma.raw(
operatorReplacements[
filter.operator as keyof typeof operatorReplacements
],
]
)
: Prisma.raw(filter.operator); //checked by zod
@@ -97,13 +97,13 @@ export function tableColumnsToSqlFilter(
break;
case "stringOptions":
valuePrisma = Prisma.sql`(${Prisma.join(
filter.value.map((v) => Prisma.sql`${v}`),
filter.value.map((v) => Prisma.sql`${v}`)
)})`;
break;
case "arrayOptions":
valuePrisma = Prisma.sql`ARRAY[${Prisma.join(
filter.value.map((v) => Prisma.sql`${v}`),
", ",
", "
)}] `;
break;
@@ -123,12 +123,12 @@ export function tableColumnsToSqlFilter(
filter.type === "string" || filter.type === "stringObject"
? [
["contains", "does not contain", "ends with"].includes(
filter.operator,
filter.operator
)
? Prisma.raw("'%' || ")
: Prisma.empty,
["contains", "does not contain", "starts with"].includes(
filter.operator,
filter.operator
)
? Prisma.raw(" || '%'")
: Prisma.empty,
@@ -152,7 +152,7 @@ export function tableColumnsToSqlFilter(
const castValueToPostgresTypes = (
column: ColumnDefinition,
table: TableNames,
table: TableNames
) => {
return column.name === "type" &&
(table === "observations" ||
@@ -168,7 +168,7 @@ const dateOperators = filterOperators["datetime"];
export const datetimeFilterToPrismaSql = (
safeColumn: string,
operator: (typeof dateOperators)[number],
value: Date,
value: Date
) => {
if (!dateOperators.includes(operator)) {
throw new Error("Invalid operator: " + operator);
@@ -178,12 +178,12 @@ export const datetimeFilterToPrismaSql = (
}
return Prisma.sql`AND ${Prisma.raw(safeColumn)} ${Prisma.raw(
operator,
operator
)} ${value}::timestamp with time zone at time zone 'UTC'`;
};
export const datetimeFilterToPrisma = (
timestampFilter: z.infer<typeof timeFilter>,
timestampFilter: z.infer<typeof timeFilter>
) => {
const prismaTimestampFilter =
timestampFilter.operator === ">="
@@ -10,6 +10,7 @@ import {
traceEvent,
scoreEvent,
sdkLogEvent,
ingestionEvent,
} from "../types";
import { validateAndInflateScore } from "../validateAndInflateScore";
import { Trace, Observation, Score, Prisma, Model } from "@prisma/client";
@@ -28,6 +29,32 @@ export interface EventProcessor {
): Promise<Trace | Observation | Score> | undefined;
}
export const getProcessorForEvent = (
event: z.infer<typeof ingestionEvent>,
calculateTokenDelegate: (p: {
model: Model;
text: unknown;
}) => number | undefined,
): EventProcessor => {
switch (event.type) {
case eventTypes.TRACE_CREATE:
return new TraceProcessor(event);
case eventTypes.OBSERVATION_CREATE:
case eventTypes.OBSERVATION_UPDATE:
case eventTypes.EVENT_CREATE:
case eventTypes.SPAN_CREATE:
case eventTypes.SPAN_UPDATE:
case eventTypes.GENERATION_CREATE:
case eventTypes.GENERATION_UPDATE:
return new ObservationProcessor(event, calculateTokenDelegate);
case eventTypes.SCORE_CREATE: {
return new ScoreProcessor(event);
}
case eventTypes.SDK_LOG:
return new SdkLogProcessor(event);
}
};
export class ObservationProcessor implements EventProcessor {
event: ObservationEvent;
calculateTokenDelegate: (p: {
@@ -48,7 +75,7 @@ export class ObservationProcessor implements EventProcessor {
async convertToObservation(
apiScope: LegacyIngestionAccessScope,
existingObservation: Observation | null,
existingObservation: Omit<Observation, "input" | "output"> | null,
): Promise<{
id: string;
create: Prisma.ObservationUncheckedCreateInput;
@@ -120,7 +147,8 @@ export class ObservationProcessor implements EventProcessor {
// Token counts
const [newInputCount, newOutputCount] =
"usage" in this.event.body
? this.calculateTokenCounts(
? await this.calculateTokenCounts(
apiScope.projectId,
this.event.body,
this.calculateTokenDelegate,
internalModel ?? undefined,
@@ -311,7 +339,8 @@ export class ObservationProcessor implements EventProcessor {
};
}
calculateTokenCounts(
async calculateTokenCounts(
projectId: string,
body:
| z.infer<typeof legacyObservationCreateEvent>["body"]
| z.infer<typeof generationCreateEvent>["body"],
@@ -320,29 +349,59 @@ export class ObservationProcessor implements EventProcessor {
text: unknown;
}) => number | undefined,
model?: Model,
existingObservation?: Observation,
existingObservation?: Omit<Observation, "input" | "output">,
) {
const newPromptTokens =
body.usage?.input ??
((body.input || existingObservation?.input) && model && model.tokenizerId
? calculateTokenDelegate({
model: model,
text: body.input ?? existingObservation?.input,
})
: undefined);
let newPromptTokens = body.usage?.input;
if (newPromptTokens === undefined && model && model.tokenizerId) {
if (body.input) {
newPromptTokens = calculateTokenDelegate({
model: model,
text: body.input,
});
} else {
logger.info(
`No input provided, trying to calculate for id: ${existingObservation?.id}`,
);
const observationInput = await prisma.observation.findFirst({
where: { id: existingObservation?.id, projectId: projectId },
select: {
input: true,
},
});
const newCompletionTokens =
body.usage?.output ??
((body.output || existingObservation?.output) &&
model &&
model.tokenizerId
? calculateTokenDelegate({
model: model,
text: body.output ?? existingObservation?.output,
})
: undefined);
newPromptTokens = calculateTokenDelegate({
model: model,
text: observationInput?.input,
});
}
}
return [newPromptTokens, newCompletionTokens];
let newCompletionTokens = body.usage?.output;
if (newCompletionTokens === undefined && model && model.tokenizerId) {
if (body.output) {
newCompletionTokens = calculateTokenDelegate({
model: model,
text: body.output,
});
} else {
logger.info(
`No output provided, trying to calculate for id: ${existingObservation?.id}`,
);
const observationOutput = await prisma.observation.findFirst({
where: { id: existingObservation?.id, projectId: projectId },
select: {
output: true,
},
});
newCompletionTokens = calculateTokenDelegate({
model: model,
text: observationOutput?.output,
});
}
}
return [newPromptTokens ?? undefined, newCompletionTokens ?? undefined];
}
static calculateTokenCosts(
@@ -410,7 +469,43 @@ export class ObservationProcessor implements EventProcessor {
const existingObservation = this.event.body.id
? await prisma.observation.findFirst({
where: { id: this.event.body.id },
select: {
// do not select I/O to spare our db
input: false,
output: false,
id: true,
traceId: true,
projectId: true,
type: true,
startTime: true,
endTime: true,
name: true,
metadata: true,
parentObservationId: true,
level: true,
statusMessage: true,
version: true,
createdAt: true,
updatedAt: true,
model: true,
internalModelId: true,
modelParameters: true,
promptTokens: true,
completionTokens: true,
totalTokens: true,
unit: true,
inputCost: true,
outputCost: true,
totalCost: true,
calculatedInputCost: true,
calculatedOutputCost: true,
calculatedTotalCost: true,
completionStartTime: true,
promptId: true,
internalModel: true,
},
where: { id: this.event.body.id, projectId: apiScope.projectId },
})
: null;
@@ -436,6 +531,7 @@ export class ObservationProcessor implements EventProcessor {
});
}
}
export class TraceProcessor implements EventProcessor {
event: z.infer<typeof traceEvent>;
@@ -457,7 +553,7 @@ export class TraceProcessor implements EventProcessor {
const internalId = body.id ?? v4();
logger.info(
logger.debug(
`Trying to create trace, project ${apiScope.projectId}, id: ${internalId}`,
);
@@ -545,6 +641,7 @@ export class TraceProcessor implements EventProcessor {
return upsertedTrace;
}
}
export class ScoreProcessor implements EventProcessor {
event: z.infer<typeof scoreEvent>;
@@ -2,14 +2,8 @@ import { env } from "node:process";
import z from "zod";
import { ForbiddenError, UnauthorizedError } from "../../../errors";
import { eventTypes, ingestionApiSchema, ingestionEvent } from "../types";
import {
EventProcessor,
TraceProcessor,
ObservationProcessor,
ScoreProcessor,
SdkLogProcessor,
} from "./EventProcessor";
import { EventBodyType, EventName, TraceUpsertEventType } from "../../queues";
import { getProcessorForEvent } from "./EventProcessor";
import { TraceUpsertEventType } from "../../queues";
import {
convertTraceUpsertEventsToRedisEvents,
getTraceUpsertQueue,
@@ -52,7 +46,7 @@ export const handleBatch = async (
authCheck: LegacyIngestionAuthHeaderVerificationResult,
calculateTokenDelegate: (p: TokenCountInput) => number | undefined,
) => {
logger.info(`handling ingestion ${events.length} events`);
logger.debug(`handling ingestion ${events.length} events`);
if (!authCheck.validKey) throw new UnauthorizedError(authCheck.error);
@@ -144,40 +138,18 @@ const handleSingleEvent = async (
const cleanedEvent = ingestionEvent.parse(cleanEvent(event));
const { type } = cleanedEvent;
let processor: EventProcessor;
switch (type) {
case eventTypes.TRACE_CREATE:
processor = new TraceProcessor(cleanedEvent);
break;
case eventTypes.OBSERVATION_CREATE:
case eventTypes.OBSERVATION_UPDATE:
case eventTypes.EVENT_CREATE:
case eventTypes.SPAN_CREATE:
case eventTypes.SPAN_UPDATE:
case eventTypes.GENERATION_CREATE:
case eventTypes.GENERATION_UPDATE:
processor = new ObservationProcessor(
cleanedEvent,
calculateTokenDelegate,
);
break;
case eventTypes.SCORE_CREATE: {
processor = new ScoreProcessor(cleanedEvent);
break;
}
case eventTypes.SDK_LOG:
processor = new SdkLogProcessor(cleanedEvent);
}
// Deny access to non-score events if the access level is not "all"
// This is an additional safeguard to auth checks in EventProcessor
if (apiScope.accessLevel !== "all" && type !== eventTypes.SCORE_CREATE) {
if (
apiScope.accessLevel !== "all" &&
cleanedEvent.type !== eventTypes.SCORE_CREATE
) {
throw new ForbiddenError("Access denied. Event type not allowed.");
}
return await processor.process(apiScope);
return getProcessorForEvent(cleanedEvent, calculateTokenDelegate).process(
apiScope,
);
};
// cleans NULL characters from the event
@@ -208,7 +180,7 @@ export const isNotNullOrUndefined = <T>(
export const isUndefinedOrNull = <T>(val?: T | null): val is undefined | null =>
val === undefined || val === null;
export const sendToWorkerIfEnvironmentConfigured = async (
export const addTracesToTraceUpsertQueue = async (
batchResults: BatchResult[],
projectId: string,
): Promise<void> => {
@@ -235,32 +207,6 @@ export const sendToWorkerIfEnvironmentConfigured = async (
}
await queue.addBulk(convertTraceUpsertEventsToRedisEvents(traceEvents));
} else if (
env.LANGFUSE_WORKER_HOST &&
env.LANGFUSE_WORKER_PASSWORD &&
env.NEXT_PUBLIC_LANGFUSE_CLOUD_REGION
) {
logger.info(`Sending ${traceEvents.length} events to worker via HTTP`);
const body: EventBodyType = {
name: EventName.TraceUpsert,
payload: traceEvents,
};
if (traceEvents.length > 0) {
await fetch(`${env.LANGFUSE_WORKER_HOST}/api/events`, {
method: "POST",
headers: {
"Content-Type": "application/json",
Authorization:
"Basic " +
Buffer.from(
"admin" + ":" + env.LANGFUSE_WORKER_PASSWORD,
).toString("base64"),
},
body: JSON.stringify(body),
signal: AbortSignal.timeout(8 * 1000),
});
}
}
} catch (error) {
logger.error("Error sending events to worker", error);
@@ -8,7 +8,7 @@ export async function findModel(p: {
unit?: string;
startTime?: Date;
};
existingDbObservation?: Observation;
existingDbObservation?: Omit<Observation, "input" | "output">;
}): Promise<Model | null> {
const { event, existingDbObservation } = p;
// either get the model from the existing observation
+11 -10
View File
@@ -115,7 +115,7 @@ export const CreateGenerationBody = CreateSpanBody.extend({
z.array(z.string()),
z.record(z.string()),
])
.nullish()
.nullish(),
)
.nullish(),
usage: usage,
@@ -143,7 +143,7 @@ export const UpdateGenerationBody = UpdateSpanBody.extend({
z.array(z.string()),
z.record(z.string()),
])
.nullish()
.nullish(),
)
.nullish(),
usage: usage,
@@ -174,14 +174,14 @@ export const ScoreBody = z.discriminatedUnion("dataType", [
value: z.number(),
dataType: z.literal("NUMERIC"),
configId: z.string().nullish(),
})
}),
),
BaseScoreBody.merge(
z.object({
value: z.string(),
dataType: z.literal("CATEGORICAL"),
configId: z.string().nullish(),
})
}),
),
BaseScoreBody.merge(
z.object({
@@ -191,14 +191,14 @@ export const ScoreBody = z.discriminatedUnion("dataType", [
}),
dataType: z.literal("BOOLEAN"),
configId: z.string().nullish(),
})
}),
),
BaseScoreBody.merge(
z.object({
value: z.union([z.string(), z.number()]),
dataType: z.undefined(),
configId: z.string().nullish(),
})
}),
),
]);
@@ -243,7 +243,7 @@ export const LegacyGenerationsCreateSchema = z.object({
modelParameters: z
.record(
z.string(),
z.union([z.string(), z.number(), z.boolean()]).nullish()
z.union([z.string(), z.number(), z.boolean()]).nullish(),
)
.nullish(),
prompt: jsonSchema.nullish(),
@@ -267,7 +267,7 @@ export const LegacyGenerationPatchSchema = z.object({
modelParameters: z
.record(
z.string(),
z.union([z.string(), z.number(), z.boolean()]).nullish()
z.union([z.string(), z.number(), z.boolean()]).nullish(),
)
.nullish(),
prompt: jsonSchema.nullish(),
@@ -291,7 +291,7 @@ export const LegacyObservationBody = z.object({
modelParameters: z
.record(
z.string(),
z.union([z.string(), z.number(), z.boolean()]).nullish()
z.union([z.string(), z.number(), z.boolean()]).nullish(),
)
.nullish(),
input: jsonSchema.nullish(),
@@ -306,6 +306,7 @@ export const LegacyObservationBody = z.object({
export const SdkLogEvent = z.object({
log: jsonSchema,
id: z.string().nullish(), // Not used, but makes downstream processing easier.
});
// definitions for the ingestion API
@@ -402,7 +403,7 @@ export const ingestionBatchEvent = z.array(ingestionEvent);
export type IngestionBatchEventType = z.infer<typeof ingestionBatchEvent>;
export const ingestionEventWithProjectId = ingestionEvent.and(
z.object({ projectId: z.string() })
z.object({ projectId: z.string() }),
);
export type IngestionEventWithProjectIdType = z.infer<
typeof ingestionEventWithProjectId
@@ -12,13 +12,3 @@ Throughout our applications we want to use as much Otel as possible. This helps
- `web` and `worker` have an instrumentation.ts file, which configures otel for the application.
- For trpc, we use `@baselime/trpc-opentelemetry-middleware` to enrich spans with trpc inputs and outputs.
- When building adding new infrastructure, we should search for auto instumentations for our code base.
## Library support
- `dd-trace` has a direct dependency of `@opentelemetry/api` version < `1.9` [GH](https://github.com/DataDog/dd-trace-js/blob/ed9b0b30f7b0283579a9bf8c18e1f9deab18fecf/package.json#L81).
- `@prisma/instrumentation` requires at least `@opentelemetry/api` version `1.8` [GH](https://github.com/prisma/prisma/blob/d780290b13754420abcfa5d7592f02049c6cc005/packages/instrumentation/package.json#L25)
- We need to ensure that all other libraries we use are compatible with `@opentelemetry/api` version `1.8`. If we have a package using a later version, this will break the instrumentation and crash the container. We can check the versions with the following command:
```bash
pnpm --filter=web why @opentelemetry/api
```
@@ -3,54 +3,42 @@ import * as dd from "dd-trace";
// type CallbackFn<T> = () => T;
export type TCarrier = {
traceparent?: string;
tracestate?: string;
};
export type SpanCtx = {
name: string;
spanKind?: opentelemetry.SpanKind; // https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/trace/api.md#spankind
rootSpan?: boolean; // https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/overview.md#traces
traceScope?: string;
traceContext?: TCarrier;
};
type AsyncCallbackFn<T> = () => Promise<T>;
type AsyncCallbackFn<T> = (span: opentelemetry.Span) => Promise<T>;
export async function instrumentAsync<T>(
ctx: SpanCtx,
callback: AsyncCallbackFn<T>
callback: AsyncCallbackFn<T>,
): Promise<T> {
return await getTracer(ctx.traceScope ?? callback.name).startActiveSpan(
ctx.name,
{
root: ctx.rootSpan,
kind: ctx.spanKind,
},
async (span) => {
try {
const result = await callback();
span.end();
return result;
} catch (ex) {
traceException(ex as opentelemetry.Exception, span);
span.end();
throw ex;
}
}
);
}
const activeContext = ctx.traceContext
? opentelemetry.propagation.extract(
opentelemetry.context.active(),
ctx.traceContext,
)
: opentelemetry.context.active();
type SyncCallbackFn<T> = () => T;
export function instrumentSync<T>(
ctx: SpanCtx,
callback: SyncCallbackFn<T>
): T {
return getTracer(ctx.traceScope ?? callback.name).startActiveSpan(
ctx.name,
{
root: ctx.rootSpan,
root: !Boolean(ctx.traceContext) && ctx.rootSpan,
kind: ctx.spanKind,
},
(span) => {
activeContext,
async (span) => {
try {
const result = callback();
const result = await callback(span);
span.end();
return result;
} catch (ex) {
@@ -58,7 +46,41 @@ export function instrumentSync<T>(
span.end();
throw ex;
}
}
},
);
}
type SyncCallbackFn<T> = (span: opentelemetry.Span) => T;
export function instrumentSync<T>(
ctx: SpanCtx,
callback: SyncCallbackFn<T>,
): T {
const activeContext = ctx.traceContext
? opentelemetry.propagation.extract(
opentelemetry.context.active(),
ctx.traceContext,
)
: opentelemetry.context.active();
return getTracer(ctx.traceScope ?? callback.name).startActiveSpan(
ctx.name,
{
root: !Boolean(ctx.traceContext) && ctx.rootSpan,
kind: ctx.spanKind,
},
activeContext,
(span) => {
try {
const result = callback(span);
span.end();
return result;
} catch (ex) {
traceException(ex as opentelemetry.Exception, span);
span.end();
throw ex;
}
},
);
}
@@ -67,7 +89,7 @@ export const getCurrentSpan = () => opentelemetry.trace.getActiveSpan();
export const traceException = (
ex: unknown,
span?: opentelemetry.Span,
code?: string
code?: string,
) => {
const activeSpan = span ?? getCurrentSpan();
@@ -115,7 +137,7 @@ export const traceException = (
export const addUserToSpan = (
attributes: { userId?: string; projectId?: string; email?: string },
span?: opentelemetry.Span
span?: opentelemetry.Span,
) => {
const activeSpan = span ?? getCurrentSpan();
@@ -138,7 +160,7 @@ export const recordGauge = (
| {
[tag: string]: string | number;
}
| undefined
| undefined,
) => {
dd.dogstatsd.gauge(stat, value, tags);
};
@@ -146,7 +168,7 @@ export const recordGauge = (
export const recordIncrement = (
stat: string,
value?: number | undefined,
tags?: { [tag: string]: string | number } | undefined
tags?: { [tag: string]: string | number } | undefined,
) => {
dd.dogstatsd.increment(stat, value, tags);
};
@@ -154,7 +176,19 @@ export const recordIncrement = (
export const recordHistogram = (
stat: string,
value?: number | undefined,
tags?: { [tag: string]: string | number } | undefined
tags?: { [tag: string]: string | number } | undefined,
) => {
dd.dogstatsd.histogram(stat, value, tags);
};
/**
* Converts a queue name to the matching datadog metric name.
* Consumer only needs to append the relevant suffix.
*
* Example: `legacy-ingestion-queue` -> `langfuse.queue.legacy_ingestion`
*/
export const convertQueueNameToMetricName = (queueName: string): string => {
return (
"langfuse.queue." + queueName.replace(/-/g, "_").replace(/_queue$/, "")
);
};
@@ -1,3 +1,5 @@
import { type ZodSchema } from "zod";
import { ChatAnthropic } from "@langchain/anthropic";
import {
AIMessage,
@@ -12,20 +14,12 @@ import {
import { IterableReadableStream } from "@langchain/core/utils/stream";
import { ChatOpenAI } from "@langchain/openai";
import {
ChatMessage,
ChatMessageRole,
LLMFunctionCall,
ModelParams,
LLMAdapter,
} from "./types";
import zodToJsonSchema from "zod-to-json-schema";
import { JsonOutputFunctionsParser } from "langchain/output_parsers";
import { ChatMessage, ChatMessageRole, ModelParams, LLMAdapter } from "./types";
type LLMCompletionParams = {
messages: ChatMessage[];
modelParams: ModelParams;
functionCall?: LLMFunctionCall;
structuredOutputSchema?: ZodSchema;
callbacks?: BaseCallbackHandler[];
baseURL?: string;
apiKey?: string;
@@ -51,7 +45,7 @@ export async function fetchLLMCompletion(
export async function fetchLLMCompletion(
params: LLMCompletionParams & {
streaming: false;
functionCall: LLMFunctionCall;
structuredOutputSchema: ZodSchema;
}
): Promise<unknown>;
@@ -121,18 +115,41 @@ export async function fetchLLMCompletion(
throw new Error("This model provider is not supported.");
}
if (params.functionCall) {
const functionCallingModel = chatModel.bind({
functions: [
{
...params.functionCall,
parameters: zodToJsonSchema(params.functionCall.parameters),
},
],
function_call: { name: params.functionCall.name },
});
const outputParser = new JsonOutputFunctionsParser();
return await functionCallingModel.pipe(outputParser).invoke(finalMessages);
if (params.structuredOutputSchema) {
return await (chatModel as ChatOpenAI) // Typecast necessary due to https://github.com/langchain-ai/langchainjs/issues/6795
.withStructuredOutput(params.structuredOutputSchema)
.invoke(finalMessages);
}
/*
Workaround OpenAI o1 while in beta:
This is a temporary workaround to avoid sending system messages to OpenAI's O1 models.
O1 models do not support in beta:
- system messages
- top_p
- max_tokens at all, one has to use max_completion_tokens instead
- temperature different than 1
Reference: https://platform.openai.com/docs/guides/reasoning/beta-limitations
*/
if (modelParams.model.startsWith("o1-")) {
return await new ChatOpenAI({
openAIApiKey: apiKey,
modelName: modelParams.model,
temperature: 1,
maxTokens: undefined,
topP: undefined,
callbacks,
maxRetries,
configuration: {
baseURL,
},
})
.pipe(new StringOutputParser())
.invoke(
finalMessages.filter((message) => message._getType() !== "system")
);
}
if (streaming) {
+7 -1
View File
@@ -4,7 +4,7 @@ import z from "zod";
export type PromptVariable = { name: string; value: string; isUsed: boolean };
export type ChatMessage = {
role: ChatMessageRole;
role: ChatMessageRole | string; // Users may ingest any string as role via API/SDK
content: string;
};
@@ -22,6 +22,8 @@ export enum ChatMessageRole {
Assistant = "assistant",
}
export const ChatMessageDefaultRoleSchema = z.nativeEnum(ChatMessageRole);
export type ModelParams = {
provider: string;
adapter: LLMAdapter;
@@ -52,6 +54,10 @@ export const openAIModels = [
"gpt-4o-2024-05-13",
"gpt-4o-mini",
"gpt-4o-mini-2024-07-18",
"o1-preview",
"o1-preview-2024-09-12",
"o1-mini",
"o1-mini-2024-09-12",
"gpt-4-turbo-preview",
"gpt-4-1106-preview",
"gpt-4-0613",
+17
View File
@@ -1,5 +1,21 @@
import { env } from "../env";
import winston from "winston";
import { getCurrentSpan } from "./instrumentation";
const tracingFormat = function () {
return winston.format((info) => {
const span = getCurrentSpan();
if (span) {
const { spanId, traceId } = span.spanContext();
const traceIdEnd = traceId.slice(traceId.length / 2);
info["dd.trace_id"] = BigInt(`0x${traceIdEnd}`).toString();
info["dd.span_id"] = BigInt(`0x${spanId}`).toString();
info["trace_id"] = traceId;
info["span_id"] = spanId;
}
return info;
})();
};
const getWinstonLogger = (
nodeEnv: "development" | "production" | "test",
@@ -18,6 +34,7 @@ const getWinstonLogger = (
const jsonLoggerFormat = winston.format.combine(
winston.format.errors({ stack: true }),
winston.format.timestamp(),
tracingFormat(),
winston.format.json(),
);
@@ -0,0 +1,172 @@
import { ObservationView, Prisma } from "@prisma/client";
import {
datetimeFilterToPrismaSql,
tableColumnsToSqlFilterAndPrefix,
} from "../filterToPrisma";
import { orderByToPrismaSql } from "../orderByToPrisma";
import { observationsTableCols } from "../../observationsTable";
import { TableFilters } from "./types";
type AdditionalObservationFields = {
traceName: string | null;
promptName: string | null;
promptVersion: string | null;
traceTags: Array<string>;
};
type FullObservation = AdditionalObservationFields & ObservationView;
export type FullObservations = Array<FullObservation>;
export type FullObservationsWithScores = Array<
FullObservation & { scores?: Record<string, string[] | number[]> | null }
>;
export type IOAndMetadataOmittedObservations = Array<
Omit<ObservationView, "input" | "output" | "metadata"> &
AdditionalObservationFields
>;
export function parseGetAllGenerationsInput(filters: TableFilters) {
const searchCondition = filters.searchQuery
? Prisma.sql`AND (
o."id" ILIKE ${`%${filters.searchQuery}%`} OR
o."name" ILIKE ${`%${filters.searchQuery}%`} OR
o."model" ILIKE ${`%${filters.searchQuery}%`} OR
t."name" ILIKE ${`%${filters.searchQuery}%`}
)`
: Prisma.empty;
const filterCondition = tableColumnsToSqlFilterAndPrefix(
filters.filter ?? [],
observationsTableCols,
"observations"
);
const orderByCondition = orderByToPrismaSql(
filters.orderBy,
observationsTableCols
);
// to improve query performance, add timeseries filter to observation queries as well
const startTimeFilter = filters.filter?.find(
(f) => f.column === "Start Time" && f.type === "datetime"
);
const datetimeFilter =
startTimeFilter && startTimeFilter.type === "datetime"
? datetimeFilterToPrismaSql(
"start_time",
startTimeFilter.operator,
startTimeFilter.value
)
: Prisma.empty;
return {
searchCondition,
filterCondition,
orderByCondition,
datetimeFilter,
};
}
export function createGenerationsQuery({
projectId,
datetimeFilter = Prisma.empty,
page,
limit,
searchCondition = Prisma.empty,
filterCondition = Prisma.empty,
orderByCondition = Prisma.empty,
selectIOAndMetadata = false,
selectScoreValues = false,
}: {
projectId: string;
datetimeFilter?: Prisma.Sql;
page?: number;
limit?: number;
searchCondition?: Prisma.Sql;
filterCondition?: Prisma.Sql;
orderByCondition?: Prisma.Sql;
selectIOAndMetadata?: boolean;
selectScoreValues?: boolean;
}) {
return Prisma.sql`
WITH scores_avg AS (
SELECT
trace_id,
observation_id,
${selectScoreValues ? Prisma.sql`jsonb_object_agg(name::text, "values") AS "scores_values",` : Prisma.empty}
jsonb_object_agg(name::text, avg_value::double precision) AS "scores_avg"
FROM (
SELECT
trace_id,
observation_id,
name,
${selectScoreValues ? Prisma.sql`array_agg(COALESCE(string_value, value::text)) AS "values",` : Prisma.empty}
avg(value) avg_value,
comment
FROM
scores
WHERE
project_id = ${projectId}
${selectScoreValues ? Prisma.empty : Prisma.sql`AND scores."data_type" IN ('NUMERIC', 'BOOLEAN')`}
GROUP BY
trace_id,
observation_id,
name,
comment
ORDER BY
trace_id
) tmp
GROUP BY
trace_id,
observation_id
)
SELECT
${selectScoreValues ? Prisma.sql`s_avg."scores_values" AS "scores",` : Prisma.empty}
o.id,
o.name,
o.model,
o."modelParameters",
o.start_time as "startTime",
o.end_time as "endTime",
${selectIOAndMetadata ? Prisma.sql`o.input, o.output, o.metadata,` : Prisma.empty}
o.trace_id as "traceId",
t.name as "traceName",
o.completion_start_time as "completionStartTime",
o.time_to_first_token as "timeToFirstToken",
o.prompt_tokens as "promptTokens",
o.completion_tokens as "completionTokens",
o.total_tokens as "totalTokens",
o.unit,
o.level,
o.status_message as "statusMessage",
o.version,
o.model_id as "modelId",
o.input_price as "inputPrice",
o.output_price as "outputPrice",
o.total_price as "totalPrice",
o.calculated_input_cost as "calculatedInputCost",
o.calculated_output_cost as "calculatedOutputCost",
o.calculated_total_cost as "calculatedTotalCost",
o."latency",
o.prompt_id as "promptId",
p.name as "promptName",
p.version as "promptVersion",
t.tags as "traceTags"
FROM observations_view o
JOIN traces t ON t.id = o.trace_id AND t.project_id = ${projectId}
LEFT JOIN scores_avg AS s_avg ON s_avg.trace_id = t.id and s_avg.observation_id = o.id
LEFT JOIN prompts p ON p.id = o.prompt_id AND p.project_id = ${projectId}
WHERE
o.project_id = ${projectId}
AND o.type = 'GENERATION'
${datetimeFilter}
${searchCondition}
${filterCondition}
${orderByCondition}
${limit ? Prisma.sql`LIMIT ${limit}` : Prisma.empty}
${page && limit ? Prisma.sql`OFFSET ${page * limit}` : Prisma.empty}
`;
}
@@ -0,0 +1,135 @@
import { Prisma } from "@prisma/client";
import { TableFilters } from "./types";
import {
datetimeFilterToPrismaSql,
tableColumnsToSqlFilterAndPrefix,
} from "../filterToPrisma";
import { tracesTableCols } from "../../tracesTable";
import { orderByToPrismaSql } from "../orderByToPrisma";
export function parseTraceAllFilters(input: TableFilters) {
const filterCondition = tableColumnsToSqlFilterAndPrefix(
input.filter ?? [],
tracesTableCols,
"traces"
);
const orderByCondition = orderByToPrismaSql(input.orderBy, tracesTableCols);
// to improve query performance, add timeseries filter to observation queries as well
const timeseriesFilter = input.filter?.find(
(f) => f.column === "Timestamp" && f.type === "datetime"
);
const observationTimeseriesFilter =
timeseriesFilter && timeseriesFilter.type === "datetime"
? datetimeFilterToPrismaSql(
"start_time",
timeseriesFilter.operator,
timeseriesFilter.value
)
: Prisma.empty;
const searchCondition = input.searchQuery
? Prisma.sql`AND (
t."id" ILIKE ${`%${input.searchQuery}%`} OR
t."external_id" ILIKE ${`%${input.searchQuery}%`} OR
t."user_id" ILIKE ${`%${input.searchQuery}%`} OR
t."name" ILIKE ${`%${input.searchQuery}%`}
)`
: Prisma.empty;
return {
filterCondition,
orderByCondition,
observationTimeseriesFilter,
searchCondition,
};
}
export function createTracesQuery({
select,
projectId,
observationTimeseriesFilter = Prisma.empty,
page,
limit,
searchCondition = Prisma.empty,
filterCondition = Prisma.empty,
orderByCondition = Prisma.empty,
selectScoreValues = false,
}: {
select: Prisma.Sql;
projectId: string;
observationTimeseriesFilter?: Prisma.Sql;
page?: number;
limit?: number;
searchCondition?: Prisma.Sql;
filterCondition?: Prisma.Sql;
orderByCondition?: Prisma.Sql;
selectScoreValues?: boolean;
}) {
return Prisma.sql`
SELECT
${select}
FROM
"traces" AS t
LEFT JOIN LATERAL (
SELECT
SUM(prompt_tokens) AS "promptTokens",
SUM(completion_tokens) AS "completionTokens",
SUM(total_tokens) AS "totalTokens",
SUM(calculated_total_cost) AS "calculatedTotalCost",
SUM(calculated_input_cost) AS "calculatedInputCost",
SUM(calculated_output_cost) AS "calculatedOutputCost"
FROM
"observations_view"
WHERE
trace_id = t.id
AND "type" = 'GENERATION'
AND "project_id" = ${projectId}
${observationTimeseriesFilter}
) AS generation_metrics ON true
LEFT JOIN LATERAL (
SELECT
COUNT(*) AS "observationCount",
EXTRACT(EPOCH FROM COALESCE(MAX("end_time"), MAX("start_time"))) - EXTRACT(EPOCH FROM MIN("start_time"))::double precision AS "latency",
COALESCE(
MAX(CASE WHEN level = 'ERROR' THEN 'ERROR' END),
MAX(CASE WHEN level = 'WARNING' THEN 'WARNING' END),
MAX(CASE WHEN level = 'DEFAULT' THEN 'DEFAULT' END),
'DEBUG'
) AS "level"
FROM
"observations"
WHERE
trace_id = t.id
AND "project_id" = ${projectId}
${observationTimeseriesFilter}
) AS observation_metrics ON true
LEFT JOIN LATERAL (
SELECT
${selectScoreValues ? Prisma.sql`jsonb_object_agg(name::text, "values") AS "scores_values",` : Prisma.empty}
jsonb_object_agg(name::text, avg_value::double precision) AS "scores_avg"
FROM (
SELECT
name,
${selectScoreValues ? Prisma.sql`array_agg(COALESCE(string_value, value::text)) AS "values",` : Prisma.empty}
AVG(value) avg_value
FROM
scores
WHERE
trace_id = t.id
AND t."project_id" = ${projectId}
${selectScoreValues ? Prisma.empty : Prisma.sql`AND scores."data_type" IN ('NUMERIC', 'BOOLEAN')`}
GROUP BY
name
) tmp
) AS s_avg ON true
WHERE
t."project_id" = ${projectId}
${searchCondition}
${filterCondition}
${orderByCondition}
${limit ? Prisma.sql`LIMIT ${limit}` : Prisma.empty}
${page && limit ? Prisma.sql`OFFSET ${page * limit}` : Prisma.empty}
`;
}
@@ -1 +1,9 @@
export { createSessionsAllQuery } from "./createSessionsAllQuery";
export { createTracesQuery, parseTraceAllFilters } from "./createTracesQuery";
export {
createGenerationsQuery,
parseGetAllGenerationsInput,
type FullObservations,
type FullObservationsWithScores,
type IOAndMetadataOmittedObservations,
} from "./createGenerationsQuery";
@@ -0,0 +1,14 @@
import z from "zod";
import { singleFilter } from "../../interfaces/filters";
import { orderBy } from "../../interfaces/orderBy";
import { optionalPaginationZod } from "../../utils/zod";
const TableFilterSchema = z.object({
projectId: z.string(),
filter: z.array(singleFilter).nullish(),
searchQuery: z.string().nullish(),
orderBy: orderBy,
...optionalPaginationZod,
});
export type TableFilters = z.infer<typeof TableFilterSchema>;
+31 -2
View File
@@ -1,5 +1,5 @@
import { z } from "zod";
import { ingestionBatchEvent } from ".";
import { eventTypes, ingestionBatchEvent, TCarrier } from ".";
export enum EventName {
TraceUpsert = "TraceUpsert",
@@ -9,7 +9,8 @@ export enum EventName {
CloudUsageMetering = "CloudUsageMetering",
}
export const LegacyIngestionEvent = z.object({
export const LegacyIngestionEventFull = z.object({
useS3EventStore: z.literal(false),
data: ingestionBatchEvent,
authCheck: z.object({
validKey: z.literal(true),
@@ -20,6 +21,29 @@ export const LegacyIngestionEvent = z.object({
}),
});
export const LegacyIngestionEventMeta = z.object({
useS3EventStore: z.literal(true),
data: z.array(
z.object({
type: z.nativeEnum(eventTypes),
eventBodyId: z.string(),
eventId: z.string(),
})
),
authCheck: z.object({
validKey: z.literal(true),
scope: z.object({
projectId: z.string(),
accessLevel: z.enum(["all", "scores"]),
}),
}),
});
export const LegacyIngestionEvent = z.discriminatedUnion("useS3EventStore", [
LegacyIngestionEventFull,
LegacyIngestionEventMeta,
]);
export const BatchExportJobSchema = z.object({
projectId: z.string(),
batchExportId: z.string(),
@@ -31,6 +55,7 @@ export const TraceUpsertEventSchema = z.object({
export const EvalExecutionEvent = z.object({
projectId: z.string(),
jobExecutionId: z.string(),
delay: z.number().nullish(),
});
export type BatchExportJobType = z.infer<typeof BatchExportJobSchema>;
@@ -80,23 +105,27 @@ export type TQueueJobTypes = {
id: string;
payload: TraceUpsertEventType;
name: QueueJobs.TraceUpsert;
_tracecontext?: TCarrier;
};
[QueueName.EvaluationExecution]: {
timestamp: Date;
id: string;
payload: EvalExecutionEventType;
name: QueueJobs.EvaluationExecution;
_tracecontext?: TCarrier;
};
[QueueName.BatchExport]: {
timestamp: Date;
id: string;
payload: BatchExportJobType;
name: QueueJobs.BatchExportJob;
_tracecontext?: TCarrier;
};
[QueueName.LegacyIngestionQueue]: {
timestamp: Date;
id: string;
payload: LegacyIngestionEventType;
name: QueueJobs.LegacyIngestionJob;
_tracecontext?: TCarrier;
};
};
@@ -15,8 +15,12 @@ export const getBatchExportQueue = () => {
connection: connection,
defaultJobOptions: {
removeOnComplete: true,
removeOnFail: 100,
removeOnFail: 10_000,
attempts: 2,
backoff: {
type: "exponential",
delay: 5000,
},
},
})
: null;
@@ -18,9 +18,13 @@ export const getIngestionFlushQueue = () => {
connection: connection,
defaultJobOptions: {
removeOnComplete: true, // Important: If not true, new jobs for that ID would be ignored as jobs in the complete set are still considered as part of the queue
removeOnFail: 1_000,
removeOnFail: 100_000,
delay: env.LANGFUSE_INGESTION_FLUSH_DELAY_MS,
attempts: env.LANGFUSE_INGESTION_FLUSH_ATTEMPTS,
backoff: {
type: "exponential",
delay: 5000,
},
},
})
: null;
@@ -21,8 +21,12 @@ export class LegacyIngestionQueue {
connection: newRedis,
defaultJobOptions: {
removeOnComplete: true,
removeOnFail: 100,
removeOnFail: 100_000,
attempts: 5,
backoff: {
type: "exponential",
delay: 5000,
},
},
},
)
@@ -21,7 +21,12 @@ export const getTraceUpsertQueue = () => {
connection: connection,
defaultJobOptions: {
removeOnComplete: 100, // Important: If not true, new jobs for that ID would be ignored as jobs in the complete set are still considered as part of the queue
removeOnFail: 1_000,
removeOnFail: 100_000,
attempts: 5,
backoff: {
type: "exponential",
delay: 5000,
},
},
})
: null;
@@ -30,7 +35,7 @@ export const getTraceUpsertQueue = () => {
};
export function convertTraceUpsertEventsToRedisEvents(
events: TraceUpsertEventType[]
events: TraceUpsertEventType[],
) {
const uniqueTracesPerProject = events.reduce((acc, event) => {
if (!acc.get(event.projectId)) {
@@ -1,5 +1,9 @@
import type { Readable } from "stream";
import { GetObjectCommand, S3Client } from "@aws-sdk/client-s3";
import {
GetObjectCommand,
PutObjectCommand,
S3Client,
} from "@aws-sdk/client-s3";
import { Upload } from "@aws-sdk/lib-storage";
import { getSignedUrl } from "@aws-sdk/s3-request-presigner";
import { logger } from "../logger";
@@ -16,24 +20,30 @@ export class S3StorageService {
private bucketName: string;
constructor(params: {
accessKeyId: string;
secretAccessKey: string;
accessKeyId: string | undefined;
secretAccessKey: string | undefined;
bucketName: string;
endpoint: string;
region: string;
endpoint: string | undefined;
region: string | undefined;
forcePathStyle: boolean;
}) {
const { accessKeyId, secretAccessKey, bucketName, endpoint, region } =
params;
// Use accessKeyId and secretAccessKey if provided or fallback to default credentials
const { accessKeyId, secretAccessKey } = params;
const credentials =
accessKeyId !== undefined && secretAccessKey !== undefined
? {
accessKeyId,
secretAccessKey,
}
: undefined;
this.client = new S3Client({
credentials: {
accessKeyId,
secretAccessKey,
},
endpoint,
region,
credentials,
endpoint: params.endpoint,
region: params.region,
forcePathStyle: params.forcePathStyle,
});
this.bucketName = bucketName;
this.bucketName = params.bucketName;
}
public async uploadFile({
@@ -57,11 +67,42 @@ export class S3StorageService {
return { signedUrl };
} catch (err) {
logger.error(err);
logger.error(`Failed to upload file to ${fileName}`, err);
throw new Error("Failed to upload to S3 or generate signed URL");
}
}
public async uploadJson(path: string, body: Record<string, unknown>[]) {
const putCommand = new PutObjectCommand({
Bucket: this.bucketName,
Key: path,
Body: JSON.stringify(body),
ContentType: "application/json",
});
try {
await this.client.send(putCommand);
} catch (err) {
logger.error(`Failed to upload JSON to S3 ${path}`, err);
throw Error("Failed to upload JSON to S3");
}
}
public async download(path: string): Promise<string> {
const getCommand = new GetObjectCommand({
Bucket: this.bucketName,
Key: path,
});
try {
const response = await this.client.send(getCommand);
return (await response.Body?.transformToString()) ?? "";
} catch (err) {
logger.error(`Failed to download file from S3 ${path}`, err);
throw Error("Failed to download file from S3");
}
}
private async getSignedUrl(
fileName: string,
ttlSeconds: number,
@@ -77,6 +118,7 @@ export class S3StorageService {
{ expiresIn: ttlSeconds },
);
} catch (err) {
logger.error(`Failed to generate presigned URL for ${fileName}`, err);
throw Error("Failed to generate signed URL");
}
}
@@ -19,7 +19,6 @@ interface BatchExportSuccessTemplateProps {
receiverEmail: string;
downloadLink: string;
batchExportName: string;
expiresInHours: number;
}
export const BatchExportSuccessEmailTemplate = ({
@@ -27,7 +26,6 @@ export const BatchExportSuccessEmailTemplate = ({
downloadLink,
userName,
batchExportName,
expiresInHours,
}: BatchExportSuccessTemplateProps) => {
const previewText = `Download your data export from Langfuse`;
@@ -56,8 +54,11 @@ export const BatchExportSuccessEmailTemplate = ({
<Text className="text-sm leading-6 text-black">
Your data export{" "}
<span className="font-mono">{batchExportName}</span> is ready to
download. The download link will expire in{" "}
<strong>{expiresInHours} hours</strong>.
download. The download link is valid for a few hours.
</Text>
<Text className="text-sm leading-6 text-black">
Please note data exports do not reflect custom column ordering or
visibility.
</Text>
<Section className="mb-4 mt-8 text-center">
<Button
@@ -13,7 +13,6 @@ type SendBatchExportSuccessParams = {
downloadLink: string;
userName: string;
batchExportName: string;
expiresInHours: number;
};
export const sendBatchExportSuccessEmail = async ({
@@ -22,7 +21,6 @@ export const sendBatchExportSuccessEmail = async ({
downloadLink,
userName,
batchExportName,
expiresInHours,
}: SendBatchExportSuccessParams) => {
if (!env.EMAIL_FROM_ADDRESS || !env.SMTP_CONNECTION_URL) {
logger.error("Missing environment variables for sending email.");
@@ -37,8 +35,7 @@ export const sendBatchExportSuccessEmail = async ({
downloadLink,
userName,
batchExportName,
expiresInHours,
}),
})
);
await mailer.sendMail({
@@ -36,7 +36,7 @@ export const sendMembershipInvitationEmail = async ({
}: SendMembershipInvitationParams) => {
if (!env.EMAIL_FROM_ADDRESS || !env.SMTP_CONNECTION_URL) {
logger.error(
"Missing environment variables for sending membership invitation email.",
"Missing environment variables for sending membership invitation email."
);
return;
}
@@ -51,7 +51,7 @@ export const sendMembershipInvitationEmail = async ({
const authUrl = getAuthURL();
if (!authUrl) {
logger.error(
"Missing NEXTAUTH_URL or NEXT_PUBLIC_LANGFUSE_CLOUD_REGION environment variable.",
"Missing NEXTAUTH_URL or NEXT_PUBLIC_LANGFUSE_CLOUD_REGION environment variable."
);
return;
}
@@ -68,7 +68,7 @@ export const sendMembershipInvitationEmail = async ({
inviteLink: authUrl,
emailFromAddress: env.EMAIL_FROM_ADDRESS,
langfuseCloudRegion: env.NEXT_PUBLIC_LANGFUSE_CLOUD_REGION,
}),
})
);
await mailer.sendMail({
@@ -56,8 +56,9 @@ const ResetPasswordTemplate = ({ url }: ResetPasswordTemplateProps) => {
</Button>
</Section>
<Text className="text-center text-xs leading-6 text-[#666666]">
If you do not want to change your password or didn&apos;t request
a reset, you can ignore and delete this email.
The link is valid for 10 minutes. If you do not want to change
your password or didn&apos;t request a reset, you can ignore and
delete this email.
</Text>
</Container>
</Body>
@@ -76,7 +77,7 @@ export async function sendResetPasswordVerificationRequest(
to: identifier,
from: provider.from,
subject: `Forgot your password?`,
text: `To reset your Langfuse password, please confirm your email:\n${url}\n\nIf you do not want to change your password or didn't request a reset, you can ignore and delete this email.`,
text: `To reset your Langfuse password, please confirm your email:\n${url}\n\nThe link is valid for 10 minutes. If you do not want to change your password or didn't request a reset, you can ignore and delete this email.`,
html: htmlTemplate,
});
const failed = result.rejected.concat(result.pending).filter(Boolean);
@@ -1,6 +1,8 @@
import { Transform, type TransformCallback } from "stream";
import { stringify } from "./stringify";
const DELIMITER = ",";
export function transformStreamToCsv(): Transform {
let isFirstChunk = true;
let headers: string[] = [];
@@ -15,15 +17,19 @@ export function transformStreamToCsv(): Transform {
if (isFirstChunk) {
// Extract headers from the first object
headers = Object.keys(row);
this.push(headers.join(",") + "\n");
this.push(headers.join(DELIMITER) + "\n");
isFirstChunk = false;
}
// Convert the object to a CSV line and push it
const csvRow = headers.map((header) => {
const field = row[header];
const str = stringify(field);
let str = stringify(field);
// escape and format fields that contain commas
if (str.includes(",")) {
str = `"${str.replace(/"/g, '""')}"`;
}
if (str.startsWith('"') && str.endsWith('"')) {
return str;
} else {
@@ -31,7 +37,7 @@ export function transformStreamToCsv(): Transform {
}
});
this.push(csvRow.join(",") + "\n");
this.push(csvRow.join(DELIMITER) + "\n");
callback();
},
+5 -5
View File
@@ -78,28 +78,28 @@ export const tracesTableCols: ColumnDefinition[] = [
name: "Input Tokens",
id: "inputTokens",
type: "number",
internal: 'tm."promptTokens"',
internal: 'generation_metrics."promptTokens"',
nullable: true,
},
{
name: "Output Tokens",
id: "outputTokens",
type: "number",
internal: 'tm."completionTokens"',
internal: 'generation_metrics."completionTokens"',
nullable: true,
},
{
name: "Total Tokens",
id: "totalTokens",
type: "number",
internal: 'tm."totalTokens"',
internal: 'generation_metrics."totalTokens"',
nullable: true,
},
{
name: "Usage",
id: "usage",
type: "number",
internal: 'tm."totalTokens"',
internal: 'generation_metrics."totalTokens"',
nullable: true,
},
@@ -113,7 +113,7 @@ export const tracesTableCols: ColumnDefinition[] = [
name: "Latency (s)",
id: "latency",
type: "number",
internal: "tl.latency",
internal: "observation_metrics.latency",
},
{
name: "Input Cost ($)",
+2 -1
View File
@@ -1,7 +1,8 @@
import { type z } from "zod";
import { singleFilter } from "./interfaces/filters";
import { singleFilter, timeFilter } from "./interfaces/filters";
// to be sent to the server
export type TimeFilter = z.infer<typeof timeFilter>;
export type FilterCondition = z.infer<typeof singleFilter>;
export type FilterState = FilterCondition[];
+10
View File
@@ -0,0 +1,10 @@
// https://github.com/t3-oss/t3-env/blob/e7e21095e00a477e37608783defda5a6a99586d0/packages/core/src/index.ts#L228
// unfortunately, we are not able to install t3-env in all our packaging as some rely on commonjs.
export const removeEmptyEnvVariables = (runtimeEnv: any) => {
for (const [key, value] of Object.entries(runtimeEnv)) {
if (value === "") {
delete runtimeEnv[key];
}
}
return runtimeEnv;
};
+3644 -4159
View File
File diff suppressed because it is too large Load Diff
+38 -13
View File
@@ -33,21 +33,43 @@ ENV DOCKER_BUILD 1
ENV NEXT_MANUAL_SIG_HANDLE true
# pass public variables in build step
ARG NEXT_PUBLIC_LANGFUSE_CLOUD_REGION
ARG NEXT_PUBLIC_DEMO_ORG_ID
ARG NEXT_PUBLIC_DEMO_PROJECT_ID
ARG NEXT_PUBLIC_SIGN_UP_DISABLED
ARG NEXT_PUBLIC_TURNSTILE_SITE_KEY
ARG NEXT_PUBLIC_POSTHOG_KEY
ARG NEXT_PUBLIC_POSTHOG_HOST
ARG NEXT_PUBLIC_CRISP_WEBSITE_ID
ARG NEXT_PUBLIC_SENTRY_DSN
ARG NEXT_LANGFUSE_TRACING_SAMPLE_RATE
# Accept build id as NEXT_PUBLIC_BUILD_ID or PORTER_NEXT_PUBLIC_BUILD_ID
ARG PORTER_NEXT_PUBLIC_BUILD_ID
ARG NEXT_PUBLIC_LANGFUSE_CLOUD_REGION
ENV NEXT_PUBLIC_LANGFUSE_CLOUD_REGION=$NEXT_PUBLIC_LANGFUSE_CLOUD_REGION
ARG NEXT_PUBLIC_DEMO_ORG_ID
ENV NEXT_PUBLIC_DEMO_ORG_ID=$NEXT_PUBLIC_DEMO_ORG_ID
ARG NEXT_PUBLIC_DEMO_PROJECT_ID
ENV NEXT_PUBLIC_DEMO_PROJECT_ID=$NEXT_PUBLIC_DEMO_PROJECT_ID
ARG NEXT_PUBLIC_SIGN_UP_DISABLED
ENV NEXT_PUBLIC_SIGN_UP_DISABLED=$NEXT_PUBLIC_SIGN_UP_DISABLED
ARG NEXT_PUBLIC_TURNSTILE_SITE_KEY
ENV NEXT_PUBLIC_TURNSTILE_SITE_KEY=$NEXT_PUBLIC_TURNSTILE_SITE_KEY
ARG NEXT_PUBLIC_POSTHOG_KEY
ENV NEXT_PUBLIC_POSTHOG_KEY=$NEXT_PUBLIC_POSTHOG_KEY
ARG NEXT_PUBLIC_POSTHOG_HOST
ENV NEXT_PUBLIC_POSTHOG_HOST=$NEXT_PUBLIC_POSTHOG_HOST
ARG NEXT_PUBLIC_LANGFUSE_TRACING_SAMPLE_RATE
ENV NEXT_PUBLIC_LANGFUSE_TRACING_SAMPLE_RATE=$NEXT_PUBLIC_LANGFUSE_TRACING_SAMPLE_RATE
ARG NEXT_PUBLIC_SENTRY_ENVIRONMENT
ENV NEXT_PUBLIC_SENTRY_ENVIRONMENT=$NEXT_PUBLIC_SENTRY_ENVIRONMENT
ARG NEXT_PUBLIC_SENTRY_DSN
ENV NEXT_PUBLIC_SENTRY_DSN=$NEXT_PUBLIC_SENTRY_DSN
ARG NEXT_PUBLIC_BASE_PATH
ENV NEXT_PUBLIC_BASE_PATH=$NEXT_PUBLIC_BASE_PATH
# Sentry already needs to be set on build time to upload sourcemaps
# This must not be set for OSS releases as we would share our Sentry secret.
ARG SENTRY_AUTH_TOKEN
ENV SENTRY_AUTH_TOKEN=$SENTRY_AUTH_TOKEN
ARG SENTRY_ORG
ENV SENTRY_ORG=$SENTRY_ORG
ARG SENTRY_PROJECT
ENV SENTRY_PROJECT=$SENTRY_PROJECT
# Accept build id as NEXT_PUBLIC_BUILD_ID
ARG NEXT_PUBLIC_BUILD_ID
ENV NEXT_PUBLIC_BUILD_ID=${PORTER_NEXT_PUBLIC_BUILD_ID:-$NEXT_PUBLIC_BUILD_ID}
ENV NEXT_PUBLIC_BUILD_ID=$NEXT_PUBLIC_BUILD_ID
# Copy source code of isolated subworkspace
COPY --from=pruner /app/out/full/ .
@@ -60,6 +82,9 @@ RUN rm -f ./web/src/middleware.ts
# Uncomment the following line in case you want to disable telemetry during the build.
ENV NEXT_TELEMETRY_DISABLED 1
ENV NEXT_MANUAL_SIG_HANDLE true
# set the CI flag to true to get CI specific logs
ENV CI true
RUN NODE_OPTIONS='--max-old-space-size=4096' turbo run build --filter=web
@@ -81,7 +106,7 @@ RUN apk add --no-cache dumb-init
RUN addgroup --system --gid 1001 nodejs
RUN adduser --system --uid 1001 nextjs
RUN npm install -g --no-package-lock --no-save prisma@5.13.0
RUN npm install -g --no-package-lock --no-save prisma@5.20.0
COPY --from=builder --chown=nextjs:nodejs /app/web/next.config.mjs .
COPY --from=builder --chown=nextjs:nodejs /app/web/package.json .
+6 -5
View File
@@ -11,8 +11,8 @@ import { env } from "./src/env.mjs";
* img-src https to allow loading images from SSO providers
*/
const cspHeader = `
default-src 'self' https://*.langfuse.com https://*.posthog.com https://*.sentry.io wss://*.crisp.chat https://*.crisp.chat;
script-src 'self' 'unsafe-eval' 'unsafe-inline' https://*.langfuse.com https://client.crisp.chat https://settings.crisp.chat https://challenges.cloudflare.com https://*.sentry.io https://ph.langfuse.com https://static.cloudflareinsights.com https://*.stripe.com;
default-src 'self' https://*.langfuse.com https://*.langfuse.dev https://*.posthog.com https://*.sentry.io wss://*.crisp.chat https://*.crisp.chat;
script-src 'self' 'unsafe-eval' 'unsafe-inline' https://*.langfuse.com https://*.langfuse.dev https://client.crisp.chat https://settings.crisp.chat https://challenges.cloudflare.com https://*.sentry.io https://static.cloudflareinsights.com https://*.stripe.com;
style-src 'self' 'unsafe-inline' https://client.crisp.chat;
img-src 'self' https: blob: data: https://client.crisp.chat https://image.crisp.chat https://storage.crisp.chat;
font-src 'self' https://client.crisp.chat;
@@ -22,7 +22,7 @@ const cspHeader = `
base-uri 'self';
form-action 'self';
frame-ancestors 'none';
connect-src 'self' https://client.crisp.chat https://storage.crisp.chat wss://client.relay.crisp.chat wss://stream.relay.crisp.chat https://*.ingest.us.sentry.io https://ph.langfuse.com;
connect-src 'self' https://*.langfuse.com https://*.langfuse.dev https://client.crisp.chat https://storage.crisp.chat wss://client.relay.crisp.chat wss://stream.relay.crisp.chat https://*.ingest.us.sentry.io;
media-src 'self' https://client.crisp.chat;
${env.LANGFUSE_CSP_ENFORCE_HTTPS === "true" ? "upgrade-insecure-requests; block-all-mixed-content;" : ""}
${env.SENTRY_CSP_REPORT_URI ? `report-uri ${env.SENTRY_CSP_REPORT_URI}; report-to csp-endpoint;` : ""}
@@ -50,11 +50,12 @@ const nextConfig = {
instrumentationHook: true,
serverComponentsExternalPackages: [
"dd-trace",
"@opentelemetry/auto-instrumentations-node",
"@opentelemetry/api",
"@appsignal/opentelemetry-instrumentation-bullmq",
],
},
poweredByHeader: false,
basePath: env.NEXT_PUBLIC_BASE_PATH,
/**
* If you have `experimental: { appDir: true }` set, then you must comment the below `i18n` config
@@ -176,7 +177,7 @@ export default withSentryConfig(nextConfig, {
// This can increase your server load as well as your hosting bill.
// Note: Check that the configured route will not match with your Next.js middleware, otherwise reporting of client-
// side errors will fail.
tunnelRoute: "/api/monitoring-tunnel",
// tunnelRoute: "/api/monitoring-tunnel",
// Hides source maps from generated client bundles
hideSourceMaps: true,
+63 -60
View File
@@ -1,6 +1,6 @@
{
"name": "web",
"version": "2.78.0",
"version": "2.84.1",
"private": true,
"license": "MIT",
"engines": {
@@ -23,76 +23,79 @@
},
"dependencies": {
"@anthropic-ai/tokenizer": "^0.0.4",
"@appsignal/opentelemetry-instrumentation-bullmq": "^0.7.1",
"@appsignal/opentelemetry-instrumentation-bullmq": "^0.7.3",
"@baselime/trpc-opentelemetry-middleware": "^0.1.2",
"@codemirror/lang-json": "^6.0.1",
"@codemirror/lint": "^6.8.0",
"@headlessui/react": "1.7.18",
"@dnd-kit/core": "^6.1.0",
"@dnd-kit/modifiers": "^7.0.0",
"@dnd-kit/sortable": "^8.0.0",
"@dnd-kit/utilities": "^3.2.2",
"@headlessui/react": "2.1.9",
"@headlessui/tailwindcss": "0.2.1",
"@heroicons/react": "^2.1.3",
"@heroicons/react": "^2.1.5",
"@hookform/resolvers": "^3.3.4",
"@langchain/anthropic": "^0.1.21",
"@langchain/core": "^0.1.61",
"@langchain/openai": "^0.0.33",
"@langchain/anthropic": "^0.3.1",
"@langchain/core": "^0.3.9",
"@langchain/openai": "^0.3.0",
"@langfuse/ee": "workspace:*",
"@langfuse/shared": "workspace:*",
"@marsidev/react-turnstile": "^0.5.4",
"@mui/x-tree-view": "^7.6.2",
"@mui/x-tree-view": "^7.19.0",
"@next-auth/prisma-adapter": "^1.0.7",
"@opentelemetry/api": ">=1.0.0 <1.9.0",
"@opentelemetry/auto-instrumentations-node": "^0.44.0",
"@opentelemetry/context-async-hooks": "^1.25.1",
"@opentelemetry/core": "^1.14.0",
"@opentelemetry/instrumentation": "^0.52.1",
"@opentelemetry/instrumentation-http": "^0.52.1",
"@opentelemetry/instrumentation-ioredis": "^0.42.0",
"@opentelemetry/api": "^1.9.0",
"@opentelemetry/core": "^1.26.0",
"@opentelemetry/exporter-trace-otlp-proto": "^0.53.0",
"@opentelemetry/instrumentation": "^0.53.0",
"@opentelemetry/instrumentation-aws-sdk": "^0.44.0",
"@opentelemetry/instrumentation-http": "^0.53.0",
"@opentelemetry/instrumentation-ioredis": "^0.43.0",
"@opentelemetry/instrumentation-winston": "^0.40.0",
"@opentelemetry/instrumentation-undici": "^0.4.0",
"@opentelemetry/resources": "^1.25.1",
"@opentelemetry/sdk-metrics": "1.23.0",
"@opentelemetry/sdk-node": "^0.50.0",
"@opentelemetry/sdk-trace-base": "^1.25.1",
"@opentelemetry/sdk-trace-node": "^1.25.1",
"@opentelemetry/semantic-conventions": "^1.25.1",
"@prisma/instrumentation": "^5.13.0",
"@radix-ui/react-accordion": "^1.2.0",
"@radix-ui/react-alert-dialog": "^1.1.1",
"@radix-ui/react-avatar": "^1.1.0",
"@radix-ui/react-checkbox": "^1.1.1",
"@radix-ui/react-collapsible": "^1.1.0",
"@radix-ui/react-dialog": "^1.1.1",
"@radix-ui/react-dropdown-menu": "^2.1.1",
"@radix-ui/react-hover-card": "^1.1.1",
"@opentelemetry/resource-detector-aws": "^1.6.2",
"@opentelemetry/resource-detector-container": "^0.4.2",
"@opentelemetry/resources": "^1.26.0",
"@opentelemetry/sdk-node": "^0.53.0",
"@opentelemetry/sdk-trace-base": "^1.26.0",
"@opentelemetry/sdk-trace-node": "^1.26.0",
"@prisma/instrumentation": "^5.20.0",
"@radix-ui/react-accordion": "^1.2.1",
"@radix-ui/react-alert-dialog": "^1.1.2",
"@radix-ui/react-avatar": "^1.1.1",
"@radix-ui/react-checkbox": "^1.1.2",
"@radix-ui/react-collapsible": "^1.1.1",
"@radix-ui/react-dialog": "^1.1.2",
"@radix-ui/react-dropdown-menu": "^2.1.2",
"@radix-ui/react-hover-card": "^1.1.2",
"@radix-ui/react-icons": "^1.3.0",
"@radix-ui/react-label": "^2.1.0",
"@radix-ui/react-popover": "^1.1.1",
"@radix-ui/react-scroll-area": "^1.1.0",
"@radix-ui/react-select": "^2.1.1",
"@radix-ui/react-popover": "^1.1.2",
"@radix-ui/react-scroll-area": "^1.2.0",
"@radix-ui/react-select": "^2.1.2",
"@radix-ui/react-separator": "^1.1.0",
"@radix-ui/react-slider": "^1.2.0",
"@radix-ui/react-slider": "^1.2.1",
"@radix-ui/react-slot": "^1.1.0",
"@radix-ui/react-switch": "^1.1.0",
"@radix-ui/react-tabs": "^1.1.0",
"@radix-ui/react-switch": "^1.1.1",
"@radix-ui/react-tabs": "^1.1.1",
"@radix-ui/react-toggle": "^1.1.0",
"@radix-ui/react-toggle-group": "^1.1.0",
"@radix-ui/react-tooltip": "^1.1.2",
"@radix-ui/react-tooltip": "^1.1.3",
"@remixicon/react": "^4.2.0",
"@repo/eslint-config": "workspace:*",
"@repo/typescript-config": "workspace:*",
"@sentry/nextjs": "8.26.0",
"@t3-oss/env-nextjs": "^0.10.1",
"@sentry/nextjs": "^8.33.1",
"@t3-oss/env-nextjs": "^0.11.1",
"@tailwindcss/container-queries": "^0.1.1",
"@tanstack/react-query": "^4.36.1",
"@tanstack/react-table": "^8.11.8",
"@tanstack/react-table": "^8.20.5",
"@tremor/react": "3.16.2",
"@trpc/client": "^10.45.0",
"@trpc/next": "^10.45.0",
"@trpc/react-query": "^10.45.0",
"@trpc/server": "^10.45.0",
"@uiw/codemirror-theme-github": "^4.23.0",
"@uiw/codemirror-theme-tokyo-night": "^4.22.2",
"@uiw/codemirror-theme-tokyo-night": "^4.23.5",
"@uiw/react-codemirror": "^4.21.25",
"ai": "^3.0.23",
"ai": "^3.4.9",
"bcryptjs": "^2.4.3",
"bullmq": "^5.12.10",
"class-variance-authority": "^0.7.0",
@@ -101,25 +104,25 @@
"core-js": "^3.38.1",
"cors": "^2.8.5",
"date-fns": "^3.3.1",
"dd-trace": "^5.21.0",
"dd-trace": "^5.23.1",
"decimal.js": "^10.4.3",
"dompurify": "^3.1.5",
"graphql": "^16.9.0",
"ioredis": "^5.4.1",
"ip-address": "^9.0.5",
"js-tiktoken": "^1.0.12",
"js-tiktoken": "^1.0.15",
"kysely": "^0.27.4",
"langchain": "^0.2.6",
"langchain": "^0.3.2",
"lodash": "^4.17.21",
"lucide-react": "^0.436.0",
"next": "^14.2.6",
"lucide-react": "^0.447.0",
"next": "^14.2.15",
"next-auth": "^4.24.7",
"next-query-params": "^5.0.0",
"next-query-params": "^5.0.1",
"next-themes": "^0.3.0",
"posthog-js": "^1.154.6",
"posthog-js": "^1.161.3",
"posthog-node": "^3.6.3",
"prexit": "^2.2.0",
"prisma": "^5.13.0",
"prisma": "^5.20.0",
"rate-limiter-flexible": "^5.0.3",
"react": "18.2.0",
"react-day-picker": "^8.10.1",
@@ -145,8 +148,8 @@
},
"devDependencies": {
"@jedmao/location": "^3.0.0",
"@mermaid-js/mermaid-cli": "^10.7.0",
"@playwright/test": "^1.43.1",
"@mermaid-js/mermaid-cli": "^11.2.0",
"@playwright/test": "^1.47.2",
"@tailwindcss/forms": "^0.5.7",
"@testing-library/jest-dom": "^6.4.6",
"@testing-library/react": "^15.0.7",
@@ -155,7 +158,7 @@
"@types/dompurify": "^3.0.5",
"@types/eslint": "^8.56.7",
"@types/jest": "^29.5.12",
"@types/lodash": "^4.17.7",
"@types/lodash": "^4.17.10",
"@types/node": "20.10.5",
"@types/react": "~18.2.79",
"@types/react-dom": "~18.2.25",
@@ -166,17 +169,17 @@
"autoprefixer": "^10.4.19",
"dotenv-cli": "^7.4.2",
"eslint": "^8.56.0",
"eslint-config-next": "^14.2.6",
"eslint-config-next": "^14.2.15",
"jest": "^29.7.0",
"jest-environment-jsdom": "^29.7.0",
"node-mocks-http": "^1.14.1",
"postcss": "^8.4.38",
"prettier": "^3.2.5",
"postcss": "^8.4.47",
"prettier": "^3.3.3",
"prettier-plugin-tailwindcss": "^0.6.6",
"tailwindcss": "^3.4.10",
"tailwindcss": "^3.4.13",
"ts-node": "^10.9.2",
"tsconfig-paths": "^4.2.0",
"tsx": "^4.19.0",
"tsx": "^4.19.1",
"typescript": "^5.4.5",
"wait-for-expect": "^3.0.2"
},
@@ -184,6 +187,6 @@
"initVersion": "7.13.0"
},
"optionalDependencies": {
"crisp-sdk-web": "^1.0.21"
"crisp-sdk-web": "^1.0.25"
}
}
+70 -36
View File
@@ -63,7 +63,7 @@ paths:
content:
application/json:
schema: {}
security: &ref_0
security:
- BasicAuth: []
requestBody:
required: true
@@ -141,7 +141,8 @@ paths:
content:
application/json:
schema: {}
security: *ref_0
security:
- BasicAuth: []
/api/public/dataset-items/{id}:
get:
description: Get a dataset item
@@ -186,7 +187,8 @@ paths:
content:
application/json:
schema: {}
security: *ref_0
security:
- BasicAuth: []
/api/public/dataset-run-items:
post:
description: Create a dataset run item
@@ -226,7 +228,8 @@ paths:
content:
application/json:
schema: {}
security: *ref_0
security:
- BasicAuth: []
requestBody:
required: true
content:
@@ -286,7 +289,8 @@ paths:
content:
application/json:
schema: {}
security: *ref_0
security:
- BasicAuth: []
post:
description: Create a dataset
operationId: datasets_create
@@ -325,7 +329,8 @@ paths:
content:
application/json:
schema: {}
security: *ref_0
security:
- BasicAuth: []
requestBody:
required: true
content:
@@ -376,7 +381,8 @@ paths:
content:
application/json:
schema: {}
security: *ref_0
security:
- BasicAuth: []
/api/public/datasets/{datasetName}/runs/{runName}:
get:
description: Get a dataset run and its items
@@ -426,7 +432,8 @@ paths:
content:
application/json:
schema: {}
security: *ref_0
security:
- BasicAuth: []
/api/public/datasets/{datasetName}/runs:
get:
description: Get dataset runs
@@ -485,7 +492,8 @@ paths:
content:
application/json:
schema: {}
security: *ref_0
security:
- BasicAuth: []
/api/public/health:
get:
description: Check health of API and database
@@ -580,7 +588,8 @@ paths:
content:
application/json:
schema: {}
security: *ref_0
security:
- BasicAuth: []
requestBody:
required: true
content:
@@ -649,8 +658,8 @@ paths:
- name: fromTimestamp
in: query
description: >-
Optional filter to only include traces on or after a certain
datetime (ISO 8601)
Optional filter to only include traces and observations on or after
a certain datetime (ISO 8601)
required: false
schema:
type: string
@@ -659,8 +668,8 @@ paths:
- name: toTimestamp
in: query
description: >-
Optional filter to only include traces before a certain datetime
(ISO 8601)
Optional filter to only include traces and observations before a
certain datetime (ISO 8601)
required: false
schema:
type: string
@@ -698,7 +707,8 @@ paths:
content:
application/json:
schema: {}
security: *ref_0
security:
- BasicAuth: []
/api/public/models:
post:
description: Create a model
@@ -738,7 +748,8 @@ paths:
content:
application/json:
schema: {}
security: *ref_0
security:
- BasicAuth: []
requestBody:
required: true
content:
@@ -797,7 +808,8 @@ paths:
content:
application/json:
schema: {}
security: *ref_0
security:
- BasicAuth: []
/api/public/models/{id}:
get:
description: Get a model
@@ -842,7 +854,8 @@ paths:
content:
application/json:
schema: {}
security: *ref_0
security:
- BasicAuth: []
delete:
description: >-
Delete a model. Cannot delete models managed by Langfuse. You can create
@@ -885,7 +898,8 @@ paths:
content:
application/json:
schema: {}
security: *ref_0
security:
- BasicAuth: []
/api/public/observations/{observationId}:
get:
description: Get a observation
@@ -933,7 +947,8 @@ paths:
content:
application/json:
schema: {}
security: *ref_0
security:
- BasicAuth: []
/api/public/observations:
get:
description: Get a list of observations
@@ -1046,7 +1061,8 @@ paths:
content:
application/json:
schema: {}
security: *ref_0
security:
- BasicAuth: []
/api/public/projects:
get:
description: Get Project associated with API key
@@ -1086,7 +1102,8 @@ paths:
content:
application/json:
schema: {}
security: *ref_0
security:
- BasicAuth: []
/api/public/v2/prompts/{promptName}:
get:
description: Get a prompt
@@ -1148,7 +1165,8 @@ paths:
content:
application/json:
schema: {}
security: *ref_0
security:
- BasicAuth: []
/api/public/v2/prompts:
get:
description: Get a list of prompt names with versions and labels
@@ -1240,7 +1258,8 @@ paths:
content:
application/json:
schema: {}
security: *ref_0
security:
- BasicAuth: []
post:
description: Create a new version for the prompt with the given `name`
operationId: prompts_create
@@ -1279,7 +1298,8 @@ paths:
content:
application/json:
schema: {}
security: *ref_0
security:
- BasicAuth: []
requestBody:
required: true
content:
@@ -1327,7 +1347,8 @@ paths:
content:
application/json:
schema: {}
security: *ref_0
security:
- BasicAuth: []
requestBody:
required: true
content:
@@ -1388,7 +1409,8 @@ paths:
content:
application/json:
schema: {}
security: *ref_0
security:
- BasicAuth: []
/api/public/score-configs/{configId}:
get:
description: Get a score config
@@ -1434,7 +1456,8 @@ paths:
content:
application/json:
schema: {}
security: *ref_0
security:
- BasicAuth: []
/api/public/scores:
post:
description: Create a score
@@ -1474,7 +1497,8 @@ paths:
content:
application/json:
schema: {}
security: *ref_0
security:
- BasicAuth: []
requestBody:
required: true
content:
@@ -1612,7 +1636,8 @@ paths:
content:
application/json:
schema: {}
security: *ref_0
security:
- BasicAuth: []
/api/public/scores/{scoreId}:
get:
description: Get a score
@@ -1658,7 +1683,8 @@ paths:
content:
application/json:
schema: {}
security: *ref_0
security:
- BasicAuth: []
delete:
description: Delete a score
operationId: score_delete
@@ -1699,7 +1725,8 @@ paths:
content:
application/json:
schema: {}
security: *ref_0
security:
- BasicAuth: []
/api/public/sessions:
get:
description: Get sessions
@@ -1775,7 +1802,8 @@ paths:
content:
application/json:
schema: {}
security: *ref_0
security:
- BasicAuth: []
/api/public/sessions/{sessionId}:
get:
description: >-
@@ -1824,7 +1852,8 @@ paths:
content:
application/json:
schema: {}
security: *ref_0
security:
- BasicAuth: []
/api/public/traces/{traceId}:
get:
description: Get a specific trace
@@ -1870,7 +1899,8 @@ paths:
content:
application/json:
schema: {}
security: *ref_0
security:
- BasicAuth: []
/api/public/traces:
get:
description: Get list of traces
@@ -1997,7 +2027,8 @@ paths:
content:
application/json:
schema: {}
security: *ref_0
security:
- BasicAuth: []
components:
schemas:
Trace:
@@ -2696,6 +2727,7 @@ components:
to exact match, use `(?i)^modelname$`
startDate:
type: string
format: date
nullable: true
description: Apply only to generations which are newer than this ISO date.
unit:
@@ -3508,6 +3540,7 @@ components:
properties:
date:
type: string
format: date
countTraces:
type: integer
countObservations:
@@ -3592,6 +3625,7 @@ components:
to exact match, use `(?i)^modelname$`
startDate:
type: string
format: date
nullable: true
description: Apply only to generations which are newer than this ISO date.
unit:
+2 -2
View File
@@ -516,12 +516,12 @@
{
"key": "fromTimestamp",
"value": "",
"description": "Optional filter to only include traces on or after a certain datetime (ISO 8601)"
"description": "Optional filter to only include traces and observations on or after a certain datetime (ISO 8601)"
},
{
"key": "toTimestamp",
"value": "",
"description": "Optional filter to only include traces before a certain datetime (ISO 8601)"
"description": "Optional filter to only include traces and observations before a certain datetime (ISO 8601)"
}
],
"variable": []
+7 -13
View File
@@ -2,13 +2,15 @@ import * as Sentry from "@sentry/nextjs";
Sentry.init({
dsn: process.env.NEXT_PUBLIC_SENTRY_DSN,
environment: process.env.NEXT_PUBLIC_SENTRY_ENVIRONMENT,
// Replay may only be enabled for the client-side
integrations: [
Sentry.replayIntegration(),
// Sentry.debugIntegration(),
Sentry.captureConsoleIntegration({
levels: ["error"],
}),
// Sentry.captureConsoleIntegration({
// levels: ["error"],
// }),
],
// Set tracesSampleRate to 1.0 to capture 100%
@@ -16,24 +18,16 @@ Sentry.init({
// We recommend adjusting this value in production
tracesSampleRate: process.env.NEXT_PUBLIC_LANGFUSE_TRACING_SAMPLE_RATE
? Number(process.env.NEXT_PUBLIC_LANGFUSE_TRACING_SAMPLE_RATE)
: 0.5,
: 0,
// Capture Replay for 100% of all sessions,
// plus for 100% of sessions with an error
replaysSessionSampleRate: process.env.NEXT_PUBLIC_LANGFUSE_TRACING_SAMPLE_RATE
? Number(process.env.NEXT_PUBLIC_LANGFUSE_TRACING_SAMPLE_RATE)
: 0.5,
: 0,
replaysOnErrorSampleRate: 1.0,
debug: false,
beforeSend(event, _hint) {
// Check if it is an exception, and if so, show the report dialog
if (event.exception) {
Sentry.showReportDialog({ eventId: event.event_id });
}
return event;
},
// ...
// Note: if you want to override the automatic release value, do not set a
+41
View File
@@ -97,3 +97,44 @@ test("Unauthenticated user should be redirected to target URL after login", asyn
await expect(page).toHaveURL(promptUrl);
});
test("Unauthenticated user should not be redirected to non-relative URLs after login", async ({
page,
}) => {
const nonRelativeUrl = "https://example.com";
await page.goto(
`/auth/sign-in?targetPath=${encodeURIComponent(nonRelativeUrl)}`,
);
await page.fill('input[name="email"]', "demo@langfuse.com");
await page.fill('input[type="password"]', "password");
await page.click('button[data-testid="submit-email-password-sign-in-form"]');
// Wait for navigation
await page.waitForTimeout(2000);
// Expect to be redirected to the home page, not the non-relative URL
await expect(page).toHaveURL("/");
// Verify we're logged in
await expect(page.getByRole("button", { name: /Demo User/ })).toBeVisible();
});
test("Unauthenticated user should be redirected to relative URL after login", async ({
page,
}) => {
const relativeUrl = "/support";
await page.goto(
`/auth/sign-in?targetPath=${encodeURIComponent(relativeUrl)}`,
);
await page.fill('input[name="email"]', "demo@langfuse.com");
await page.fill('input[type="password"]', "password");
await page.click('button[data-testid="submit-email-password-sign-in-form"]');
// Wait for navigation
await page.waitForTimeout(2000);
// Expect to be redirected to the relative URL
await expect(page).toHaveURL(relativeUrl);
});
@@ -14,7 +14,7 @@ describe("observations.export RPC", () => {
beforeAll(async () => {
// Disable S3 upload
process.env.S3_ENDPOINT = "";
process.env.S3_BUCKET_NAME = "";
await pruneDatabase();
const traceId = "trace-1";
@@ -39,6 +39,8 @@ describe("RateLimitService", () => {
rateLimitOverrides: [],
};
expect(redis).toBeDefined();
const rateLimitService = new RateLimitService(redis!);
const result = await rateLimitService.rateLimitRequest(scope, "public-api");
+11 -6
View File
@@ -30,7 +30,17 @@ export const BatchExportTableButton: React.FC<BatchExportTableButtonProps> = (
props,
) => {
const [isExporting, setIsExporting] = React.useState(false);
const createExport = api.batchExport.create.useMutation();
const createExport = api.batchExport.create.useMutation({
onSettled: () => {
setIsExporting(false);
},
onSuccess: () => {
showSuccessToast({
title: "Export queued",
description: "You will receive an email when the export is ready.",
});
},
});
const entitled = useHasOrgEntitlement("batch-export");
const hasAccess = useHasProjectAccess({
projectId: props.projectId,
@@ -49,11 +59,6 @@ export const BatchExportTableButton: React.FC<BatchExportTableButtonProps> = (
orderBy: props.orderByState,
},
});
setIsExporting(false);
showSuccessToast({
title: "Export queued",
description: "You will receive an email when the export is ready.",
});
};
if (!entitled || !hasAccess) return null;
@@ -9,27 +9,40 @@ import type { MessagesContext } from "./types";
type ChatMessageProps = Pick<
MessagesContext,
"deleteMessage" | "updateMessage"
"deleteMessage" | "updateMessage" | "availableRoles"
> & { message: ChatMessageWithId };
export const ChatMessageComponent: React.FC<ChatMessageProps> = ({
message,
updateMessage,
deleteMessage,
availableRoles,
}) => {
const textAreaRef = useRef<HTMLTextAreaElement | null>(null);
const [textAreaRows, setTextAreaRows] = useState(1);
const [roleIndex, setRoleIndex] = useState(1);
const toggleRole = () => {
if (message.role === ChatMessageRole.System) return;
updateMessage(
message.id,
"role",
message.role === ChatMessageRole.User
? ChatMessageRole.Assistant
: ChatMessageRole.User,
);
// if user has set custom roles, available roles will be non-empty and we toggle through custom and default roles (assistant, user)
if (!!availableRoles && Boolean(availableRoles.length)) {
let randomRole = availableRoles[roleIndex % availableRoles.length];
if (randomRole === message.role) {
randomRole = availableRoles[(roleIndex + 1) % availableRoles.length];
}
updateMessage(message.id, "role", randomRole);
setRoleIndex(roleIndex + 1);
} else {
// if user has not set custom roles, we toggle through default roles (assistant, user)
updateMessage(
message.id,
"role",
message.role === ChatMessageRole.User
? ChatMessageRole.Assistant
: ChatMessageRole.User,
);
}
};
const handleContentChange = (event: ChangeEvent<HTMLTextAreaElement>) => {
@@ -64,7 +77,7 @@ export const ChatMessageComponent: React.FC<ChatMessageProps> = ({
<Textarea
ref={textAreaRef}
id={message.id}
className="height-[auto] min-h-6 w-full font-mono text-xs focus:outline-none"
className="height-[auto] min-h-6 w-full font-mono text-xs focus:outline-none"
placeholder={placeholder}
value={message.content}
onChange={handleContentChange}

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