Compare commits

...
67 Commits
Author SHA1 Message Date
Max Deichmann e439c81512 chore: release v2.7.1
CI/CD / lint (push) Waiting to run
CI/CD / test-docker-build (push) Waiting to run
CI/CD / tests (18) (push) Waiting to run
CI/CD / tests (20) (push) Waiting to run
CI/CD / e2e-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-02-22 14:08:26 +01:00
Max DeichmannandGitHub 799d32fa21 move nextjs into web directory (#1223) 2024-02-22 14:02:28 +01:00
Max DeichmannandGitHub 7927d0013f feat(API): catch prisma errors individually for public apis (#1215) 2024-02-21 21:05:19 +00:00
Marc KlingenandGitHub 147262a437 fix(ui): long observation/trace names should break instead of overflow (#1218) 2024-02-21 00:07:11 +00:00
Max Deichmann c02bb556ca chore: release v2.7.0
CI/CD / lint (push) Waiting to run
CI/CD / test-docker-build (push) Waiting to run
CI/CD / tests (18) (push) Waiting to run
CI/CD / tests (20) (push) Waiting to run
CI/CD / e2e-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-02-20 20:05:45 +01:00
Richard KrümmelandGitHub 1b792d844f fix(ui): fix prompt config display logic (#1211) 2024-02-20 13:57:45 +01:00
Max DeichmannandGitHub 52a42812b4 refactor: add kysely (#1210) 2024-02-20 11:24:22 +00:00
3b84af8cbf feat: add prompt config (#1147)
Co-authored-by: Max Deichmann <m.deichmann@tum.de>
2024-02-20 10:31:44 +00:00
Marc KlingenandGitHub 1521445939 fix(ui): uriencode filter state to support special characters (#1209) 2024-02-20 00:56:18 +00:00
Marc KlingenandGitHub a1e961de09 feat(api): add htmlPath and totalCost to GET traces/[id] (#1207) 2024-02-20 00:34:59 +00:00
Marc Klingen c360cf10d4 chore: release v2.6.0
CI/CD / lint (push) Waiting to run
CI/CD / test-docker-build (push) Waiting to run
CI/CD / tests (18) (push) Waiting to run
CI/CD / tests (20) (push) Waiting to run
CI/CD / e2e-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-02-19 23:35:23 +01:00
Marc KlingenandGitHub 3e2f3f3d28 feat(api): add GET metrics/daily endpoint to fetch aggregate statistics (#1204) 2024-02-19 23:29:51 +01:00
Richard KrümmelandGitHub 11e5da2cd8 feat(ui): show full id in session table (#1165) 2024-02-19 22:09:08 +00:00
Max DeichmannandGitHub 8205595f2c fix: fix scores table (#1205) 2024-02-19 20:49:15 +00:00
101277cab8 feat: add traces filter to dashboard (#1202)
Co-authored-by: RichardKruemmel <richard.kruemmel@code.berlin>
2024-02-19 20:36:12 +00:00
Marc Klingen 954e31ca4a fix(api): type of totalCost on trace 2024-02-19 17:51:49 +01:00
Marc KlingenandGitHub 43c3258135 feat(api): add htmlPath, latency, totalCost to GET /traces (#1201) 2024-02-19 15:39:02 +00:00
Marc KlingenandGitHub eb8ae94a5d feat(dashboard): add 75th percentile to latency graph (#1195) 2024-02-17 12:07:39 +00:00
George GebbettandGitHub 33abe53acc feat(ui): preview of OpenAI function calls (#981) 2024-02-16 15:47:51 +00:00
5e8ec0bd13 feat(api): add DELETE /api/public/score/[scoreId] (#1167)
---------

Co-authored-by: Marc Klingen <git@marcklingen.com>
2024-02-16 15:06:02 +00:00
177f370914 feat: display total cost of a session (#1172)
Co-authored-by: gitstart-langfuse <gitstart-langfuse@users.noreply.github.com>
Co-authored-by: Max Deichmann <m.deichmann@tum.de>
2024-02-16 15:12:38 +01:00
5538242649 feat: show total model cost per user (#1173)
Co-authored-by: gitstart-langfuse <gitstart-langfuse@users.noreply.github.com>
Co-authored-by: Max Deichmann <m.deichmann@tum.de>
2024-02-16 15:02:18 +01:00
8c8e58e2b8 fix: Correct sorting on all tables (#1168)
Co-authored-by: gitstart-langfuse <gitstart-langfuse@users.noreply.github.com>
Co-authored-by: Max Deichmann <m.deichmann@tum.de>
2024-02-16 13:00:11 +00:00
dependabot[bot]GitHubdependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
a2c367c1b6 chore(deps): bump core-js from 3.35.1 to 3.36.0 (#1177)
Bumps [core-js](https://github.com/zloirock/core-js/tree/HEAD/packages/core-js) from 3.35.1 to 3.36.0.
- [Release notes](https://github.com/zloirock/core-js/releases)
- [Changelog](https://github.com/zloirock/core-js/blob/master/CHANGELOG.md)
- [Commits](https://github.com/zloirock/core-js/commits/v3.36.0/packages/core-js)

---
updated-dependencies:
- dependency-name: core-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-02-16 03:03:39 +00:00
dependabot[bot]GitHubdependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
da0ac73603 chore(deps): bump the patches group with 4 updates (#1176)
Bumps the patches group with 4 updates: [@sentry/nextjs](https://github.com/getsentry/sentry-javascript), [@sentry/profiling-node](https://github.com/getsentry/sentry-javascript), [@sentry/types](https://github.com/getsentry/sentry-javascript) and [posthog-node](https://github.com/PostHog/posthog-js-lite/tree/HEAD/posthog-node).


Updates `@sentry/nextjs` from 7.101.0 to 7.101.1
- [Release notes](https://github.com/getsentry/sentry-javascript/releases)
- [Changelog](https://github.com/getsentry/sentry-javascript/blob/7.101.1/CHANGELOG.md)
- [Commits](https://github.com/getsentry/sentry-javascript/compare/7.101.0...7.101.1)

Updates `@sentry/profiling-node` from 7.101.0 to 7.101.1
- [Release notes](https://github.com/getsentry/sentry-javascript/releases)
- [Changelog](https://github.com/getsentry/sentry-javascript/blob/7.101.1/CHANGELOG.md)
- [Commits](https://github.com/getsentry/sentry-javascript/compare/7.101.0...7.101.1)

Updates `@sentry/types` from 7.101.0 to 7.101.1
- [Release notes](https://github.com/getsentry/sentry-javascript/releases)
- [Changelog](https://github.com/getsentry/sentry-javascript/blob/7.101.1/CHANGELOG.md)
- [Commits](https://github.com/getsentry/sentry-javascript/compare/7.101.0...7.101.1)

Updates `posthog-node` from 3.6.2 to 3.6.3
- [Release notes](https://github.com/PostHog/posthog-js-lite/releases)
- [Changelog](https://github.com/PostHog/posthog-js-lite/blob/main/posthog-node/CHANGELOG.md)
- [Commits](https://github.com/PostHog/posthog-js-lite/commits/posthog-node-v3.6.3/posthog-node)

---
updated-dependencies:
- dependency-name: "@sentry/nextjs"
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: patches
- dependency-name: "@sentry/profiling-node"
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: patches
- dependency-name: "@sentry/types"
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: patches
- dependency-name: posthog-node
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: patches
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-02-16 02:59:15 +00:00
Marc Klingen e60e74478c chore: release v2.5.1
CI/CD / lint (push) Waiting to run
CI/CD / test-docker-build (push) Waiting to run
CI/CD / tests (18) (push) Waiting to run
CI/CD / tests (20) (push) Waiting to run
CI/CD / e2e-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-02-16 03:13:32 +01:00
Marc KlingenandGitHub 329f83e844 feat(api): optionally set timestamp when creating a trace (#1181)
Fixes #1157
2024-02-16 01:53:00 +00:00
Marc KlingenandGitHub 28c035da76 refactor: rbac access checks (#1180) 2024-02-16 01:35:22 +00:00
Max Deichmann 4dfeceb9d6 chore: release v2.5.0
CI/CD / lint (push) Waiting to run
CI/CD / test-docker-build (push) Waiting to run
CI/CD / tests (18) (push) Waiting to run
CI/CD / tests (20) (push) Waiting to run
CI/CD / e2e-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-02-15 23:14:05 +01:00
Max DeichmannandGitHub df802184a6 fix: fix order of matched models (#1175) 2024-02-15 22:11:50 +00:00
Marc KlingenandGitHub 407d169095 feat(api): add latency to generations endpoints (#1169) 2024-02-15 23:01:24 +01:00
Max DeichmannandGitHub e1958e73d4 fix: fix gpt-4-turbo-preview prices (#1174) 2024-02-15 21:20:47 +00:00
dependabot[bot]GitHubdependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2a08c3395c chore(deps): bump the sentry group with 3 updates (#1151)
Bumps the sentry group with 3 updates: [@sentry/nextjs](https://github.com/getsentry/sentry-javascript), [@sentry/profiling-node](https://github.com/getsentry/sentry-javascript) and [@sentry/types](https://github.com/getsentry/sentry-javascript).


Updates `@sentry/nextjs` from 7.100.1 to 7.101.0
- [Release notes](https://github.com/getsentry/sentry-javascript/releases)
- [Changelog](https://github.com/getsentry/sentry-javascript/blob/develop/CHANGELOG.md)
- [Commits](https://github.com/getsentry/sentry-javascript/compare/7.100.1...7.101.0)

Updates `@sentry/profiling-node` from 7.100.1 to 7.101.0
- [Release notes](https://github.com/getsentry/sentry-javascript/releases)
- [Changelog](https://github.com/getsentry/sentry-javascript/blob/develop/CHANGELOG.md)
- [Commits](https://github.com/getsentry/sentry-javascript/compare/7.100.1...7.101.0)

Updates `@sentry/types` from 7.100.1 to 7.101.0
- [Release notes](https://github.com/getsentry/sentry-javascript/releases)
- [Changelog](https://github.com/getsentry/sentry-javascript/blob/develop/CHANGELOG.md)
- [Commits](https://github.com/getsentry/sentry-javascript/compare/7.100.1...7.101.0)

---
updated-dependencies:
- dependency-name: "@sentry/nextjs"
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: sentry
- dependency-name: "@sentry/profiling-node"
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: sentry
- dependency-name: "@sentry/types"
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: sentry
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-02-15 02:19:16 +00:00
dependabot[bot]GitHubdependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
5c405f4ed8 chore(deps): bump the patches group with 3 updates (#1162)
Bumps the patches group with 3 updates: [next-auth](https://github.com/nextauthjs/next-auth), [posthog-js](https://github.com/PostHog/posthog-js) and [release-it](https://github.com/release-it/release-it).


Updates `next-auth` from 4.24.5 to 4.24.6
- [Release notes](https://github.com/nextauthjs/next-auth/releases)
- [Commits](https://github.com/nextauthjs/next-auth/compare/next-auth@4.24.5...next-auth@4.24.6)

Updates `posthog-js` from 1.105.7 to 1.105.9
- [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/1.105.7...v1.105.9)

Updates `release-it` from 17.0.3 to 17.0.5
- [Release notes](https://github.com/release-it/release-it/releases)
- [Changelog](https://github.com/release-it/release-it/blob/main/CHANGELOG.md)
- [Commits](https://github.com/release-it/release-it/compare/17.0.3...17.0.5)

---
updated-dependencies:
- dependency-name: next-auth
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: patches
- dependency-name: posthog-js
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: patches
- dependency-name: release-it
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: patches
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-02-15 02:14:33 +00:00
Marc Klingen 25cc29f353 fix(ui): color of checkboxes 2024-02-15 01:36:02 +01:00
Marc Klingen b5e1b9916f chore: release v2.4.3
CI/CD / lint (push) Waiting to run
CI/CD / test-docker-build (push) Waiting to run
CI/CD / tests (18) (push) Waiting to run
CI/CD / tests (20) (push) Waiting to run
CI/CD / e2e-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-02-15 01:15:58 +01:00
Marc KlingenandGitHub 8075996a1c fix(ui): overflow and responsive ui in trace view (#1164) 2024-02-15 00:13:48 +00:00
Marc KlingenandGitHub 80cc85d756 perf(prompts): add db indicies on project_id and id (#1163) 2024-02-14 23:30:41 +00:00
Marc KlingenandGitHub 45c611bc6d fix(prompts): allow underscores in variables when editing a prompt via UI (#1161) 2024-02-14 23:25:09 +01:00
Richard KrümmelandGitHub 1478aa9c2b fix: dashboard drop down selector on mobile view (#1153) 2024-02-14 14:53:13 +01:00
Marc KlingenandGitHub 4944408b62 fix(cloud): show plan limit only on hobby plan (#1152) 2024-02-14 01:09:18 +00:00
Marc Klingen f6e81f818b fix(cloud): hobby plan usage limit 2024-02-14 01:06:07 +01:00
Marc KlingenandGitHub b2d5839dcd chore(cloud): update stripe billing link (#1149) 2024-02-13 22:03:54 +00:00
Marc Klingen 2608878d20 chore: release v2.4.2
CI/CD / lint (push) Waiting to run
CI/CD / test-docker-build (push) Waiting to run
CI/CD / tests (18) (push) Waiting to run
CI/CD / tests (20) (push) Waiting to run
CI/CD / e2e-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-02-13 15:53:15 +01:00
Marc Klingen 62a2857619 docs: add DB_EXPORT_PAGE_SIZE to .env.prod.example 2024-02-13 15:48:49 +01:00
0c1942c2b3 fix(docker): remove the need for runtime variables at build time (#1067)
---------

Co-authored-by: Marc Klingen <git@marcklingen.com>
2024-02-13 15:40:16 +01:00
b95c93cdb1 fix: memory optimizes generation.export trpc to allow for larger exports (#1124)
* fix: memory optimizes generation.export trpc

* test: disable S3 uploads in export test

* refactor: modularize generations.export

* feat: adds streaming to S3 bucket

* feat: adds DB_EXPORT_PAGE_SIZE environment variable

* refactor: clean up generationsRouter

---------

Co-authored-by: Max Deichmann <m.deichmann@tum.de>
2024-02-13 12:55:12 +00:00
Marc Klingen 3691099c74 chore: release v2.4.1
CI/CD / lint (push) Waiting to run
CI/CD / test-docker-build (push) Waiting to run
CI/CD / tests (18) (push) Waiting to run
CI/CD / tests (20) (push) Waiting to run
CI/CD / e2e-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-02-13 13:26:51 +01:00
Marc KlingenandGitHub 3f1ae71eec fix: feedback api route (#1146)
Fixes #1144
2024-02-13 13:14:27 +01:00
Max DeichmannandGitHub 668cf3f6c2 Feat: update openai pricing (#1145) 2024-02-13 11:36:22 +00:00
Marc KlingenandGitHub 6221be155d feat(security): add audit logging (#1142) 2024-02-13 11:35:54 +01:00
Marc KlingenandGitHub 3e13d191db chore: drop middleware previously used for IP blocklisting (#1143) 2024-02-12 22:53:07 +00:00
dependabot[bot]GitHubdependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
61297826f4 chore(deps): bump lucide-react from 0.323.0 to 0.330.0 (#1140)
Bumps [lucide-react](https://github.com/lucide-icons/lucide/tree/HEAD/packages/lucide-react) from 0.323.0 to 0.330.0.
- [Release notes](https://github.com/lucide-icons/lucide/releases)
- [Commits](https://github.com/lucide-icons/lucide/commits/0.330.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-02-12 23:47:36 +01:00
Max DeichmannandGitHub 3268ef6488 style: adjust contributing template (#1136)
remove
2024-02-12 20:54:20 +01:00
Marc Klingen 4aba13ec35 chore: release v2.4.0
CI/CD / lint (push) Waiting to run
CI/CD / test-docker-build (push) Waiting to run
CI/CD / tests (18) (push) Waiting to run
CI/CD / tests (20) (push) Waiting to run
CI/CD / e2e-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-02-12 17:52:28 +01:00
Marc Klingen 0f5d5cd9a0 fix(ui): invalidation of traces after delete 2024-02-12 17:46:58 +01:00
Marc KlingenandGitHub 78df145e20 fix(ui): invalidation of traces when deleting a single trace (#1134) 2024-02-12 12:00:57 +00:00
Max DeichmannandGitHub b35618f1c9 refactor: add betterstack env variable to env.prod.example (#1132) 2024-02-12 10:17:51 +00:00
dependabot[bot]GitHubdependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
a139ada85e chore(deps): bump the sentry group with 3 updates (#1110)
Bumps the sentry group with 3 updates: [@sentry/nextjs](https://github.com/getsentry/sentry-javascript), [@sentry/profiling-node](https://github.com/getsentry/sentry-javascript) and [@sentry/types](https://github.com/getsentry/sentry-javascript).


Updates `@sentry/nextjs` from 7.99.0 to 7.100.1
- [Release notes](https://github.com/getsentry/sentry-javascript/releases)
- [Changelog](https://github.com/getsentry/sentry-javascript/blob/7.100.1/CHANGELOG.md)
- [Commits](https://github.com/getsentry/sentry-javascript/compare/7.99.0...7.100.1)

Updates `@sentry/profiling-node` from 1.3.5 to 7.100.1
- [Release notes](https://github.com/getsentry/sentry-javascript/releases)
- [Changelog](https://github.com/getsentry/sentry-javascript/blob/7.100.1/CHANGELOG.md)
- [Commits](https://github.com/getsentry/sentry-javascript/commits/7.100.1)

Updates `@sentry/types` from 7.99.0 to 7.100.1
- [Release notes](https://github.com/getsentry/sentry-javascript/releases)
- [Changelog](https://github.com/getsentry/sentry-javascript/blob/7.100.1/CHANGELOG.md)
- [Commits](https://github.com/getsentry/sentry-javascript/compare/7.99.0...7.100.1)

---
updated-dependencies:
- dependency-name: "@sentry/nextjs"
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: sentry
- dependency-name: "@sentry/profiling-node"
  dependency-type: direct:production
  update-type: version-update:semver-major
  dependency-group: sentry
- dependency-name: "@sentry/types"
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: sentry
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-02-12 00:07:57 +00:00
dependabot[bot]GitHubdependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
cdfb0c6b7c chore(deps): bump posthog-js from 1.104.4 to 1.105.7 (#1131)
Bumps [posthog-js](https://github.com/PostHog/posthog-js) from 1.104.4 to 1.105.7.
- [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.104.4...1.105.7)

---
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-02-12 01:02:10 +01:00
dependabot[bot]GitHubdependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
c0bbf39b29 chore(deps-dev): bump the patches group with 3 updates (#1130)
Bumps the patches group with 3 updates: [@types/react-dom](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/react-dom), [postcss](https://github.com/postcss/postcss) and [tsx](https://github.com/privatenumber/tsx).


Updates `@types/react-dom` from 18.2.18 to 18.2.19
- [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases)
- [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/react-dom)

Updates `postcss` from 8.4.34 to 8.4.35
- [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.34...8.4.35)

Updates `tsx` from 4.7.0 to 4.7.1
- [Release notes](https://github.com/privatenumber/tsx/releases)
- [Changelog](https://github.com/privatenumber/tsx/blob/develop/release.config.cjs)
- [Commits](https://github.com/privatenumber/tsx/compare/v4.7.0...v4.7.1)

---
updated-dependencies:
- dependency-name: "@types/react-dom"
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: patches
- dependency-name: postcss
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: patches
- dependency-name: tsx
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: patches
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-02-11 23:44:28 +00:00
Marc Klingen a6900ad3c4 fix(ui): use lightner colors in observation tree 2024-02-12 00:31:00 +01:00
21940a0464 feat(ui): improve trace view: collapse/expand observation tree + color coding of observation types (#916)
---------

Co-authored-by: Marc Klingen <git@marcklingen.com>
2024-02-11 23:47:46 +01:00
dependabot[bot]GitHubdependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
fe642f8e9e chore(deps-dev): bump @typescript-eslint/eslint-plugin from 6.20.0 to 6.21.0 (#1112)
chore(deps-dev): bump @typescript-eslint/eslint-plugin

Bumps [@typescript-eslint/eslint-plugin](https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/eslint-plugin) from 6.20.0 to 6.21.0.
- [Release notes](https://github.com/typescript-eslint/typescript-eslint/releases)
- [Changelog](https://github.com/typescript-eslint/typescript-eslint/blob/main/packages/eslint-plugin/CHANGELOG.md)
- [Commits](https://github.com/typescript-eslint/typescript-eslint/commits/v6.21.0/packages/eslint-plugin)

---
updated-dependencies:
- dependency-name: "@typescript-eslint/eslint-plugin"
  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-02-11 22:05:53 +00:00
c803b0d9c2 feat(ui): delete prompts and prompt versions + deduplication of prompt variables (#1116)
* fix and feat: update Var renaming and add functionality to delete prompt and versions

* pass ci by removing unused components

* ux improvements

---------

Co-authored-by: Marc Klingen <git@marcklingen.com>
2024-02-11 22:00:31 +00:00
Marc KlingenandGitHub e7086cf044 fix(ci): temporarily fix modelmatch test by waiting 5 sec (#1129)
* fix(ci): temporarily fix modelmatch test by waiting 5 sec

* fix
2024-02-11 21:56:19 +00:00
Marc KlingenandGitHub 9d49bbe987 chore(ui): responsive layout (#1126) 2024-02-10 03:29:38 +00:00
477 changed files with 27422 additions and 18569 deletions
+5 -1
View File
@@ -29,4 +29,8 @@ S3_ENDPOINT=
S3_ACCESS_KEY_ID=
S3_SECRET_ACCESS_KEY=
S3_BUCKET_NAME=
S3_REGION=
S3_REGION=
# Set during docker build of application
# Used to disable environment verification at build time
# DOCKER_BUILD=1
+6
View File
@@ -57,6 +57,9 @@ SALT="salt"
# S3_BUCKET_NAME=
# S3_REGION=
# 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
@@ -81,6 +84,9 @@ SALT="salt"
# NEXT_SENTRY_PROJECT=
# SENTRY_AUTH_TOKEN=
# Betterstack
# LANGFUSE_TEAM_BETTERSTACK_TOKEN=
# Demo project that users can use to try the platform
# NEXT_PUBLIC_DEMO_PROJECT_ID=
+3
View File
@@ -0,0 +1,3 @@
node_modules
build
coverage
-7
View File
@@ -9,10 +9,3 @@ body:
attributes:
label: Additional information
description: Add any other information related to the change here. If your idea is related to any issues or discussions, link them here.
- type: checkboxes
id: contribute
attributes:
label: Contribute
description: Are you willing to contribute to the implementation of this idea?
options:
- label: Yes, I can implement this and raise a PR
+37 -22
View File
@@ -12,6 +12,10 @@ on:
branches:
- "main"
defaults:
run:
working-directory: web
jobs:
lint:
runs-on: ubuntu-latest
@@ -21,22 +25,23 @@ jobs:
with:
node-version: 20
cache: "npm"
cache-dependency-path: "**/package-lock.json"
- name: install dependencies
run: |
npm ci
- name: Load default env
working-directory: .
run: |
cp .env.dev.example .env
- name: lint
run: npm run lint
test-docker-build:
defaults:
run:
working-directory: web
runs-on: ubuntu-latest
env:
DATABASE_URL: postgresql://postgres:postgres@localhost:5432/postgres
NEXTAUTH_SECRET: "secret"
SALT: "salt"
NEXTAUTH_URL: "http://localhost:3030"
REGISTRY: ghcr.io
IMAGE_NAME: ${{ github.repository }}
@@ -45,6 +50,7 @@ jobs:
uses: actions/setup-node@v3
with:
node-version: 20
cache-dependency-path: "**/package-lock.json"
- name: Checkout
uses: actions/checkout@v3
@@ -52,15 +58,13 @@ jobs:
- name: Build Docker image
uses: docker/build-push-action@v4
with:
context: .
context: web
push: false
build-args: |
DATABASE_URL=${{ env.DATABASE_URL }}
NEXTAUTH_SECRET=${{ env.NEXTAUTH_SECRET }}
NEXTAUTH_URL=${{ env.NEXTAUTH_URL }}
SALT=${{ env.SALT }}
tests:
defaults:
run:
working-directory: web
runs-on: ubuntu-latest
strategy:
matrix:
@@ -77,19 +81,26 @@ jobs:
with:
node-version: ${{ matrix.node-version }}
cache: "npm"
cache-dependency-path: "**/package-lock.json"
- name: install dependencies
run: |
npm ci
- name: Load default env
working-directory: .
run: |
cp .env.dev.example .env
cp .env.dev.example web/.env
- name: Run, migrate, seed DB
- name: Run + migrate
working-directory: .
run: |
docker-compose -f docker-compose.dev.yml up -d
sleep 5 # Wait for PostgreSQL to accept connections
- name: Seed DB
run: |
npx --yes prisma migrate reset --force --skip-generate
- name: Build
@@ -102,6 +113,9 @@ jobs:
run: npm run test
e2e-tests:
defaults:
run:
working-directory: web
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
@@ -109,19 +123,26 @@ jobs:
with:
node-version: 20
cache: "npm"
cache-dependency-path: "**/package-lock.json"
- name: install dependencies
run: |
npm ci
- name: Load default env
working-directory: .
run: |
cp .env.dev.example .env
cp .env.dev.example web/.env
- name: Run, migrate, seed DB
- name: Run + migrate
working-directory: .
run: |
docker-compose -f docker-compose.dev.yml up -d
sleep 5 # Wait for PostgreSQL to accept connections
- name: Seed DB
run: |
npx --yes prisma migrate reset --force --skip-generate
- name: Build
@@ -142,9 +163,11 @@ jobs:
- name: Successful deploy
if: ${{ !(contains(needs.*.result, 'failure')) }}
run: exit 0
working-directory: .
- name: Failing deploy
if: ${{ contains(needs.*.result, 'failure') }}
run: exit 1
working-directory: .
push-docker-image:
needs: all-ci-passed
@@ -152,10 +175,6 @@ jobs:
environment: "protected branches"
runs-on: ubuntu-latest
env:
DATABASE_URL: postgresql://postgres:postgres@localhost:5432/postgres
NEXTAUTH_SECRET: "secret"
SALT: "salt"
NEXTAUTH_URL: "http://localhost:3030"
REGISTRY: ghcr.io
IMAGE_NAME: ${{ github.repository }}
permissions:
@@ -167,6 +186,7 @@ jobs:
uses: actions/setup-node@v3
with:
node-version: 20
cache-dependency-path: "**/package-lock.json"
- name: Checkout
uses: actions/checkout@v3
@@ -194,12 +214,7 @@ jobs:
- name: Build and push Docker image
uses: docker/build-push-action@v4
with:
context: .
context: web
push: true
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
build-args: |
DATABASE_URL=${{ env.DATABASE_URL }}
NEXTAUTH_SECRET=${{ env.NEXTAUTH_SECRET }}
NEXTAUTH_URL=${{ env.NEXTAUTH_URL }}
SALT=${{ env.SALT }}
+5 -1
View File
@@ -48,4 +48,8 @@ yarn-error.log*
/generated/typescript-server
# openapi spec that is copied during build
/public/openapi*.yml
/public/openapi*.yml
# vscode
.devcontainer
+16 -10
View File
@@ -120,25 +120,31 @@ Requirements
**Steps**
1. Fork the the repository and clone it locally
2. Install dependencies
```bash
npm install
```
3. Run the development database
2. Run the development database
```bash
docker-compose -f docker-compose.dev.yml up -d
```
4. Create an env file
3. Create an env file
```bash
cp .env.dev.example .env
```
5. Run the migrations
4. CD into the web directory
```bash
cd web
```
5. Install dependencies
```bash
npm install
```
6. Run the migrations
```bash
npm run db:migrate
@@ -148,7 +154,7 @@ Requirements
# npm run db:seed:examples
```
6. Start the development server
7. Start the development server
```bash
npm run dev
-6
View File
@@ -4,17 +4,11 @@ services:
langfuse-server:
build:
dockerfile: Dockerfile
args:
- DATABASE_URL=postgresql://postgres:postgres@db:5432/postgres
- NEXTAUTH_SECRET=mysecret
- SALT=mysalt
- NEXTAUTH_URL=http://localhost:3000
depends_on:
- db
ports:
- "3000:3000"
environment:
- NODE_ENV=production
- DATABASE_URL=postgresql://postgres:postgres@db:5432/postgres
- NEXTAUTH_SECRET=mysecret
- SALT=mysalt
-1
View File
@@ -8,7 +8,6 @@ services:
ports:
- "3000:3000"
environment:
- NODE_ENV=production
- DATABASE_URL=postgresql://postgres:postgres@db:5432/postgres
- NEXTAUTH_SECRET=mysecret
- SALT=mysalt
+1 -1
View File
@@ -15,7 +15,7 @@ groups:
version: 0.0.26
output:
location: local-file-system
path: ../../../generated/openapi-client
path: ../../../web/generated/openapi-client
config:
namespaceExport: Langfuse
allowCustomFetcher: true
+18 -2
View File
@@ -18,18 +18,33 @@ types:
public:
type: optional<boolean>
docs: Public traces are accessible via url without login
TraceWithDetails:
TraceWithDetails: # GET /traces
extends: Trace
properties:
htmlPath:
type: string
docs: Path of trace in Langfuse UI
latency:
type: double
docs: Latency of trace in seconds
totalCost:
type: double
docs: Cost of trace in USD
observations:
type: list<string>
docs: List of observation ids
scores:
type: list<string>
docs: List of score ids
TraceWithFullDetails:
TraceWithFullDetails: # GET traces/[traceID]
extends: Trace
properties:
htmlPath:
type: string
docs: Path of trace in Langfuse UI
totalCost:
type: double
docs: Cost of trace in USD
observations: list<ObservationsView>
scores: list<Score>
Session:
@@ -72,6 +87,7 @@ types:
calculatedInputCost: optional<double>
calculatedOutputCost: optional<double>
calculatedTotalCost: optional<double>
latency: optional<double>
Usage:
properties:
@@ -128,6 +128,7 @@ types:
TraceBody:
properties:
id: optional<string>
timestamp: optional<datetime>
name: optional<string>
userId: optional<string>
input: optional<unknown>
+48
View File
@@ -0,0 +1,48 @@
# yaml-language-server: $schema=https://raw.githubusercontent.com/fern-api/fern/main/fern.schema.json
imports:
pagination: ./utils/pagination.yml
commons: ./commons.yml
service:
auth: true
base-path: /api/public
endpoints:
daily:
docs: Get daily metrics of the Langfuse project
method: GET
path: /metrics/daily
request:
name: GetDailyMetricsRequest
query-parameters:
page: optional<integer>
limit: optional<integer>
traceName:
type: optional<string>
docs: Optional filter by the name of the trace
userId:
type: optional<string>
docs: Optional filter by the userId associated with the trace
tags:
type: optional<string>
allow-multiple: true
docs: Optional filter for metrics where traces include all of these tags
response: DailyMetrics
types:
DailyMetrics:
properties:
data:
type: list<DailyMetricsDetails>
docs: A list of daily metrics, only days with ingested data are included.
meta: pagination.MetaResponse
DailyMetricsDetails:
properties:
date: date
countTraces: integer
totalCost: double
usage: list<UsageByModel>
UsageByModel:
docs: Daily usage of a given model. Usage corresponds to the unit set for the specific model (e.g. tokens).
properties:
model: string
inputUsage: integer
outputUsage: integer
totalUsage: integer
+3
View File
@@ -29,8 +29,11 @@ types:
name: string
isActive: boolean
prompt: string
config: optional<unknown>
Prompt:
properties:
name: string
version: integer
prompt: string
config: unknown
+8
View File
@@ -24,6 +24,14 @@ service:
userId: optional<string>
name: optional<string>
response: Scores
delete:
docs: Delete a score
method: DELETE
path: /scores/{scoreId}
path-parameters:
scoreId:
type: string
docs: The unique langfuse identifier of a score
types:
CreateScoreRequest:
properties:
+1 -1
View File
@@ -6,7 +6,7 @@ groups:
version: 0.0.28
output:
location: local-file-system
path: ../../../generated/openapi-server
path: ../../../web/generated/openapi-server
config:
namespaceExport: Langfuse
allowCustomFetcher: true
+206
View File
@@ -387,6 +387,81 @@ paths:
$ref: '#/components/schemas/IngestionEvent'
required:
- batch
/api/public/metrics/daily:
get:
description: Get daily metrics of the Langfuse project
operationId: metrics_daily
tags:
- Metrics
parameters:
- name: page
in: query
required: false
schema:
type: integer
nullable: true
- name: limit
in: query
required: false
schema:
type: integer
nullable: true
- name: traceName
in: query
description: Optional filter by the name of the trace
required: false
schema:
type: string
nullable: true
- name: userId
in: query
description: Optional filter by the userId associated with the trace
required: false
schema:
type: string
nullable: true
- name: tags
in: query
description: Optional filter for metrics where traces include all of these tags
required: false
schema:
type: array
items:
type: string
nullable: true
responses:
'200':
description: ''
content:
application/json:
schema:
$ref: '#/components/schemas/DailyMetrics'
'400':
description: ''
content:
application/json:
schema: {}
'401':
description: ''
content:
application/json:
schema: {}
'403':
description: ''
content:
application/json:
schema: {}
'404':
description: ''
content:
application/json:
schema: {}
'405':
description: ''
content:
application/json:
schema: {}
security: *ref_0
/api/public/observations/{observationId}:
get:
description: Get a specific observation
@@ -761,6 +836,48 @@ paths:
application/json:
schema: {}
security: *ref_0
/api/public/scores/{scoreId}:
delete:
description: Delete a score
operationId: score_delete
tags:
- Score
parameters:
- name: scoreId
in: path
description: The unique langfuse identifier of a score
required: true
schema:
type: string
responses:
'204':
description: ''
'400':
description: ''
content:
application/json:
schema: {}
'401':
description: ''
content:
application/json:
schema: {}
'403':
description: ''
content:
application/json:
schema: {}
'404':
description: ''
content:
application/json:
schema: {}
'405':
description: ''
content:
application/json:
schema: {}
security: *ref_0
/api/public/sessions/{sessionId}:
get:
description: Get a session
@@ -983,6 +1100,17 @@ components:
title: TraceWithDetails
type: object
properties:
htmlPath:
type: string
description: Path of trace in Langfuse UI
latency:
type: number
format: double
description: Latency of trace in seconds
totalCost:
type: number
format: double
description: Cost of trace in USD
observations:
type: array
items:
@@ -994,6 +1122,9 @@ components:
type: string
description: List of score ids
required:
- htmlPath
- latency
- totalCost
- observations
- scores
allOf:
@@ -1002,6 +1133,13 @@ components:
title: TraceWithFullDetails
type: object
properties:
htmlPath:
type: string
description: Path of trace in Langfuse UI
totalCost:
type: number
format: double
description: Cost of trace in USD
observations:
type: array
items:
@@ -1011,6 +1149,8 @@ components:
items:
$ref: '#/components/schemas/Score'
required:
- htmlPath
- totalCost
- observations
- scores
allOf:
@@ -1134,6 +1274,10 @@ components:
type: number
format: double
nullable: true
latency:
type: number
format: double
nullable: true
allOf:
- $ref: '#/components/schemas/Observation'
Usage:
@@ -1720,6 +1864,10 @@ components:
id:
type: string
nullable: true
timestamp:
type: string
format: date-time
nullable: true
name:
type: string
nullable: true
@@ -1936,6 +2084,60 @@ components:
required:
- successes
- errors
DailyMetrics:
title: DailyMetrics
type: object
properties:
data:
type: array
items:
$ref: '#/components/schemas/DailyMetricsDetails'
description: A list of daily metrics, only days with ingested data are included.
meta:
$ref: '#/components/schemas/utilsMetaResponse'
required:
- data
- meta
DailyMetricsDetails:
title: DailyMetricsDetails
type: object
properties:
date:
type: string
countTraces:
type: integer
totalCost:
type: number
format: double
usage:
type: array
items:
$ref: '#/components/schemas/UsageByModel'
required:
- date
- countTraces
- totalCost
- usage
UsageByModel:
title: UsageByModel
type: object
description: >-
Daily usage of a given model. Usage corresponds to the unit set for the
specific model (e.g. tokens).
properties:
model:
type: string
inputUsage:
type: integer
outputUsage:
type: integer
totalUsage:
type: integer
required:
- model
- inputUsage
- outputUsage
- totalUsage
Observations:
title: Observations
type: object
@@ -1993,6 +2195,8 @@ components:
type: boolean
prompt:
type: string
config:
nullable: true
required:
- name
- isActive
@@ -2007,10 +2211,12 @@ components:
type: integer
prompt:
type: string
config: {}
required:
- name
- version
- prompt
- config
CreateScoreRequest:
title: CreateScoreRequest
type: object
+93 -2
View File
@@ -320,7 +320,7 @@
"auth": null,
"body": {
"mode": "raw",
"raw": "{\n \"batch\": [\n {\n \"type\": \"trace-create\",\n \"body\": {\n \"id\": \"example\",\n \"name\": \"example\",\n \"userId\": \"example\",\n \"input\": \"UNKNOWN\",\n \"output\": \"UNKNOWN\",\n \"sessionId\": \"example\",\n \"release\": \"example\",\n \"version\": \"example\",\n \"metadata\": \"UNKNOWN\",\n \"tags\": [\n \"example\"\n ],\n \"public\": true\n },\n \"id\": \"example\",\n \"timestamp\": \"example\",\n \"metadata\": \"UNKNOWN\"\n }\n ]\n}",
"raw": "{\n \"batch\": [\n {\n \"type\": \"trace-create\",\n \"body\": {\n \"id\": \"example\",\n \"timestamp\": \"1994-11-05T13:15:30Z\",\n \"name\": \"example\",\n \"userId\": \"example\",\n \"input\": \"UNKNOWN\",\n \"output\": \"UNKNOWN\",\n \"sessionId\": \"example\",\n \"release\": \"example\",\n \"version\": \"example\",\n \"metadata\": \"UNKNOWN\",\n \"tags\": [\n \"example\"\n ],\n \"public\": true\n },\n \"id\": \"example\",\n \"timestamp\": \"example\",\n \"metadata\": \"UNKNOWN\"\n }\n ]\n}",
"options": {
"raw": {
"language": "json"
@@ -332,6 +332,65 @@
}
]
},
{
"_type": "container",
"description": null,
"name": "Metrics",
"item": [
{
"_type": "endpoint",
"name": "Daily",
"request": {
"description": "Get daily metrics of the Langfuse project",
"url": {
"raw": "{{baseUrl}}/api/public/metrics/daily?page=&limit=&traceName=&userId=&tags=",
"host": [
"{{baseUrl}}"
],
"path": [
"api",
"public",
"metrics",
"daily"
],
"query": [
{
"key": "page",
"value": "",
"description": null
},
{
"key": "limit",
"value": "",
"description": null
},
{
"key": "traceName",
"value": "",
"description": "Optional filter by the name of the trace"
},
{
"key": "userId",
"value": "",
"description": "Optional filter by the userId associated with the trace"
},
{
"key": "tags",
"value": "",
"description": "Optional filter for metrics where traces include all of these tags"
}
],
"variable": []
},
"header": [],
"method": "GET",
"auth": null,
"body": null
},
"response": []
}
]
},
{
"_type": "container",
"description": null,
@@ -528,7 +587,7 @@
"auth": null,
"body": {
"mode": "raw",
"raw": "{\n \"name\": \"example\",\n \"isActive\": true,\n \"prompt\": \"example\"\n}",
"raw": "{\n \"name\": \"example\",\n \"isActive\": true,\n \"prompt\": \"example\",\n \"config\": \"UNKNOWN\"\n}",
"options": {
"raw": {
"language": "json"
@@ -623,6 +682,38 @@
"body": null
},
"response": []
},
{
"_type": "endpoint",
"name": "Delete",
"request": {
"description": "Delete a score",
"url": {
"raw": "{{baseUrl}}/api/public/scores/:scoreId",
"host": [
"{{baseUrl}}"
],
"path": [
"api",
"public",
"scores",
":scoreId"
],
"query": [],
"variable": [
{
"key": "scoreId",
"value": "",
"description": "The unique langfuse identifier of a score"
}
]
},
"header": [],
"method": "DELETE",
"auth": null,
"body": null
},
"response": []
}
]
},
+196 -16696
View File
File diff suppressed because it is too large Load Diff
+9 -167
View File
@@ -1,176 +1,18 @@
{
"name": "langfuse-core",
"version": "2.3.0",
"private": true,
"name": "langfuse",
"version": "2.7.0",
"scripts": {
"prebuild": "cp generated/openapi-client/openapi.yml public/openapi-client.yml && cp generated/openapi-server/openapi.yml public/openapi-server.yml",
"build": "next build",
"dev": "next dev",
"dx": "npm i && npm run db:reset && npm run db:seed:examples && npm run dev",
"postinstall": "prisma generate",
"lint": "next lint",
"lint:fix": "next lint --fix",
"prettier": "prettier --write ./src *.{ts,js}",
"clean": "rm -rf node_modules",
"start": "next start",
"test": "jest --runInBand",
"test:watch": "jest --watch --runInBand",
"test:e2e": "playwright test",
"infra:dev:up": "docker-compose -f docker-compose.dev.yml up -d",
"infra:dev:down": "docker-compose -f docker-compose.dev.yml down",
"db:migrate": "DISABLE_ERD=false npx prisma migrate dev",
"db:reset": "npx prisma migrate reset",
"db:seed": "npx prisma db seed",
"db:seed:examples": "npx prisma db seed -- --environment examples",
"release": "release-it",
"models:migrate": "tsx scripts/model-match.ts"
},
"prisma": {
"seed": "ts-node -r tsconfig-paths/register --compiler-options {\"module\":\"CommonJS\"} prisma/seed.ts"
},
"dependencies": {
"@anthropic-ai/tokenizer": "^0.0.4",
"@aws-sdk/client-s3": "^3.507.0",
"@aws-sdk/s3-request-presigner": "^3.507.0",
"@headlessui/react": "^1.7.18",
"@heroicons/react": "^2.1.1",
"@hookform/resolvers": "^3.3.4",
"@next-auth/prisma-adapter": "^1.0.7",
"@prisma/client": "^5.9.1",
"@radix-ui/react-accordion": "^1.1.2",
"@radix-ui/react-alert-dialog": "^1.0.5",
"@radix-ui/react-avatar": "^1.0.4",
"@radix-ui/react-checkbox": "^1.0.4",
"@radix-ui/react-collapsible": "^1.0.3",
"@radix-ui/react-dialog": "^1.0.5",
"@radix-ui/react-dropdown-menu": "^2.0.6",
"@radix-ui/react-hover-card": "^1.0.7",
"@radix-ui/react-icons": "^1.3.0",
"@radix-ui/react-label": "^2.0.2",
"@radix-ui/react-popover": "^1.0.7",
"@radix-ui/react-scroll-area": "^1.0.5",
"@radix-ui/react-select": "^2.0.0",
"@radix-ui/react-separator": "^1.0.3",
"@radix-ui/react-slider": "^1.1.2",
"@radix-ui/react-slot": "^1.0.2",
"@radix-ui/react-switch": "^1.0.3",
"@radix-ui/react-tabs": "^1.0.4",
"@radix-ui/react-toggle": "^1.0.3",
"@radix-ui/react-tooltip": "^1.0.7",
"@react-email/components": "^0.0.14",
"@react-email/render": "^0.0.12",
"@sentry/nextjs": "^7.99.0",
"@sentry/profiling-node": "^1.3.5",
"@sentry/types": "^7.88.0",
"@t3-oss/env-nextjs": "^0.8.0",
"@tailwindcss/forms": "^0.5.7",
"@tanstack/react-query": "^4.36.1",
"@tanstack/react-table": "^8.11.8",
"@tremor/react": "^3.11.1",
"@trpc/client": "^10.45.0",
"@trpc/next": "^10.45.0",
"@trpc/react-query": "^10.45.0",
"@trpc/server": "^10.45.0",
"@vercel/edge-config": "^0.4.1",
"bcryptjs": "^2.4.3",
"class-variance-authority": "^0.7.0",
"clsx": "^2.1.0",
"cmdk": "^0.2.1",
"core-js": "^3.35.1",
"cors": "^2.8.5",
"date-fns": "^3.3.1",
"decimal.js": "^10.4.3",
"exponential-backoff": "^3.1.1",
"js-tiktoken": "^1.0.10",
"lodash": "^4.17.21",
"lucide-react": "^0.323.0",
"next": "^14.1.0",
"next-auth": "^4.24.5",
"next-query-params": "^5.0.0",
"nodemailer": "^6.9.9",
"posthog-js": "^1.104.4",
"posthog-node": "^3.6.2",
"react": "18.2.0",
"react-day-picker": "^8.10.0",
"react-dom": "18.2.0",
"react-hook-form": "^7.50.1",
"react-icons": "^5.0.1",
"react-responsive": "^9.0.2",
"react18-json-view": "^0.2.7",
"sonner": "^1.4.0",
"superjson": "2.2.1",
"tailwind-merge": "^2.2.1",
"tailwindcss-animate": "^1.0.7",
"use-query-params": "^2.2.1",
"uuid": "^9.0.1",
"zod": "^3.22.4"
"infra:dev:up": "docker-compose -f ../docker-compose.dev.yml up -d",
"infra:dev:down": "docker-compose -f ../docker-compose.dev.yml down"
},
"author": "",
"license": "MIT",
"devDependencies": {
"@jedmao/location": "^3.0.0",
"@mermaid-js/mermaid-cli": "^10.7.0",
"@playwright/test": "^1.41.2",
"@release-it/bumper": "^6.0.1",
"@testing-library/jest-dom": "^6.4.2",
"@testing-library/react": "^14.2.1",
"@types/bcryptjs": "^2.4.6",
"@types/cors": "^2.8.17",
"@types/eslint": "^8.56.2",
"@types/jest": "^29.5.12",
"@types/lodash": "^4.14.202",
"@types/node": "20.10.5",
"@types/nodemailer": "^6.4.14",
"@types/react": "^18.2.55",
"@types/react-dom": "^18.2.18",
"@types/uuid": "^9.0.8",
"@typescript-eslint/eslint-plugin": "^6.20.0",
"@typescript-eslint/parser": "^6.21.0",
"autoprefixer": "^10.4.17",
"dotenv-cli": "^7.3.0",
"@typescript-eslint/eslint-plugin": "^7.0.2",
"@typescript-eslint/parser": "^7.0.2",
"eslint": "^8.56.0",
"eslint-config-next": "^14.1.0",
"jest": "^29.7.0",
"jest-environment-jsdom": "^29.7.0",
"postcss": "^8.4.34",
"prettier": "^3.2.5",
"prettier-plugin-tailwindcss": "^0.5.11",
"prisma": "^5.9.1",
"prisma-erd-generator": "^1.11.2",
"release-it": "^17.0.3",
"tailwindcss": "^3.4.1",
"ts-node": "^10.9.2",
"tsconfig-paths": "^4.2.0",
"tsx": "^4.7.0",
"typescript": "^5.3.3"
},
"ct3aMetadata": {
"initVersion": "7.13.0"
},
"optionalDependencies": {
"crisp-sdk-web": "^1.0.21"
},
"release-it": {
"git": {
"commitMessage": "chore: release v${version}",
"tagName": "v${version}"
},
"github": {
"release": true,
"web": true,
"autoGenerate": true,
"releaseName": "v${version}",
"comments": {
"submit": true,
"issue": ":rocket: _This issue has been resolved in v${version}. See [${releaseName}](${releaseUrl}) for release notes._",
"pr": ":rocket: _This pull request is included in v${version}. See [${releaseName}](${releaseUrl}) for release notes._"
}
},
"plugins": {
"@release-it/bumper": {
"out": {
"file": "./src/constants/VERSION.ts",
"type": "application/typescript"
}
}
}
"prettier": "^3.2.5"
}
}
Binary file not shown.

Before

Width:  |  Height:  |  Size: 312 KiB

-215
View File
@@ -1,215 +0,0 @@
import { type NestedObservation } from "@/src/utils/types";
import { cn } from "@/src/utils/tailwind";
import { type Trace, type Score } from "@prisma/client";
import { GroupedScoreBadges } from "@/src/components/grouped-score-badge";
import { Fragment } from "react";
import { type ObservationReturnType } from "@/src/server/api/routers/traces";
import { LevelColors } from "@/src/components/level-colors";
import { formatInterval } from "@/src/utils/dates";
export const ObservationTree = (props: {
observations: ObservationReturnType[];
trace: Trace;
scores: Score[];
currentObservationId: string | undefined;
setCurrentObservationId: (id: string | undefined) => void;
showMetrics: boolean;
showScores: boolean;
className?: string;
}) => {
const nestedObservations = nestObservations(props.observations);
return (
<div className={props.className}>
<ObservationTreeTraceNode
trace={props.trace}
scores={props.scores}
currentObservationId={props.currentObservationId}
setCurrentObservationId={props.setCurrentObservationId}
showMetrics={props.showMetrics}
showScores={props.showScores}
/>
<ObservationTreeNode
observations={nestedObservations}
scores={props.scores}
indentationLevel={1}
currentObservationId={props.currentObservationId}
setCurrentObservationId={props.setCurrentObservationId}
showMetrics={props.showMetrics}
showScores={props.showScores}
/>
</div>
);
};
const ObservationTreeTraceNode = (props: {
trace: Trace & { latency?: number };
scores: Score[];
currentObservationId: string | undefined;
setCurrentObservationId: (id: string | undefined) => void;
showMetrics?: boolean;
showScores?: boolean;
}) => (
<div
className={cn(
"group mb-0.5 flex cursor-pointer flex-col gap-1 rounded-sm p-1.5",
props.currentObservationId === undefined ||
props.currentObservationId === ""
? "bg-gray-100"
: "hover:bg-gray-50",
)}
onClick={() => props.setCurrentObservationId(undefined)}
>
<div className="flex gap-2">
<span className={cn("rounded-sm bg-gray-200 p-1 text-xs")}>TRACE</span>
<span className="text-sm">{props.trace.name}</span>
</div>
{props.showMetrics && props.trace.latency ? (
<div className="flex gap-2">
<span className="text-xs text-gray-500">
{formatInterval(props.trace.latency)}
</span>
</div>
) : null}
{props.showScores && props.scores.find((s) => s.observationId === null) ? (
<div className="flex flex-wrap gap-1">
<GroupedScoreBadges
scores={props.scores.filter((s) => s.observationId === null)}
/>
</div>
) : null}
</div>
);
const ObservationTreeNode = (props: {
observations: NestedObservation[];
scores: Score[];
indentationLevel: number;
currentObservationId: string | undefined;
setCurrentObservationId: (id: string | undefined) => void;
showMetrics?: boolean;
showScores?: boolean;
}) => (
<>
{props.observations
.sort((a, b) => a.startTime.getTime() - b.startTime.getTime())
.map((observation) => (
<Fragment key={observation.id}>
<div className="flex">
{Array.from({ length: props.indentationLevel }, (_, i) => (
<div className="mx-2 border-r" key={i} />
))}
<div
className={cn(
"group my-0.5 flex flex-1 cursor-pointer flex-col gap-1 rounded-sm p-1.5",
props.currentObservationId === observation.id
? "bg-gray-100"
: "hover:bg-gray-50",
)}
onClick={() => props.setCurrentObservationId(observation.id)}
>
<div className="flex gap-2">
<span
className={cn(
"self-start rounded-sm bg-gray-200 p-1 text-xs",
)}
>
{observation.type}
</span>
<span className="line-clamp-1 text-sm">{observation.name}</span>
</div>
{props.showMetrics &&
(observation.promptTokens ||
observation.completionTokens ||
observation.totalTokens ||
observation.endTime) && (
<div className="flex gap-2">
{observation.endTime ? (
<span className="text-xs text-gray-500">
{formatInterval(
(observation.endTime.getTime() -
observation.startTime.getTime()) /
1000,
)}
</span>
) : null}
{observation.promptTokens ||
observation.completionTokens ||
observation.totalTokens ? (
<span className="text-xs text-gray-500">
{observation.promptTokens} {" "}
{observation.completionTokens} ({" "}
{observation.totalTokens})
</span>
) : null}
</div>
)}
{observation.level !== "DEFAULT" ? (
<div className="flex">
<span
className={cn(
"rounded-sm p-0.5 text-xs",
LevelColors[observation.level].bg,
LevelColors[observation.level].text,
)}
>
{observation.level}
</span>
</div>
) : null}
{props.showScores &&
props.scores.find((s) => s.observationId === observation.id) ? (
<div className="flex flex-wrap gap-1">
<GroupedScoreBadges
scores={props.scores.filter(
(s) => s.observationId === observation.id,
)}
/>
</div>
) : null}
</div>
</div>
<ObservationTreeNode
observations={observation.children}
scores={props.scores}
indentationLevel={props.indentationLevel + 1}
currentObservationId={props.currentObservationId}
setCurrentObservationId={props.setCurrentObservationId}
showMetrics={props.showMetrics}
showScores={props.showScores}
/>
</Fragment>
))}
</>
);
export function nestObservations(
list: ObservationReturnType[],
): NestedObservation[] {
if (list.length === 0) return [];
// Step 1: Create a map where the keys are object IDs, and the values are
// the corresponding objects with an added 'children' property.
const map = new Map<string, NestedObservation>();
for (const obj of list) {
map.set(obj.id, { ...obj, children: [] });
}
// Step 2: Create another map for the roots of all trees.
const roots = new Map<string, NestedObservation>();
// Step 3: Populate the 'children' arrays and root map.
for (const obj of map.values()) {
if (obj.parentObservationId) {
const parent = map.get(obj.parentObservationId);
if (parent) {
parent.children.push(obj);
}
} else {
roots.set(obj.id, obj);
}
}
// TODO sum token amounts per level
// Step 4: Return the roots.
return Array.from(roots.values());
}
-1
View File
@@ -1 +0,0 @@
export const VERSION = "v2.3.0";
@@ -1,88 +0,0 @@
import {
dateTimeAggregationOptions,
dateTimeAggregationSettings,
} from "@/src/features/dashboard/lib/timeseries-aggregation";
import { z } from "zod";
import {
createTRPCRouter,
protectedProjectProcedure,
} from "@/src/server/api/trpc";
import { executeQuery } from "@/src/server/api/services/query-builder";
import { sqlInterface } from "@/src/server/api/services/sqlInterface";
export const dashboardRouter = createTRPCRouter({
chart: protectedProjectProcedure
.input(sqlInterface.extend({ projectId: z.string() }))
.query(async ({ input, ctx }) => {
return await executeQuery(ctx.prisma, input.projectId, input);
}),
scores: protectedProjectProcedure
.input(
z.object({
projectId: z.string(),
agg: z.enum(dateTimeAggregationOptions),
}),
)
.query(async ({ input, ctx }) => {
// queryRawUnsafe to add input.agg to the WHERE clause
const output = await ctx.prisma.$queryRawUnsafe<
{
date_trunc: Date;
values: {
[key: string]: number;
} | null;
}[]
>(`
WITH timeseries AS (
SELECT
date_trunc('${
dateTimeAggregationSettings[input.agg].date_trunc
}', dt) as date_trunc
FROM generate_series(
NOW() - INTERVAL '${input.agg}', NOW(), INTERVAL '1 minute'
) as dt
WHERE dt > NOW() - INTERVAL '${input.agg}'
GROUP BY 1
),
metrics AS (
SELECT
date_trunc('${
dateTimeAggregationSettings[input.agg].date_trunc
}', scores.timestamp) as date_trunc,
scores.name as metric_name,
AVG(value) as avg_value
FROM scores
LEFT JOIN traces ON scores.trace_id = traces.id
WHERE scores.timestamp > NOW() - INTERVAL '${input.agg}'
AND traces.project_id = '${input.projectId}'
GROUP BY 1,2
),
json_metrics AS (
SELECT
date_trunc,
jsonb_object_agg(metric_name, avg_value) as values
FROM metrics
GROUP BY 1
)
SELECT
timeseries.date_trunc,
json_metrics.values as values
FROM timeseries
LEFT JOIN json_metrics ON timeseries.date_trunc = json_metrics.date_trunc
ORDER BY 1
`);
return output.map((row) => ({
...row,
values: row.values
? Object.entries(row.values).map(([label, value]) => ({
label: "avg_" + label,
value,
}))
: [],
ts: row.date_trunc.getTime(),
}));
}),
});
@@ -1,108 +0,0 @@
// Langfuse Cloud only
import { Button } from "@/src/components/ui/button";
import { env } from "@/src/env.mjs";
import { api } from "@/src/utils/api";
import { Card, Flex, MarkerBar, Metric, Text } from "@tremor/react";
import Link from "next/link";
import { PricingPage } from "@/src/features/pricing-page/PricingPage";
import {
Dialog,
DialogContent,
DialogHeader,
DialogTrigger,
} from "@/src/components/ui/dialog";
import Header from "@/src/components/layouts/header";
import { usePostHog } from "posthog-js/react";
export const ProjectUsageChart: React.FC<{ projectId: string }> = ({
projectId,
}) => {
const usage = api.usageMetering.currentMonth.useQuery({
projectId,
});
const posthog = usePostHog();
const project = api.projects.byId.useQuery({ projectId });
const planLimit =
project.data?.cloudConfig?.monthlyObservationLimit ?? 100_000;
const plan = project.data?.cloudConfig?.plan ?? "Hobby";
const currentMonth = new Date().toLocaleDateString("en-US", {
month: "short",
});
if (!env.NEXT_PUBLIC_LANGFUSE_CLOUD_REGION) return null;
return (
<div>
<h2 className="mb-5 text-base font-semibold leading-6 text-gray-900">
Usage
</h2>
<Card className="p-4 lg:w-1/2">
{usage.data !== undefined ? (
<>
<Text>Observations / month</Text>
<Metric>{usage.data}</Metric>
<Flex className="mt-4">
<Text>
{`${currentMonth}: ${usage.data} (${(
(usage.data / planLimit) *
100
).toLocaleString(undefined, {
maximumFractionDigits: 2,
})}%)`}
</Text>
<Text>Plan limit: {simplifyNumber(planLimit)}</Text>
</Flex>
<MarkerBar
value={Math.min((usage.data / planLimit) * 100, 100)}
className="mt-3"
/>
</>
) : null}
</Card>
<div className="mt-4 flex flex-row items-center gap-2">
<Dialog
onOpenChange={(open) => {
if (open) {
posthog.capture("project_settings:pricing_dialog_opened");
}
}}
>
<DialogTrigger asChild>
<Button variant="secondary">Change plans</Button>
</DialogTrigger>
<DialogContent className="max-w-3xl">
<DialogHeader>
<Header
title="Select plan"
level="h3"
actionButtons={
<Button variant="secondary" asChild>
<Link href="https://langfuse.com/pricing">
Pricing page
</Link>
</Button>
}
/>
</DialogHeader>
<p>
All plans offer a 7-day free trial. For more information about the
plans, please visit our pricing page or reach out to us via the
chat.
</p>
<PricingPage className="mb-5 mt-10 " />
</DialogContent>
</Dialog>
<div className="inline-block text-sm text-gray-500">
Currently: {plan}
</div>
</div>
</div>
);
};
function simplifyNumber(num: number) {
if (num >= 1000000) return num / 1000000 + "m";
if (num >= 1000) return num / 1000 + "k";
return num.toString();
}
-35
View File
@@ -1,35 +0,0 @@
// Middleware to block requests from certain IPs on Langfuse Cloud
// Not included in the self-host build, removed in Dockerfile
// import { type NextApiRequest } from "next";
// import { type NextRequest } from "next/server";
// import { get } from "@vercel/edge-config";
// export async function middleware(req: NextRequest) {
// try {
// if (process.env.NEXT_PUBLIC_LANGFUSE_CLOUD_REGION !== undefined) {
// const config = await get("blockedIps");
// const blockedIps = Array.isArray(config) ? config : [config];
// const ip = getIP(req);
// if (ip && blockedIps.includes(ip)) {
// console.log("Blocked request by ip: ", ip);
// return new Response("Access denied", { status: 403 });
// }
// }
// return;
// } catch (e) {
// console.error("Server side error in middleware: ", e);
// return new Response("Internal server error", { status: 500 });
// }
// }
// export default function getIP(request: Request | NextApiRequest) {
// const xff =
// request instanceof Request
// ? request.headers.get("x-forwarded-for")
// : request.headers["x-forwarded-for"];
// return xff ? (Array.isArray(xff) ? xff[0] : xff.split(",")[0]) : "127.0.0.1";
// }
-206
View File
@@ -1,206 +0,0 @@
import { type NextApiRequest, type NextApiResponse } from "next";
import { z } from "zod";
import { cors, runMiddleware } from "@/src/features/public-api/server/cors";
import { prisma } from "@/src/server/db";
import { verifyAuthHeaderAndReturnScope } from "@/src/features/public-api/server/apiAuth";
import { Prisma } from "@prisma/client";
import { paginationZod } from "@/src/utils/zod";
const GetUsageSchema = z.object({
...paginationZod,
group_by: z.enum(["trace_name"]).nullish(),
trace_name: z.string().nullish(),
});
export default async function handler(
req: NextApiRequest,
res: NextApiResponse,
) {
await runMiddleware(req, res, cors);
// CHECK AUTH
const authCheck = await verifyAuthHeaderAndReturnScope(
req.headers.authorization,
);
if (!authCheck.validKey)
return res.status(401).json({
message: authCheck.error,
});
// END CHECK AUTH
try {
if (req.method === "GET") {
if (authCheck.scope.accessLevel !== "all") {
return res.status(401).json({
message:
"Access denied - need to use basic auth with secret key to GET scores",
});
}
const obj = GetUsageSchema.parse(req.query); // uses query and not body
const traceNameCondition = obj.trace_name
? Prisma.sql`AND t.name = ${obj.trace_name}`
: Prisma.empty;
if (obj.group_by === undefined) {
const usage = await prisma.$queryRaw`
WITH model_usage AS (
SELECT
DATE_TRUNC('DAY',
o.start_time) observation_day,
o.model,
SUM(o.prompt_tokens) prompt_tokens,
SUM(o.completion_tokens) completion_tokens,
SUM(o.total_tokens) total_tokens
FROM
traces t
LEFT JOIN observations o ON o.trace_id = t.id
WHERE o.start_time IS NOT NULL
AND o.project_id = ${authCheck.scope.projectId}
AND t.project_id = ${authCheck.scope.projectId}
${traceNameCondition}
GROUP BY 1,2
order by 1,2
),
daily_usage AS (
SELECT
observation_day,
json_agg(json_build_object('model',
model,
'prompt_tokens',
prompt_tokens,
'completion_tokens',
completion_tokens,
'total_tokens',
total_tokens)) daily_usage_json
FROM model_usage
group by 1
)
SELECT
observation_day "date",
daily_usage_json usage
FROM daily_usage
ORDER BY 1 desc
LIMIT ${obj.limit} OFFSET ${(obj.page - 1) * obj.limit}
`;
const totalItemsRes = await prisma.$queryRaw<{ count: bigint }[]>`
SELECT
count(DISTINCT DATE_TRUNC('DAY', observations.start_time))
FROM
observations
JOIN traces ON observations.trace_id = traces.id
WHERE traces.project_id = ${authCheck.scope.projectId}
`;
const totalItems =
totalItemsRes[0] !== undefined ? Number(totalItemsRes[0].count) : 0;
return res.status(200).json({
data: usage,
meta: {
page: obj.page,
limit: obj.limit,
totalItems,
totalPages: Math.ceil(totalItems / obj.limit),
},
});
} else if (obj.group_by === "trace_name") {
const usage = await prisma.$queryRaw`
WITH model_usage AS (
SELECT
t."name" trace_name,
DATE_TRUNC('DAY',
o.start_time) observation_day,
o.model,
SUM(o.prompt_tokens) prompt_tokens,
SUM(o.completion_tokens) completion_tokens,
SUM(o.total_tokens) total_tokens
FROM
traces t
LEFT JOIN observations o ON o.trace_id = t.id
WHERE o.start_time IS NOT NULL
AND t.project_id = ${authCheck.scope.projectId}
AND o.project_id = ${authCheck.scope.projectId}
${traceNameCondition}
GROUP BY 1,2,3
order by 1,2,3
),
daily_usage AS (
SELECT
trace_name,
observation_day,
json_agg(json_build_object('model',
model,
'prompt_tokens',
prompt_tokens,
'completion_tokens',
completion_tokens,
'total_tokens',
total_tokens)) daily_usage_json
FROM model_usage
WHERE prompt_tokens > 0
OR completion_tokens > 0
OR total_tokens > 0
group by 1,2
order by 1,2 desc
),
all_trace_names AS (
SELECT t."name" trace_name
FROM traces t
WHERE t.project_id = ${authCheck.scope.projectId}
${traceNameCondition}
GROUP BY 1
)
SELECT
all_trace_names.trace_name,
json_agg(json_build_object(
'date',
observation_day,
'usage',
daily_usage_json
)) metrics
FROM all_trace_names
LEFT JOIN daily_usage ON all_trace_names.trace_name = daily_usage.trace_name
group by 1
ORDER BY 1
LIMIT ${obj.limit} OFFSET ${(obj.page - 1) * obj.limit}
`;
const totalItemsRes = await prisma.$queryRaw<{ count: bigint }[]>`
SELECT
count(DISTINCT CASE WHEN "name" IS NULL THEN 'COUNT_NULL' ELSE "name" END)
FROM
traces
WHERE project_id = ${authCheck.scope.projectId}
`;
const totalItems =
totalItemsRes[0] !== undefined ? Number(totalItemsRes[0].count) : 0;
return res.status(200).json({
data: usage,
meta: {
page: obj.page,
limit: obj.limit,
totalItems,
totalPages: Math.ceil(totalItems / obj.limit),
},
});
} else {
return res.status(400).json({
message: "Invalid group_by value",
});
}
} else {
console.error(req.method, req.body);
return res.status(405).json({ message: "Method not allowed" });
}
} catch (error: unknown) {
console.error(error);
const errorMessage =
error instanceof Error ? error.message : "An unknown error occurred";
res.status(400).json({
message: "Invalid request data",
error: errorMessage,
});
}
}
-573
View File
@@ -1,573 +0,0 @@
import { z } from "zod";
import {
createTRPCRouter,
protectedProjectProcedure,
} from "@/src/server/api/trpc";
import { Prisma, type ObservationView } from "@prisma/client";
import { jsonSchema, paginationZod } from "@/src/utils/zod";
import { singleFilter } from "@/src/server/api/interfaces/filters";
import {
datetimeFilterToPrismaSql,
filterToPrismaSql,
} from "@/src/features/filters/server/filterToPrisma";
import {
type ObservationOptions,
observationsTableCols,
} from "@/src/server/api/definitions/observationsTable";
import { usdFormatter } from "@/src/utils/numbers";
import { env } from "@/src/env.mjs";
import {
S3Client,
PutObjectCommand,
GetObjectCommand,
} from "@aws-sdk/client-s3";
import { getSignedUrl } from "@aws-sdk/s3-request-presigner";
import {
exportFileFormats,
exportOptions,
} from "@/src/server/api/interfaces/exportTypes";
import { orderBy } from "@/src/server/api/interfaces/orderBy";
import { orderByToPrismaSql } from "@/src/features/orderBy/server/orderByToPrisma";
const GenerationTableOptions = z.object({
projectId: z.string(), // Required for protectedProjectProcedure
filter: z.array(singleFilter),
searchQuery: z.string().nullable(),
orderBy: orderBy,
});
const ListInputs = GenerationTableOptions.extend({
...paginationZod,
});
// extend generationfilteroptions with export options
const ExportInputs = GenerationTableOptions.extend({
fileFormat: z.enum(exportFileFormats),
});
export const generationsRouter = createTRPCRouter({
all: protectedProjectProcedure
.input(ListInputs)
.query(async ({ input, ctx }) => {
// ATTENTION: When making changes to this query, make sure to also update the export query
const searchCondition = input.searchQuery
? Prisma.sql`AND (
o."id" ILIKE ${`%${input.searchQuery}%`} OR
o."name" ILIKE ${`%${input.searchQuery}%`} OR
o."model" ILIKE ${`%${input.searchQuery}%`} OR
t."name" ILIKE ${`%${input.searchQuery}%`}
)`
: Prisma.empty;
const filterCondition = filterToPrismaSql(
input.filter,
observationsTableCols,
);
const orderByCondition = orderByToPrismaSql(
input.orderBy,
observationsTableCols,
);
// to improve query performance, add timeseries filter to observation queries as well
const startTimeFilter = input.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;
const generations = await ctx.prisma.$queryRaw<
Array<
ObservationView & {
traceId: string;
traceName: string;
latency: number | null;
}
>
>(
Prisma.sql`
WITH observations_with_latency AS (
SELECT
o.*,
CASE WHEN o.end_time IS NULL THEN NULL ELSE (EXTRACT(EPOCH FROM o."end_time") - EXTRACT(EPOCH FROM o."start_time"))::double precision END AS "latency"
FROM observations_view o
WHERE o.type = 'GENERATION'
AND o.project_id = ${input.projectId}
${datetimeFilter}
),
-- used for filtering
scores_avg AS (
SELECT
trace_id,
observation_id,
jsonb_object_agg(name::text, avg_value::double precision) AS scores_avg
FROM (
SELECT
trace_id,
observation_id,
name,
avg(value) avg_value
FROM
scores
GROUP BY
1,
2,
3
ORDER BY
1) tmp
GROUP BY
1, 2
)
SELECT
o.id,
o.name,
o.model,
o.start_time as "startTime",
o.end_time as "endTime",
o.latency,
o.input,
o.output,
o.metadata,
o.trace_id as "traceId",
t.name as "traceName",
o.completion_start_time as "completionStartTime",
o.prompt_tokens as "promptTokens",
o.completion_tokens as "completionTokens",
o.total_tokens as "totalTokens",
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"
FROM observations_with_latency o
JOIN traces t ON t.id = o.trace_id
LEFT JOIN scores_avg AS s_avg ON s_avg.trace_id = t.id and s_avg.observation_id = o.id
WHERE
t.project_id = ${input.projectId}
${searchCondition}
${filterCondition}
${orderByCondition}
LIMIT ${input.limit}
OFFSET ${input.page * input.limit}
`,
);
const totalGenerations = await ctx.prisma.$queryRaw<
Array<{ count: bigint }>
>(
Prisma.sql`
WITH observations_with_latency AS (
SELECT
o.*,
CASE WHEN o.end_time IS NULL THEN NULL ELSE (EXTRACT(EPOCH FROM o."end_time") - EXTRACT(EPOCH FROM o."start_time"))::double precision END AS "latency"
FROM observations_view o
WHERE o.type = 'GENERATION'
AND o.project_id = ${input.projectId}
${datetimeFilter}
),
-- used for filtering
scores_avg AS (
SELECT
trace_id,
observation_id,
jsonb_object_agg(name::text, avg_value::double precision) AS scores_avg
FROM (
SELECT
trace_id,
observation_id,
name,
avg(value) avg_value
FROM
scores
GROUP BY
1,
2,
3
ORDER BY
1) tmp
GROUP BY
1, 2
)
SELECT
count(*)
FROM observations_with_latency o
JOIN traces t ON t.id = o.trace_id
LEFT JOIN scores_avg AS s_avg ON s_avg.trace_id = t.id and s_avg.observation_id = o.id
WHERE
t.project_id = ${input.projectId}
${searchCondition}
${filterCondition}
`,
);
const scores = await ctx.prisma.score.findMany({
where: {
trace: {
projectId: input.projectId,
},
observationId: {
in: generations.map((gen) => gen.id),
},
},
});
const count = totalGenerations[0]?.count;
return {
totalCount: count ? Number(count) : undefined,
generations: generations.map((generation) => {
const filteredScores = scores.filter(
(s) => s.observationId === generation.id,
);
return {
...generation,
scores: filteredScores,
};
}),
};
}),
export: protectedProjectProcedure
.input(ExportInputs)
.query(async ({ input, ctx }) => {
// ATTENTION: When making changes to this query, make sure to also update the all query
const searchCondition = input.searchQuery
? Prisma.sql`AND (
o."id" ILIKE ${`%${input.searchQuery}%`} OR
o."name" ILIKE ${`%${input.searchQuery}%`} OR
o."model" ILIKE ${`%${input.searchQuery}%`} OR
t."name" ILIKE ${`%${input.searchQuery}%`}
)`
: Prisma.empty;
const filterCondition = filterToPrismaSql(
input.filter,
observationsTableCols,
);
const orderByCondition = orderByToPrismaSql(
input.orderBy,
observationsTableCols,
);
// to improve query performance, add timeseries filter to observation queries as well
const startTimeFilter = input.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;
const generations = await ctx.prisma.$queryRaw<
Array<
ObservationView & {
traceId: string;
traceName: string;
latency: number | null;
}
>
>(
Prisma.sql`
WITH observations_with_latency AS (
SELECT
o.*,
CASE WHEN o.end_time IS NULL THEN NULL ELSE (EXTRACT(EPOCH FROM o."end_time") - EXTRACT(EPOCH FROM o."start_time"))::double precision END AS "latency"
FROM observations_view o
WHERE o.type = 'GENERATION'
AND o.project_id = ${input.projectId}
${datetimeFilter}
),
-- used for filtering
scores_avg AS (
SELECT
trace_id,
observation_id,
jsonb_object_agg(name::text, avg_value::double precision) AS scores_avg
FROM (
SELECT
trace_id,
observation_id,
name,
avg(value) avg_value
FROM
scores
GROUP BY
1,
2,
3
ORDER BY
1) tmp
GROUP BY
1, 2
)
SELECT
o.id,
o.name,
o.model,
o.start_time as "startTime",
o.end_time as "endTime",
o.latency,
o.input,
o.output,
o.metadata,
o.trace_id as "traceId",
t.name as "traceName",
o.completion_start_time as "completionStartTime",
o.prompt_tokens as "promptTokens",
o.completion_tokens as "completionTokens",
o.total_tokens as "totalTokens",
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"
FROM observations_with_latency o
JOIN traces t ON t.id = o.trace_id
LEFT JOIN scores_avg AS s_avg ON s_avg.trace_id = t.id and s_avg.observation_id = o.id
WHERE
t.project_id = ${input.projectId}
${searchCondition}
${filterCondition}
${orderByCondition}
`,
);
let output: string = "";
// create file
switch (input.fileFormat) {
case "CSV":
output = [
[
"traceId",
"name",
"model",
"startTime",
"endTime",
"cost",
"prompt",
"completion",
"metadata",
],
]
.concat(
generations.map((generation) =>
[
generation.traceId,
generation.name ?? "",
generation.model ?? "",
generation.startTime.toISOString(),
generation.endTime?.toISOString() ?? "",
generation.calculatedTotalCost
? usdFormatter(
generation.calculatedTotalCost.toNumber(),
2,
8,
)
: "",
JSON.stringify(generation.input),
JSON.stringify(generation.output),
JSON.stringify(generation.metadata),
].map((field) => {
const str = typeof field === "string" ? field : String(field);
return `"${str.replace(/"/g, '""')}"`;
}),
),
)
.map((row) => row.join(","))
.join("\n");
break;
case "JSON":
output = JSON.stringify(generations);
break;
case "OPENAI-JSONL":
const inputSchemaOpenAI = z.array(
z.object({
role: z.enum(["system", "user", "assistant"]),
content: z.string(),
}),
);
const outputSchema = z
.object({
completion: jsonSchema,
})
.or(jsonSchema);
output = generations
.map((generation) => ({
parsedInput: inputSchemaOpenAI.safeParse(generation.input),
parsedOutput: outputSchema.safeParse(generation.output),
}))
.filter((generation) => generation.parsedInput.success)
.map((generation) =>
generation.parsedInput.success // check for typescript validation, is always true due to previous filter
? generation.parsedInput.data.concat(
generation.parsedOutput.success
? [
{
role: "assistant",
content:
typeof generation.parsedOutput.data ===
"object" &&
"completion" in generation.parsedOutput.data
? JSON.stringify(
generation.parsedOutput.data.completion,
)
: JSON.stringify(generation.parsedOutput.data),
},
]
: [],
)
: [],
)
// to jsonl
.map((row) => JSON.stringify(row))
.join("\n");
break;
default:
throw new Error("Invalid export file format");
}
const fileName = `lf-export-${
input.projectId
}-${new Date().toISOString()}.${
exportOptions[input.fileFormat].extension
}`;
if (
env.S3_BUCKET_NAME &&
env.S3_ACCESS_KEY_ID &&
env.S3_SECRET_ACCESS_KEY &&
env.S3_ENDPOINT &&
env.S3_REGION
) {
const client = new S3Client({
credentials: {
accessKeyId: env.S3_ACCESS_KEY_ID,
secretAccessKey: env.S3_SECRET_ACCESS_KEY,
},
endpoint: env.S3_ENDPOINT,
region: env.S3_REGION,
});
await client.send(
new PutObjectCommand({
Bucket: env.S3_BUCKET_NAME,
Key: fileName,
Body: output,
ContentType: exportOptions[input.fileFormat].fileType,
}),
);
const signedUrl = await getSignedUrl(
client,
new GetObjectCommand({
Bucket: env.S3_BUCKET_NAME,
Key: fileName,
ResponseContentDisposition: `attachment; filename="${fileName}"`,
}),
{
expiresIn: 60 * 60, // in 1 hour, signed url will expire
},
);
return {
type: "s3",
url: signedUrl,
fileName,
} as const;
} else {
return {
type: "data",
data: output,
fileName,
} as const;
}
}),
filterOptions: protectedProjectProcedure
.input(z.object({ projectId: z.string() }))
.query(async ({ input, ctx }) => {
const queryFilter = {
projectId: input.projectId,
type: "GENERATION",
} as const;
const scores = await ctx.prisma.score.groupBy({
where: {
observation: {
projectId: input.projectId,
},
},
by: ["name"],
});
const model = await ctx.prisma.observation.groupBy({
by: ["model"],
where: queryFilter,
_count: { _all: true },
});
const name = await ctx.prisma.observation.groupBy({
by: ["name"],
where: queryFilter,
_count: { _all: true },
});
const traceName = await ctx.prisma.$queryRaw<
Array<{
traceName: string | null;
count: number;
}>
>(Prisma.sql`
SELECT
t.name "traceName",
count(*)::int AS count
FROM traces t
JOIN observations o ON o.trace_id = t.id
WHERE o.type = 'GENERATION'
AND o.project_id = ${input.projectId}
AND t.project_id = ${input.projectId}
GROUP BY 1
`);
// typecheck filter options, needs to include all columns with options
const res: ObservationOptions = {
model: model
.filter((i) => i.model !== null)
.map((i) => ({
value: i.model as string,
count: i._count._all,
})),
name: name
.filter((i) => i.name !== null)
.map((i) => ({
value: i.name as string,
count: i._count._all,
})),
traceName: traceName
.filter((i) => i.traceName !== null)
.map((i) => ({
value: i.traceName as string,
count: i.count,
})),
scores_avg: scores.map((score) => score.name),
};
return res;
}),
});
-16
View File
@@ -1,16 +0,0 @@
import { PrismaClient } from "@prisma/client";
import { env } from "@/src/env.mjs";
const globalForPrisma = globalThis as unknown as {
prisma: PrismaClient | undefined;
};
export const prisma =
globalForPrisma.prisma ??
new PrismaClient({
log:
env.NODE_ENV === "development" ? ["query", "error", "warn"] : ["error"],
});
if (env.NODE_ENV !== "production") globalForPrisma.prisma = prisma;
-8
View File
@@ -1,8 +0,0 @@
export class ResourceNotFoundError extends Error {
id: string;
constructor(type: string, id: string) {
super(`${type} with ${id} not found}`);
this.name = "ResourceNotFoundError";
this.id = id;
}
}
+1 -1
View File
@@ -44,4 +44,4 @@ const config = {
},
};
module.exports = config;
module.exports = config;
+55
View File
@@ -0,0 +1,55 @@
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
# dependencies
/node_modules
/.pnp
.pnp.js
# testing
/coverage
# database
/prisma/db.sqlite
/prisma/db.sqlite-journal
# next.js
/.next/
/out/
next-env.d.ts
# production
/build
# idea
.idea
# misc
.DS_Store
*.pem
# debug
npm-debug.log*
yarn-debug.log*
yarn-error.log*
.pnpm-debug.log*
# local env files
# do not commit any .env files to git, except for the .env.example file. https://create.t3.gg/en/usage/env-variables#using-environment-variables
.env
.env.local
.env*.local
# vercel
.vercel
# typescript
*.tsbuildinfo
/generated/typescript-server
# openapi spec that is copied during build
/public/openapi*.yml
# vscode
.devcontainer
+5 -16
View File
@@ -1,19 +1,11 @@
# Base image
FROM node:20-alpine AS base
ARG DATABASE_URL
ARG NEXTAUTH_SECRET
ARG NEXTAUTH_URL
ARG SALT
# It's important to update the index before installing packages to ensure you're getting the latest versions.
# Check https://github.com/nodejs/docker-node/tree/b4117f9333da4138b03a546ec926ef50a31506c3#nodealpine to understand why libc6-compat might be needed.
RUN apk update && apk upgrade --no-cache libcrypto3 libssl3 libc6-compat
FROM base AS deps
ARG DATABASE_URL
ARG NEXTAUTH_SECRET
ARG NEXTAUTH_URL
ARG SALT
WORKDIR /app
@@ -29,10 +21,6 @@ RUN \
# Rebuild the source code only when needed
FROM base AS builder
ARG DATABASE_URL
ARG NEXTAUTH_SECRET
ARG NEXTAUTH_URL
ARG SALT
WORKDIR /app
COPY --from=deps /app/node_modules ./node_modules
@@ -46,6 +34,9 @@ RUN rm -f ./src/middleware.ts
# Uncomment the following line in case you want to disable telemetry during the build.
ENV NEXT_TELEMETRY_DISABLED 1
# Disable validation of environment variables during build
ENV DOCKER_BUILD 1
# Generate prisma client
RUN npx prisma generate
@@ -54,10 +45,6 @@ RUN npm run build
# Production image, copy all the files and run next
FROM base AS runner
ARG DATABASE_URL
ARG NEXTAUTH_SECRET
ARG NEXTAUTH_URL
ARG SALT
RUN apk add --no-cache dumb-init
@@ -66,6 +53,8 @@ WORKDIR /app
ENV NODE_ENV production
# Uncomment the following line in case you want to disable telemetry during runtime.
ENV NEXT_TELEMETRY_DISABLED 1
# Needed to re-enable validation of environment variables during runtime
ENV DOCKER_BUILD 0
RUN addgroup --system --gid 1001 nodejs
RUN adduser --system --uid 1001 nextjs
View File
+104
View File
@@ -0,0 +1,104 @@
openapi: 3.0.1
info:
title: langfuse
version: ''
paths:
/api/public/scores:
post:
description: Add a score to the database, upserts on id
operationId: score_create
tags:
- Score
parameters: []
responses:
'200':
description: ''
content:
application/json:
schema:
$ref: '#/components/schemas/Score'
'400':
description: ''
content:
application/json:
schema:
type: string
'401':
description: ''
content:
application/json:
schema:
type: string
'403':
description: ''
content:
application/json:
schema:
type: string
'405':
description: ''
content:
application/json:
schema:
type: string
security:
- BearerAuth: []
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/CreateScoreRequest'
components:
schemas:
CreateScoreRequest:
title: CreateScoreRequest
type: object
properties:
id:
type: string
traceId:
type: string
name:
type: string
value:
type: number
format: double
observationId:
type: string
comment:
type: string
required:
- traceId
- name
- value
Score:
title: Score
type: object
properties:
id:
type: string
traceId:
type: string
name:
type: string
value:
type: number
format: double
observationId:
type: string
timestamp:
type: string
format: date-time
comment:
type: string
required:
- id
- traceId
- name
- value
- timestamp
securitySchemes:
BearerAuth:
type: http
scheme: bearer
File diff suppressed because it is too large Load Diff
+19977
View File
File diff suppressed because it is too large Load Diff
+177
View File
@@ -0,0 +1,177 @@
{
"name": "langfuse-core",
"version": "2.7.1",
"private": true,
"scripts": {
"prebuild": "cp generated/openapi-client/openapi.yml public/openapi-client.yml && cp generated/openapi-server/openapi.yml public/openapi-server.yml",
"build": "next build",
"dev": "dotenv -e ../.env -- next dev",
"dx": "dotenv -e ../.env -- npm i && npm run db:reset && npm run db:seed:examples && npm run dev",
"postinstall": "dotenv -e ../.env -- prisma generate",
"lint": "dotenv -e ../.env -- next lint",
"lint:fix": "dotenv -e ../.env -- next lint --fix",
"prettier": "prettier --write ./src *.{ts,js}",
"clean": "rm -rf node_modules",
"start": "next start",
"test": "dotenv -e ../.env -- jest --runInBand",
"test:watch": "dotenv -e ../.env -- jest --watch --runInBand",
"test:e2e": "dotenv -e ../.env -- playwright test",
"db:migrate": "DISABLE_ERD=false dotenv -e ../.env -- npx prisma migrate dev",
"db:reset": "dotenv -e ../.env npx -- prisma migrate reset",
"db:seed": "dotenv -e ../.env -- npx prisma db seed",
"db:seed:examples": "dotenv -e ../.env -- npx prisma db seed -- --environment examples",
"release": "dotenv -e ../.env -- release-it",
"models:migrate": "dotenv -e ../.env -- tsx scripts/model-match.ts"
},
"prisma": {
"seed": "ts-node -r tsconfig-paths/register -r dotenv/config --compiler-options {\"module\":\"CommonJS\"} prisma/seed.ts"
},
"dependencies": {
"@anthropic-ai/tokenizer": "^0.0.4",
"@aws-sdk/client-s3": "^3.507.0",
"@aws-sdk/lib-storage": "^3.511.0",
"@aws-sdk/s3-request-presigner": "^3.507.0",
"@headlessui/react": "^1.7.18",
"@heroicons/react": "^2.1.1",
"@hookform/resolvers": "^3.3.4",
"@next-auth/prisma-adapter": "^1.0.7",
"@prisma/client": "^5.9.1",
"@radix-ui/react-accordion": "^1.1.2",
"@radix-ui/react-alert-dialog": "^1.0.5",
"@radix-ui/react-avatar": "^1.0.4",
"@radix-ui/react-checkbox": "^1.0.4",
"@radix-ui/react-collapsible": "^1.0.3",
"@radix-ui/react-dialog": "^1.0.5",
"@radix-ui/react-dropdown-menu": "^2.0.6",
"@radix-ui/react-hover-card": "^1.0.7",
"@radix-ui/react-icons": "^1.3.0",
"@radix-ui/react-label": "^2.0.2",
"@radix-ui/react-popover": "^1.0.7",
"@radix-ui/react-scroll-area": "^1.0.5",
"@radix-ui/react-select": "^2.0.0",
"@radix-ui/react-separator": "^1.0.3",
"@radix-ui/react-slider": "^1.1.2",
"@radix-ui/react-slot": "^1.0.2",
"@radix-ui/react-switch": "^1.0.3",
"@radix-ui/react-tabs": "^1.0.4",
"@radix-ui/react-toggle": "^1.0.3",
"@radix-ui/react-tooltip": "^1.0.7",
"@react-email/components": "^0.0.14",
"@react-email/render": "^0.0.12",
"@sentry/nextjs": "^7.101.1",
"@sentry/profiling-node": "^7.101.1",
"@sentry/types": "^7.88.0",
"@t3-oss/env-nextjs": "^0.8.0",
"@tailwindcss/forms": "^0.5.7",
"@tanstack/react-query": "^4.36.1",
"@tanstack/react-table": "^8.11.8",
"@tremor/react": "^3.11.1",
"@trpc/client": "^10.45.0",
"@trpc/next": "^10.45.0",
"@trpc/react-query": "^10.45.0",
"@trpc/server": "^10.45.0",
"bcryptjs": "^2.4.3",
"class-variance-authority": "^0.7.0",
"clsx": "^2.1.0",
"cmdk": "^0.2.1",
"core-js": "^3.36.0",
"cors": "^2.8.5",
"date-fns": "^3.3.1",
"decimal.js": "^10.4.3",
"exponential-backoff": "^3.1.1",
"js-tiktoken": "^1.0.10",
"kysely": "^0.27.2",
"lodash": "^4.17.21",
"lucide-react": "^0.330.0",
"next": "^14.1.0",
"next-auth": "^4.24.6",
"next-query-params": "^5.0.0",
"nodemailer": "^6.9.9",
"posthog-js": "^1.105.9",
"posthog-node": "^3.6.3",
"prisma-kysely": "^1.8.0",
"react": "18.2.0",
"react-day-picker": "^8.10.0",
"react-dom": "18.2.0",
"react-hook-form": "^7.50.1",
"react-icons": "^5.0.1",
"react-responsive": "^9.0.2",
"react18-json-view": "^0.2.7",
"sonner": "^1.4.0",
"superjson": "2.2.1",
"tailwind-merge": "^2.2.1",
"tailwindcss-animate": "^1.0.7",
"use-query-params": "^2.2.1",
"uuid": "^9.0.1",
"zod": "^3.22.4"
},
"devDependencies": {
"@jedmao/location": "^3.0.0",
"@mermaid-js/mermaid-cli": "^10.7.0",
"@playwright/test": "^1.41.2",
"@release-it/bumper": "^6.0.1",
"@testing-library/jest-dom": "^6.4.2",
"@testing-library/react": "^14.2.1",
"@types/bcryptjs": "^2.4.6",
"@types/cors": "^2.8.17",
"@types/eslint": "^8.56.2",
"@types/jest": "^29.5.12",
"@types/lodash": "^4.14.202",
"@types/node": "20.10.5",
"@types/nodemailer": "^6.4.14",
"@types/react": "^18.2.55",
"@types/react-dom": "^18.2.19",
"@types/uuid": "^9.0.8",
"@typescript-eslint/eslint-plugin": "^6.21.0",
"@typescript-eslint/parser": "^6.21.0",
"autoprefixer": "^10.4.17",
"dotenv-cli": "^7.3.0",
"eslint": "^8.56.0",
"eslint-config-next": "^14.1.0",
"jest": "^29.7.0",
"jest-environment-jsdom": "^29.7.0",
"node-mocks-http": "^1.14.1",
"postcss": "^8.4.35",
"prettier": "^3.2.5",
"prettier-plugin-tailwindcss": "^0.5.11",
"prisma": "^5.9.1",
"prisma-erd-generator": "^1.11.2",
"release-it": "^17.0.5",
"tailwindcss": "^3.4.1",
"ts-node": "^10.9.2",
"tsconfig-paths": "^4.2.0",
"tsx": "^4.7.1",
"typescript": "^5.3.3"
},
"ct3aMetadata": {
"initVersion": "7.13.0"
},
"optionalDependencies": {
"crisp-sdk-web": "^1.0.21"
},
"release-it": {
"git": {
"commitMessage": "chore: release v${version}",
"tagName": "v${version}"
},
"github": {
"release": true,
"web": true,
"autoGenerate": true,
"releaseName": "v${version}",
"comments": {
"submit": true,
"issue": ":rocket: _This issue has been resolved in v${version}. See [${releaseName}](${releaseUrl}) for release notes._",
"pr": ":rocket: _This pull request is included in v${version}. See [${releaseName}](${releaseUrl}) for release notes._"
}
},
"plugins": {
"@release-it/bumper": {
"out": {
"file": "./src/constants/VERSION.ts",
"type": "application/typescript"
}
}
}
}
}

Before

Width:  |  Height:  |  Size: 194 KiB

After

Width:  |  Height:  |  Size: 194 KiB

+339
View File
@@ -0,0 +1,339 @@
import type { ColumnType } from "kysely";
export type Generated<T> = T extends ColumnType<infer S, infer I, infer U>
? ColumnType<S, I | undefined, U>
: ColumnType<T, T | undefined, T>;
export type Timestamp = ColumnType<Date, Date | string, Date | string>;
export const MembershipRole = {
OWNER: "OWNER",
ADMIN: "ADMIN",
MEMBER: "MEMBER",
VIEWER: "VIEWER"
} as const;
export type MembershipRole = (typeof MembershipRole)[keyof typeof MembershipRole];
export const ObservationType = {
SPAN: "SPAN",
EVENT: "EVENT",
GENERATION: "GENERATION"
} as const;
export type ObservationType = (typeof ObservationType)[keyof typeof ObservationType];
export const ObservationLevel = {
DEBUG: "DEBUG",
DEFAULT: "DEFAULT",
WARNING: "WARNING",
ERROR: "ERROR"
} as const;
export type ObservationLevel = (typeof ObservationLevel)[keyof typeof ObservationLevel];
export const PricingUnit = {
PER_1000_TOKENS: "PER_1000_TOKENS",
PER_1000_CHARS: "PER_1000_CHARS"
} as const;
export type PricingUnit = (typeof PricingUnit)[keyof typeof PricingUnit];
export const TokenType = {
PROMPT: "PROMPT",
COMPLETION: "COMPLETION",
TOTAL: "TOTAL"
} as const;
export type TokenType = (typeof TokenType)[keyof typeof TokenType];
export const DatasetStatus = {
ACTIVE: "ACTIVE",
ARCHIVED: "ARCHIVED"
} as const;
export type DatasetStatus = (typeof DatasetStatus)[keyof typeof DatasetStatus];
export type Account = {
id: string;
user_id: string;
type: string;
provider: string;
providerAccountId: string;
refresh_token: string | null;
access_token: string | null;
expires_at: number | null;
expires_in: number | null;
ext_expires_in: number | null;
token_type: string | null;
scope: string | null;
id_token: string | null;
session_state: string | null;
};
export type ApiKey = {
id: string;
created_at: Generated<Timestamp>;
note: string | null;
public_key: string;
hashed_secret_key: string;
fast_hashed_secret_key: string | null;
display_secret_key: string;
last_used_at: Timestamp | null;
expires_at: Timestamp | null;
project_id: string;
};
export type AuditLog = {
id: string;
created_at: Generated<Timestamp>;
updated_at: Generated<Timestamp>;
user_id: string;
project_id: string;
user_project_role: MembershipRole;
resource_type: string;
resource_id: string;
action: string;
before: string | null;
after: string | null;
};
export type CronJobs = {
name: string;
last_run: Timestamp | null;
job_started_at: Timestamp | null;
state: string | null;
};
export type Dataset = {
id: string;
name: string;
project_id: string;
created_at: Generated<Timestamp>;
updated_at: Generated<Timestamp>;
};
export type DatasetItem = {
id: string;
status: Generated<DatasetStatus>;
input: unknown;
expected_output: unknown | null;
source_observation_id: string | null;
dataset_id: string;
created_at: Generated<Timestamp>;
updated_at: Generated<Timestamp>;
};
export type DatasetRunItems = {
id: string;
dataset_run_id: string;
dataset_item_id: string;
observation_id: string;
created_at: Generated<Timestamp>;
updated_at: Generated<Timestamp>;
};
export type DatasetRuns = {
id: string;
name: string;
dataset_id: string;
created_at: Generated<Timestamp>;
updated_at: Generated<Timestamp>;
};
export type Events = {
id: string;
created_at: Generated<Timestamp>;
updated_at: Generated<Timestamp>;
project_id: string;
data: unknown;
headers: Generated<unknown>;
url: string | null;
method: string | null;
};
export type Example = {
id: string;
created_at: Generated<Timestamp>;
updated_at: Timestamp;
};
export type Membership = {
project_id: string;
user_id: string;
role: MembershipRole;
created_at: Generated<Timestamp>;
updated_at: Generated<Timestamp>;
};
export type MembershipInvitation = {
id: string;
email: string;
role: MembershipRole;
project_id: string;
sender_id: string | null;
created_at: Generated<Timestamp>;
updated_at: Generated<Timestamp>;
};
export type Model = {
id: string;
created_at: Generated<Timestamp>;
updated_at: Generated<Timestamp>;
project_id: string | null;
model_name: string;
match_pattern: string;
start_date: Timestamp | null;
input_price: string | null;
output_price: string | null;
total_price: string | null;
unit: string;
tokenizer_id: string | null;
tokenizer_config: unknown | null;
};
export type Observation = {
id: string;
trace_id: string | null;
project_id: string;
type: ObservationType;
start_time: Generated<Timestamp>;
end_time: Timestamp | null;
name: string | null;
metadata: unknown | null;
parent_observation_id: string | null;
level: Generated<ObservationLevel>;
status_message: string | null;
version: string | null;
created_at: Generated<Timestamp>;
model: string | null;
internal_model: string | null;
modelParameters: unknown | null;
input: unknown | null;
output: unknown | null;
prompt_tokens: Generated<number>;
completion_tokens: Generated<number>;
total_tokens: Generated<number>;
unit: string | null;
input_cost: string | null;
output_cost: string | null;
total_cost: string | null;
completion_start_time: Timestamp | null;
prompt_id: string | null;
};
export type ObservationView = {
id: string;
trace_id: string | null;
project_id: string;
type: ObservationType;
start_time: Generated<Timestamp>;
end_time: Timestamp | null;
name: string | null;
metadata: unknown | null;
parent_observation_id: string | null;
level: Generated<ObservationLevel>;
status_message: string | null;
version: string | null;
created_at: Generated<Timestamp>;
model: string | null;
modelParameters: unknown | null;
input: unknown | null;
output: unknown | null;
prompt_tokens: Generated<number>;
completion_tokens: Generated<number>;
total_tokens: Generated<number>;
unit: string | null;
completion_start_time: Timestamp | null;
prompt_id: string | null;
model_id: string | null;
input_price: string | null;
output_price: string | null;
total_price: string | null;
calculated_input_cost: string | null;
calculated_output_cost: string | null;
calculated_total_cost: string | null;
latency: string | null;
};
export type Pricing = {
id: string;
model_name: string;
pricing_unit: Generated<PricingUnit>;
price: string;
currency: Generated<string>;
token_type: TokenType;
};
export type Project = {
id: string;
created_at: Generated<Timestamp>;
updated_at: Generated<Timestamp>;
name: string;
cloud_config: unknown | null;
};
export type Prompt = {
id: string;
created_at: Generated<Timestamp>;
updated_at: Generated<Timestamp>;
project_id: string;
created_by: string;
prompt: string;
name: string;
version: number;
is_active: boolean;
config: Generated<unknown>;
};
export type Score = {
id: string;
timestamp: Generated<Timestamp>;
name: string;
value: number;
comment: string | null;
trace_id: string;
observation_id: string | null;
};
export type Session = {
id: string;
session_token: string;
user_id: string;
expires: Timestamp;
};
export type Trace = {
id: string;
external_id: string | null;
timestamp: Generated<Timestamp>;
name: string | null;
user_id: string | null;
metadata: unknown | null;
release: string | null;
version: string | null;
project_id: string;
public: Generated<boolean>;
bookmarked: Generated<boolean>;
tags: Generated<string[]>;
input: unknown | null;
output: unknown | null;
session_id: string | null;
};
export type TraceSession = {
id: string;
created_at: Generated<Timestamp>;
updated_at: Generated<Timestamp>;
project_id: string;
bookmarked: Generated<boolean>;
public: Generated<boolean>;
};
export type User = {
id: string;
name: string | null;
email: string | null;
email_verified: Timestamp | null;
password: string | null;
image: string | null;
admin: Generated<boolean>;
created_at: Generated<Timestamp>;
updated_at: Generated<Timestamp>;
feature_flags: Generated<string[]>;
};
export type VerificationToken = {
identifier: string;
token: string;
expires: Timestamp;
};
export type DB = {
Account: Account;
api_keys: ApiKey;
audit_logs: AuditLog;
cron_jobs: CronJobs;
dataset_items: DatasetItem;
dataset_run_items: DatasetRunItems;
dataset_runs: DatasetRuns;
datasets: Dataset;
events: Events;
Example: Example;
membership_invitations: MembershipInvitation;
memberships: Membership;
models: Model;
observations: Observation;
observations_view: ObservationView;
pricings: Pricing;
projects: Project;
prompts: Prompt;
scores: Score;
Session: Session;
trace_sessions: TraceSession;
traces: Trace;
users: User;
verification_tokens: VerificationToken;
};

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