Compare commits

...
172 Commits
Author SHA1 Message Date
Marc Klingen af789fb3ac chore: release v2.11.2
CI/CD / tests (18) (push) Failing after 58s
CI/CD / tests (20) (push) Failing after 49s
release.yml / release (push) Failing after 2s
CI/CD / e2e-tests (push) Failing after 47s
CI/CD / test-docker-build (push) Successful in 4m8s
CI/CD / all-ci-passed (push) Canceled after 0s
CI/CD / push-docker-image (push) Canceled after 0s
CI/CD / lint (push) Failing after 14m43s
2024-03-16 19:43:51 +01:00
Marc Klingen 7fcf9e98ae chore: add LANGFUSE_ENABLE_EXPERIMENTAL_FEATURES to env prod example 2024-03-15 16:21:44 +01:00
Marc Klingen 45c7a95a9c chore(ui): always display hostname 2024-03-14 19:33:10 +01:00
Marc KlingenandGitHub 75009cf83d feat(sso): toggle account linking via AUTH_GOOGLE_ALLOW_ACCOUNT_LINKING and AUTH_GITHUB_ALLOW_ACCOUNT_LINKING (#1416) 2024-03-14 17:02:14 +00:00
Max Deichmann 70642da8ab chore: release v2.11.1
CI/CD / push-docker-image (push) Blocked by required conditions
CI/CD / tests (18) (push) Failing after 1m6s
CI/CD / e2e-tests (push) Failing after 43s
release.yml / release (push) Failing after 2s
CI/CD / tests (20) (push) Failing after 55s
CI/CD / test-docker-build (push) Successful in 2m55s
CI/CD / lint (push) Successful in 4m23s
CI/CD / all-ci-passed (push) Waiting to run
2024-03-14 10:41:53 +01:00
Max DeichmannandGitHub 6754733aac feat: add haiku model (#1414)
push
2024-03-14 09:41:07 +00:00
Richard KrümmelandGitHub e45bd75afa feat(traces): add level to table (#1359) 2024-03-13 16:39:01 +01:00
Marc KlingenandGitHub 9b271f0b78 chore: no default score source in db (#1403) 2024-03-12 20:02:26 +00:00
Marc KlingenandGitHub e98565ee9c feat: add score source (#1365) 2024-03-12 19:27:51 +01:00
Marc KlingenandGitHub 62d1f31527 docs: update readme 2024-03-12 18:25:59 +01:00
Max DeichmannandGitHub c1190d70da refactor: add nuke command (#1400) 2024-03-12 17:00:42 +00:00
Max DeichmannandGitHub bf9badb711 fix: Add additional projectid condition (#1393) 2024-03-12 08:20:48 +00:00
8155749b2b feat(prompts): extend prompts table (#1346)
Co-authored-by: Max Deichmann <m.deichmann@tum.de>
2024-03-12 09:15:14 +01:00
dependabot[bot]GitHubdependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
84c0ba6487 chore(deps): bump the patches group in /worker with 3 updates (#1390)
Bumps the patches group in /worker with 3 updates: [fastify](https://github.com/fastify/fastify), [kysely](https://github.com/kysely-org/kysely) and [@types/pg](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/pg).


Updates `fastify` from 4.26.1 to 4.26.2
- [Release notes](https://github.com/fastify/fastify/releases)
- [Commits](https://github.com/fastify/fastify/compare/v4.26.1...v4.26.2)

Updates `kysely` from 0.27.2 to 0.27.3
- [Release notes](https://github.com/kysely-org/kysely/releases)
- [Commits](https://github.com/kysely-org/kysely/compare/0.27.2...0.27.3)

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

---
updated-dependencies:
- dependency-name: fastify
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: patches
- dependency-name: kysely
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: patches
- dependency-name: "@types/pg"
  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-03-12 01:40:29 +01:00
Marc Klingen 6320f48737 chore: release v2.11.0
CI/CD / push-docker-image (push) Blocked by required conditions
CI/CD / tests (18) (push) Failing after 51s
CI/CD / tests (20) (push) Failing after 47s
CI/CD / e2e-tests (push) Failing after 47s
CI/CD / test-docker-build (push) Successful in 2m54s
release.yml / release (push) Failing after 14s
CI/CD / lint (push) Successful in 4m35s
CI/CD / all-ci-passed (push) Waiting to run
2024-03-11 22:44:39 +01:00
Marc KlingenandGitHub fe053405ea feat(ui): extend ChatML message rendering to additional json for tools/functions (#1388)
* feat(ui): extend ChatML message rendering to additional json for tools/functions

* fix typing
2024-03-11 21:29:26 +00:00
Marc KlingenandGitHub 63d1abffb2 fix(ui): score table pagination (#1387) 2024-03-11 20:15:25 +00:00
Marc Klingen 3050ed756f chore: release v2.10.10
CI/CD / push-docker-image (push) Blocked by required conditions
CI/CD / tests (20) (push) Failing after 46s
CI/CD / tests (18) (push) Failing after 58s
CI/CD / e2e-tests (push) Failing after 44s
release.yml / release (push) Failing after 2s
CI/CD / lint (push) Successful in 4m23s
CI/CD / test-docker-build (push) Successful in 7m14s
CI/CD / all-ci-passed (push) Waiting to run
2024-03-11 20:01:01 +01:00
Marc Klingen 6eebf0b49d docs: update readme 2024-03-11 19:58:16 +01:00
Marc KlingenandGitHub bbc57779c3 feat(sso): add env AUTH_AZURE_ALLOW_ACCOUNT_LINKING to trust azuread emails (#1386) 2024-03-11 18:55:10 +00:00
Max Deichmann ccc9161967 chore: release v2.10.9
CI/CD / push-docker-image (push) Blocked by required conditions
CI/CD / e2e-tests (push) Failing after 47s
CI/CD / tests (20) (push) Failing after 1m59s
CI/CD / lint (push) Successful in 4m17s
CI/CD / test-docker-build (push) Successful in 3m51s
release.yml / release (push) Failing after 3s
CI/CD / tests (18) (push) Failing after 11m33s
CI/CD / all-ci-passed (push) Waiting to run
2024-03-11 16:55:39 +01:00
Max DeichmannandGitHub 6df7a2c716 fix: users table pagination (#1383) 2024-03-11 15:51:36 +00:00
Marc KlingenandGitHub 88e4f210d0 feat: add llamaindex copyable snippet to api popup and fix scroll behavior (#1381) 2024-03-09 02:04:39 +00:00
Max Deichmann 011c1452a0 chore: release v2.10.8
CI/CD / push-docker-image (push) Blocked by required conditions
CI/CD / tests (18) (push) Failing after 53s
CI/CD / tests (20) (push) Failing after 49s
CI/CD / e2e-tests (push) Failing after 50s
CI/CD / test-docker-build (push) Successful in 3m9s
release.yml / release (push) Failing after 3s
CI/CD / lint (push) Successful in 4m35s
CI/CD / all-ci-passed (push) Waiting to run
2024-03-08 10:59:06 +01:00
Max DeichmannandGitHub 4a3025dbae fix: fix generations table sorting (#1370) 2024-03-08 09:58:20 +00:00
Marc Klingen 367e8c8a61 chore: release v2.10.7
CI/CD / push-docker-image (push) Blocked by required conditions
CI/CD / tests (18) (push) Failing after 1m6s
CI/CD / tests (20) (push) Failing after 55s
CI/CD / e2e-tests (push) Failing after 56s
CI/CD / test-docker-build (push) Successful in 3m12s
release.yml / release (push) Failing after 3s
CI/CD / lint (push) Successful in 4m14s
CI/CD / all-ci-passed (push) Waiting to run
2024-03-08 00:27:14 +01:00
Max DeichmannandGitHub 84edae6468 refactor: rename dashboard class (#1366) 2024-03-07 19:03:10 +00:00
Max DeichmannandGitHub 87e04a3482 feat: load generations table i/o individually (#1364) 2024-03-07 18:26:41 +00:00
Max DeichmannandGitHub 90abf7fb34 feat: add claude 3 models (#1354) 2024-03-07 17:53:51 +00:00
Marc KlingenandGitHub 027c3791a0 fix: render records type content as chat message (#1362)
fix: also render records `content` as openai chatmessage
2024-03-07 15:20:27 +00:00
4023e7fd4a feat: add cost to dataset run table (#1344)
---------

Co-authored-by: Marc Klingen <git@marcklingen.com>
2024-03-07 15:30:48 +01:00
Marc Klingen 3d178caa17 chore: release v2.10.6
CI/CD / push-docker-image (push) Blocked by required conditions
CI/CD / tests (18) (push) Failing after 58s
CI/CD / tests (20) (push) Failing after 50s
CI/CD / e2e-tests (push) Failing after 48s
CI/CD / test-docker-build (push) Successful in 3m31s
CI/CD / lint (push) Successful in 4m13s
CI/CD / all-ci-passed (push) Waiting to run
release.yml / release (push) Failing after 3s
2024-03-07 15:03:31 +01:00
Hassieb PakzadandGitHub 128b03bec0 fix: allow null values returned for cost values in sessions table (#1360)
fix: allow null values returned from sessions table
2024-03-07 15:02:29 +01:00
Max Deichmann 73889fcc96 chore: release v2.10.5
CI/CD / push-docker-image (push) Blocked by required conditions
CI/CD / tests (20) (push) Failing after 44s
CI/CD / tests (18) (push) Failing after 1m23s
release.yml / release (push) Failing after 3s
CI/CD / e2e-tests (push) Failing after 43s
CI/CD / test-docker-build (push) Successful in 3m43s
CI/CD / lint (push) Successful in 4m21s
CI/CD / all-ci-passed (push) Waiting to run
2024-03-07 10:02:28 +01:00
Max DeichmannandGitHub d88a295f8d fix: only sort supported columns (#1356) 2024-03-07 09:46:46 +01:00
dependabot[bot]GitHubdependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>Max Deichmann
15c5fdc2a9 chore(deps-dev): bump @typescript-eslint/eslint-plugin from 6.21.0 to 7.1.1 in /worker (#1333)
chore(deps-dev): bump @typescript-eslint/eslint-plugin in /worker

Bumps [@typescript-eslint/eslint-plugin](https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/eslint-plugin) from 6.21.0 to 7.1.1.
- [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/v7.1.1/packages/eslint-plugin)

---
updated-dependencies:
- dependency-name: "@typescript-eslint/eslint-plugin"
  dependency-type: direct:development
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Max Deichmann <m.deichmann@tum.de>
2024-03-07 08:02:13 +00:00
Max Deichmann ca9c0e07d1 chore: release v2.10.4
CI/CD / push-docker-image (push) Blocked by required conditions
CI/CD / tests (18) (push) Failing after 54s
CI/CD / tests (20) (push) Failing after 51s
release.yml / release (push) Failing after 2s
CI/CD / e2e-tests (push) Failing after 48s
CI/CD / lint (push) Successful in 4m41s
CI/CD / test-docker-build (push) Successful in 5m44s
CI/CD / all-ci-passed (push) Waiting to run
2024-03-07 08:56:32 +01:00
Max DeichmannandGitHub bf0eab424f fix: fix correct reuse of db connections (#1348) 2024-03-07 08:55:38 +01:00
Marc Klingen 89d38ff6ad chore: release v2.10.3
CI/CD / push-docker-image (push) Blocked by required conditions
CI/CD / tests (18) (push) Failing after 58s
CI/CD / tests (20) (push) Failing after 49s
CI/CD / e2e-tests (push) Failing after 48s
CI/CD / test-docker-build (push) Successful in 2m53s
release.yml / release (push) Failing after 3s
CI/CD / lint (push) Successful in 4m51s
CI/CD / all-ci-passed (push) Waiting to run
2024-03-07 00:52:54 +01:00
Marc KlingenandGitHub 8d7b334521 refactor: remove separate identify() to reduce number of posthog events (#1263) 2024-03-06 23:27:21 +00:00
Marc KlingenandGitHub a374a378a0 fix: dashboard state management (#1349) 2024-03-06 23:23:11 +00:00
Max Deichmann f95a6943a6 chore: release v2.10.2
CI/CD / push-docker-image (push) Blocked by required conditions
CI/CD / test-docker-build (push) Failing after 2m15s
CI/CD / lint (push) Successful in 4m41s
CI/CD / tests (18) (push) Failing after 54s
CI/CD / tests (20) (push) Failing after 1m49s
release.yml / release (push) Failing after 4s
CI/CD / e2e-tests (push) Failing after 1m39s
CI/CD / all-ci-passed (push) Waiting to run
2024-03-06 22:11:10 +01:00
Max DeichmannandGitHub fec89b8c62 perf: remove sessions total latency (#1347) 2024-03-06 20:46:06 +01:00
Max Deichmann a10a6a7ca5 chore: release v2.10.1
CI/CD / push-docker-image (push) Blocked by required conditions
CI/CD / tests (18) (push) Failing after 53s
CI/CD / tests (20) (push) Failing after 48s
CI/CD / e2e-tests (push) Failing after 1m13s
release.yml / release (push) Failing after 13s
CI/CD / test-docker-build (push) Successful in 4m3s
CI/CD / lint (push) Successful in 4m14s
CI/CD / all-ci-passed (push) Waiting to run
2024-03-06 17:12:20 +01:00
Max DeichmannandGitHub 8f32ef935b fix: generations pagination (#1345) 2024-03-06 15:49:21 +00:00
Max DeichmannandGitHub 4f0b309e0d fix: filter users table for null users (#1342) 2024-03-06 15:24:07 +00:00
Max DeichmannandGitHub c424a5578e perf: generations table performance (#1341) 2024-03-06 15:13:02 +00:00
Marc Klingen 2113c684a4 chore: release v2.10.0
CI/CD / push-docker-image (push) Blocked by required conditions
CI/CD / tests (18) (push) Failing after 57s
CI/CD / tests (20) (push) Failing after 49s
CI/CD / e2e-tests (push) Failing after 47s
release.yml / release (push) Failing after 3s
CI/CD / test-docker-build (push) Successful in 3m27s
CI/CD / lint (push) Successful in 6m1s
CI/CD / all-ci-passed (push) Waiting to run
2024-03-06 14:38:25 +01:00
Marc KlingenandGitHub d900056f69 ci: tag docker containers with major version (#1340) 2024-03-06 14:33:49 +01:00
a2930f3f2a feat(generations): extend CSV and JSON export (#1265)
Co-authored-by: Max Deichmann <m.deichmann@tum.de>
2024-03-06 13:24:35 +00:00
Max DeichmannandGitHub 9a3fa2b461 fix: correctly calculate total events (#1339) 2024-03-06 13:10:06 +00:00
Max DeichmannandGitHub 4ac3b13ca6 perf: delayed loading for user metrics (#1338) 2024-03-06 13:00:37 +00:00
Max DeichmannandGitHub 4c32113ec3 perf: user tables (#1329) 2024-03-05 17:23:51 +00:00
Richard KrümmelandGitHub 7f81135e6b feat(traces): extend table (#1278) 2024-03-05 17:11:26 +01:00
Marc KlingenandGitHub 4a362ee10f fix: GET /traces endpoint, typing of params, return empty [] instead of null (#1324) 2024-03-05 15:42:43 +01:00
Richard KrümmelandGitHub 04374dd0b3 feat(sessions): extend table (#1271) 2024-03-05 13:43:52 +00:00
Richard KrümmelandGitHub a69571cedc feat(datasets): rename datasets (#1203) 2024-03-05 14:10:32 +01:00
Max DeichmannandGitHub 53e78ee249 fix: fix indices (#1322) 2024-03-05 13:36:31 +01:00
Max DeichmannandGitHub 64c0e26da6 perf: users table (#1320) 2024-03-05 12:27:18 +01:00
Max DeichmannandGitHub fa52811224 fix: scores table filtering (#1319) 2024-03-05 08:32:08 +00:00
Max DeichmannandGitHub 213d9da88a perf: improve scores table latency (#1315) 2024-03-05 09:25:18 +01:00
Max DeichmannandGitHub 8611afd533 perf: latency chart performance (#1314) 2024-03-04 20:38:29 +00:00
Max DeichmannandGitHub e96a3bbf6e feat: add tooltip to latency tables (#1313) 2024-03-04 19:40:19 +00:00
Max DeichmannandGitHub 60e636e37d feat: add traces, spans, generations latency tables (#1312) 2024-03-04 16:37:57 +00:00
Max DeichmannandGitHub e9e8319099 fix: adjust sentry sampling rate (#1311) 2024-03-04 11:11:12 +00:00
Marc Klingen 8ee0e611d9 chore: release v2.9.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-03-02 04:15:40 +01:00
Marc Klingen b7b9cc8dd1 fix(ui): use Textarea for dataset item edits instead of json editor
Reverts #1171
2024-03-02 04:10:21 +01:00
Marc KlingenandGitHub 21240cb594 chore(prompts): remove beta flag (#1304) 2024-03-01 23:22:07 +00:00
Max DeichmannandGitHub fdda5db1a3 feat: increase scale of dashboard USD numbers (#1301) 2024-03-01 18:42:52 +01:00
Max DeichmannandGitHub 0538f27caf fix: fix empty score names (#1299) 2024-03-01 15:10:30 +01:00
Marc KlingenandGitHub c73bc5a77f fix(ui): AreaChart only for total traces, all others use LineChart (#1297) 2024-03-01 13:22:57 +00:00
Max DeichmannandGitHub 9204c9dc69 perf: show 10 generations in table when i/o is selected (#1294) 2024-03-01 09:53:40 +01:00
Marc Klingen feb935e737 chore: switch to svg icon 2024-03-01 02:00:34 +01:00
Max Deichmann e4a029988f chore: release v2.9.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-29 14:50:13 +01:00
Marc Klingen d8bb7703ae fix(ui): second level headings should be h3 2024-02-29 12:24:55 +01:00
Marc Klingen bd7574411e chore: add npm run dx to root 2024-02-29 12:02:24 +01:00
Marc Klingen 2f55acec7c docs: update readme 2024-02-28 23:15:42 +01:00
SebandGitHub 78207ba274 chore: improve date picker formatting (#1283) 2024-02-28 20:14:16 +01:00
SebandGitHub 2250be58a5 docs: fix link to db schema (#1282) 2024-02-28 20:12:49 +01:00
Max DeichmannandGitHub 338517828c perf: increase sample rate for POST and trpc (#1284) 2024-02-28 19:06:10 +00:00
Max DeichmannandGitHub 2e80d4b475 perf: improve dashboard performance (#1281) 2024-02-28 16:51:38 +01:00
Marc KlingenandGitHub 45c9af76d8 fix(api): add input, output, sessionId, public to GET /traces (#1280) 2024-02-28 13:47:40 +00:00
Max DeichmannandGitHub bc27618c0e perf: improve generations table performance (#1279) 2024-02-28 13:41:33 +00:00
Max DeichmannandGitHub a4c8f0b702 perf: improve traces table performance (#1276) 2024-02-28 13:04:11 +00:00
Max DeichmannandGitHub 787eb90c54 perf: improve observations view and all traces (#1275) 2024-02-28 09:48:20 +00:00
Max DeichmannandGitHub 9b4601debf refactor: add load database setup script (#1273) 2024-02-27 23:43:30 +00:00
Max DeichmannandGitHub fcc0ee922f refactor: add sentry users to API (#1269) 2024-02-27 16:17:37 +01:00
Max DeichmannandGitHub 3271c4abd0 fix: observations index (#1267) 2024-02-27 14:33:56 +01:00
Max DeichmannandGitHub 2dbb9fec87 fix: fix observations index (#1266) 2024-02-27 12:42:32 +01:00
e7438c05b8 feat(generation): extend generations table (#1216)
Co-authored-by: Max Deichmann <m.deichmann@tum.de>
2024-02-27 11:20:27 +00:00
Marc Klingen 45fcf8902b fix(self-host): event mapping 2024-02-27 02:40:54 +01:00
Max DeichmannandGitHub a4bfdb25a5 perf: revert traces router query (#1261)
revert
2024-02-26 22:37:35 +00:00
Max DeichmannandGitHub 462e8a7b8c perf: traces table performance improvements (#1256) 2024-02-26 22:57:53 +01:00
Marc KlingenandGitHub 86edd502b3 feat(ui): add alert that only ChatML generations is exported as JSONL (#1257) 2024-02-26 19:56:41 +00:00
Max DeichmannandGitHub 01a7e679fa fix: correctly return 500 and 400 API errors (#1253) 2024-02-26 12:44:03 +00:00
Marc KlingenandGitHub c6406e1ddb fix(ui): sort/filter sessions by total cost (#1254) 2024-02-26 11:35:20 +00:00
Max DeichmannandGitHub 82d4881dcf fix: improve contributing.md (#1252) 2024-02-26 09:23:51 +00:00
Max DeichmannandGitHub 42d3167591 fix: docs and root level commands (#1251) 2024-02-26 08:52:09 +00:00
Marc KlingenandGitHub 977ac39ec5 docs(api): improve api reference descriptions (#1244) 2024-02-23 19:44:11 +00:00
Marc Klingen e4d8db6a34 chore: release v2.8.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-23 20:23:00 +01:00
Marc KlingenandGitHub a39bbf541d feat(api): add GET /score/[scoreId] (#1242) 2024-02-23 19:20:27 +00:00
Max DeichmannandGitHub eef5218dbd refactor: add worker tests (#1241) 2024-02-23 17:44:54 +00:00
Marc KlingenandGitHub b5e802171b fix(api): deduplicate score and observation ids in GET /traces (#1240) 2024-02-23 17:38:55 +00:00
Max DeichmannandGitHub 96c4f28b5b refactor: linting worker (#1239) 2024-02-23 17:34:16 +00:00
Max DeichmannandGitHub 8ad4a46850 feat: add database connection to worker (#1238) 2024-02-23 16:55:16 +00:00
dependabot[bot]GitHubdependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>Max Deichmann
d8f2d6cf4e chore(deps): bump the prisma group in /web with 2 updates (#1231)
Bumps the prisma group in /web with 2 updates: [@prisma/client](https://github.com/prisma/prisma/tree/HEAD/packages/client) and [prisma](https://github.com/prisma/prisma/tree/HEAD/packages/cli).


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

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

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

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Max Deichmann <m.deichmann@tum.de>
2024-02-23 16:50:10 +00:00
b8639ebb3b feat: use Json editor for dataset item editor (#1171)
Co-authored-by: gitstart-langfuse <gitstart-langfuse@users.noreply.github.com>
Co-authored-by: Max Deichmann <m.deichmann@tum.de>
2024-02-23 14:46:27 +00:00
dependabot[bot]GitHubdependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
8be73970c2 chore(deps): bump the sentry group in /web with 3 updates (#1230)
Bumps the sentry group in /web 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.101.1 to 7.102.1
- [Release notes](https://github.com/getsentry/sentry-javascript/releases)
- [Changelog](https://github.com/getsentry/sentry-javascript/blob/7.102.1/CHANGELOG.md)
- [Commits](https://github.com/getsentry/sentry-javascript/compare/7.101.1...7.102.1)

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

Updates `@sentry/types` from 7.101.1 to 7.102.1
- [Release notes](https://github.com/getsentry/sentry-javascript/releases)
- [Changelog](https://github.com/getsentry/sentry-javascript/blob/7.102.1/CHANGELOG.md)
- [Commits](https://github.com/getsentry/sentry-javascript/compare/7.101.1...7.102.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-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-23 14:34:10 +00:00
dependabot[bot]GitHubdependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>Max Deichmann
e18ab26bb5 chore(deps): bump @aws-sdk/lib-storage from 3.511.0 to 3.515.0 in /web (#1233)
Bumps [@aws-sdk/lib-storage](https://github.com/aws/aws-sdk-js-v3/tree/HEAD/lib/lib-storage) from 3.511.0 to 3.515.0.
- [Release notes](https://github.com/aws/aws-sdk-js-v3/releases)
- [Changelog](https://github.com/aws/aws-sdk-js-v3/blob/main/lib/lib-storage/CHANGELOG.md)
- [Commits](https://github.com/aws/aws-sdk-js-v3/commits/v3.515.0/lib/lib-storage)

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

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Max Deichmann <m.deichmann@tum.de>
2024-02-23 14:28:52 +00:00
dependabot[bot]GitHubdependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>Max Deichmann
095478869d chore(deps-dev): bump release-it from 17.0.5 to 17.1.1 in /web (#1234)
Bumps [release-it](https://github.com/release-it/release-it) from 17.0.5 to 17.1.1.
- [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.5...17.1.1)

---
updated-dependencies:
- dependency-name: release-it
  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>
Co-authored-by: Max Deichmann <m.deichmann@tum.de>
2024-02-23 14:23:04 +00:00
Marc KlingenandGitHub 4db7dab8ae fix(export): messages key in jsonl output format (#1236) 2024-02-23 13:54:31 +00:00
Max DeichmannandGitHub 086d26b041 fix: fix dependabot in new repo structure (#1228) 2024-02-23 09:35:32 +00:00
Max Deichmann 5fd042c7b1 chore: release v2.7.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-23 10:24:30 +01:00
Max DeichmannandGitHub dc37f1a58a feat: add worker container and redis (#1224) 2024-02-23 09:13:28 +00:00
Marc KlingenandGitHub bc4597e183 fix(ui): enable selection of text in trace inputs/outputs (#1227) 2024-02-23 02:37:45 +00:00
Marc Klingen 9bb1caf738 chore: fix paths in docs after migration to /web 2024-02-23 03:30:24 +01:00
Marc KlingenandGitHub 3ab4eb6341 docs: fix db schema in contributing.md 2024-02-23 01:14:58 +01:00
Max DeichmannandGitHub 5944611b51 fix: setup prettier (#1226) 2024-02-22 13:51:11 +00:00
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
534 changed files with 37759 additions and 19620 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
+7 -1
View File
@@ -29,7 +29,7 @@ SALT="salt"
# LANGFUSE_DEFAULT_PROJECT_ROLE=
# Enable experimental features, optional
# NEXT_PUBLIC_ENABLE_EXPERIMENTAL_FEATURES
# LANGFUSE_ENABLE_EXPERIMENTAL_FEATURES=true
# Auth, optional configuration
# AUTH_DOMAINS_WITH_SSO_ENFORCEMENT=domain1.com,domain2.com
@@ -38,11 +38,14 @@ SALT="salt"
# SSO, each group is optional
# AUTH_GOOGLE_CLIENT_ID=
# AUTH_GOOGLE_CLIENT_SECRET=
# AUTH_GOOGLE_ALLOW_ACCOUNT_LINKING=false
# AUTH_GITHUB_CLIENT_ID=
# AUTH_GITHUB_CLIENT_SECRET=
# AUTH_GITHUB_ALLOW_ACCOUNT_LINKING=false
# AUTH_AZURE_AD_CLIENT_ID=
# AUTH_AZURE_AD_CLIENT_SECRET=
# AUTH_AZURE_AD_TENANT_ID=
# AUTH_AZURE_ALLOW_ACCOUNT_LINKING=false
# Transactional email, optional
# Defines the email address to use as the from address.
@@ -57,6 +60,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
+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
+28 -1
View File
@@ -6,7 +6,34 @@
version: 2
updates:
- package-ecosystem: npm
directory: "/" # Location of package manifests
directory: "/worker" # Location of package manifests
schedule:
interval: "daily"
rebase-strategy: "disabled" # use dependabot-rebase-stale
commit-message:
prefix: chore
prefix-development: chore
include: scope
ignore:
- dependency-name: "@types/node"
- dependency-name: "@trpc/*"
groups:
sentry:
patterns:
- "@sentry/*"
prisma:
patterns:
- "prisma"
- "@prisma/*"
next:
patterns:
- "eslint-config-next"
- "next"
patches:
update-types:
- "patch"
- package-ecosystem: npm
directory: "/web" # Location of package manifests
schedule:
interval: "daily"
rebase-strategy: "disabled" # use dependabot-rebase-stale
+51 -33
View File
@@ -12,8 +12,15 @@ on:
branches:
- "main"
defaults:
run:
working-directory: web
jobs:
lint:
defaults:
run:
working-directory: .
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
@@ -21,46 +28,45 @@ jobs:
with:
node-version: 20
cache: "npm"
cache-dependency-path: "**/package-lock.json"
- name: install dependencies
run: |
npm ci
- name: Load default env
run: |
cp .env.dev.example .env
- name: lint
- name: lint web
working-directory: ./web
run: npm run lint
- name: lint worker
working-directory: ./worker
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 }}
steps:
- name: Setup node
uses: actions/setup-node@v3
with:
node-version: 20
- name: Checkout
uses: actions/checkout@v3
- name: Build Docker image
- name: Build Web Docker image
uses: docker/build-push-action@v4
with:
context: .
context: web
push: false
- name: Build Worker Docker image
uses: docker/build-push-action@v4
with:
context: worker
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 +83,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 +115,9 @@ jobs:
run: npm run test
e2e-tests:
defaults:
run:
working-directory: web
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
@@ -109,19 +125,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 +165,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 +177,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 +188,7 @@ jobs:
uses: actions/setup-node@v3
with:
node-version: 20
cache-dependency-path: "**/package-lock.json"
- name: Checkout
uses: actions/checkout@v3
@@ -190,16 +212,12 @@ jobs:
type=sha
type=semver,pattern={{version}}
type=semver,pattern={{major}}.{{minor}}
type=semver,pattern={{major}}
- 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 }}
+2 -4
View File
@@ -21,13 +21,11 @@
"**/*.{cjs,mjs,ts,tsx,astro,md,mdx,json,yaml,yml}"
],
"mdx.experimentalLanguageServer": true,
"[astro]": {
"editor.defaultFormatter": "astro-build.astro-vscode"
},
"typescript.preferences.importModuleSpecifier": "non-relative",
"docwriter.style": "JSDoc",
"[prisma]": {
"editor.defaultFormatter": "Prisma.prisma"
},
"eslint.lintTask.enable": true
"eslint.lintTask.enable": true,
"eslint.workingDirectories": ["./web", "./worker"]
}
+29 -14
View File
@@ -91,9 +91,9 @@ flowchart TB
The diagram below may not show all relationships if the foreign key is not defined in the database schema. For instance, `trace_id` in the `observation` table is not defined as a foreign key to the `trace` table to allow unordered ingestion of these objects, but it is still a foreign key in the application code.
Full database schema: [prisma/schema.prisma](prisma/schema.prisma)
Full database schema: [web/prisma/schema.prisma](web/prisma/schema.prisma)
<img src="./prisma/database.svg">
<img src="./web/prisma/database.svg">
### Infrastructure & Network Overview
@@ -110,7 +110,14 @@ flowchart LR
App --- DB
```
## Development Setup
## Repository Structure
This repository contains the following packages:
- `web`: is the main application package providing Frontend and Backend APIs for Langfuse
- `worker` (no production yet): contains an application for asynchronous processing of tasks. This package is not yet used in production.
## Development Setup (web package only)
Requirements
@@ -120,25 +127,33 @@ Requirements
**Steps**
1. Fork the the repository and clone it locally
2. Install dependencies
2. Run the development database
```bash
npm install
npm run infra:dev:up
```
3. 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
The `schema.prisma` file is in the `web` directory and hence, migrations are applied from there.
```bash
npm run db:migrate
@@ -148,7 +163,7 @@ Requirements
# npm run db:seed:examples
```
6. Start the development server
7. Start the development server
```bash
npm run dev
+21 -27
View File
@@ -21,21 +21,12 @@
</div>
</br>
<div>
<a href="https://langfuse.com/docs">
<strong>Docs</strong>
</a> ·
<a href="https://langfuse.com/issue">
<strong>Report Bug</strong>
</a> ·
<a href="https://langfuse.com/idea">
<strong>Feature Request</strong>
</a> ·
<a href="https://langfuse.com/changelog">
<strong>Changelog</strong>
</a> ·
<a href="https://langfuse.com/discord">
<strong>Discord</strong>
</a>
<a href="https://langfuse.com/docs"><strong>Docs</strong></a> ·
<a href="https://langfuse.com/issues"><strong>Report Bug</strong></a> ·
<a href="https://langfuse.com/ideas"><strong>Feature Request</strong></a> ·
<a href="https://langfuse.com/changelog"><strong>Changelog</strong></a> ·
<a href="https://langfuse.com/roadmap"><strong>Roadmap</strong></a> ·
<a href="https://langfuse.com/discord"><strong>Discord</strong></a>
</div>
</br>
<img src="https://img.shields.io/badge/License-MIT-red.svg?style=flat-square" alt="MIT License">
@@ -119,20 +110,22 @@ See our the [→ Quickstart](https://langfuse.com/docs/get-started) to get start
### Integrations
| Integration | Supports | Description |
| -------------------------------------------------------- | ------------- | ------------------------------------------------------------------------------- |
| [**SDK** - _recommended_](https://langfuse.com/docs/sdk) | Python, JS/TS | Manual instrumentation using the SDKs for full flexibility. |
| [OpenAI](https://langfuse.com/docs/openai) | Python | Automated instrumentation using drop-in replacement of OpenAI SDK. |
| [Langchain](https://langfuse.com/docs/langchain) | Python, JS/TS | Automated instrumentation by passing callback handler to Langchain application. |
| [API](https://langfuse.com/docs/api) | | Directly call the public API. OpenAPI spec available. |
| Integration | Supports | Description |
| ---------------------------------------------------------------- | ------------- | ---------------------------------------------------------------- |
| [**SDK** - _recommended_](https://langfuse.com/docs/sdk) | Python, JS/TS | Manual instrumentation using the SDKs for full flexibility. |
| [OpenAI SDK](https://langfuse.com/docs/integrations/openai) | Python | Automated instrumentation via drop-in replacement of OpenAI SDK. |
| [Langchain](https://langfuse.com/docs/integrations/langchain) | Python, JS/TS | Automated instrumentation via LlamaIndex callback system. |
| [LlamaIndex](https://langfuse.com/docs/integrations/llama-index) | Python, JS/TS | Automated instrumentation via LlamaIndex callback system. |
| [API](https://langfuse.com/docs/api) | | Directly call the public API. OpenAPI spec available. |
External projects/packages that integrate with Langfuse:
| Name | Description |
| ---------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------ |
| [LiteLLM](/https://langfuse.comdocs/litellm) | Use any LLM as a drop in replacement for GPT. Use Azure, OpenAI, Cohere, Anthropic, Ollama, VLLM, Sagemaker, HuggingFace, Replicate (100+ LLMs). |
| [Flowise](https://langfuse.com/docs/flowise) | JS/TS no-code builder for customized LLM flows. |
| [Langflow](https://langfuse.com/docs/langflow) | Python-based UI for LangChain, designed with react-flow to provide an effortless way to experiment and prototype flows. |
| Name | Description |
| --------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------ |
| [LiteLLM](https://langfuse.com/docs/integrations/litellm) | Use any LLM as a drop in replacement for GPT. Use Azure, OpenAI, Cohere, Anthropic, Ollama, VLLM, Sagemaker, HuggingFace, Replicate (100+ LLMs). |
| [Flowise](https://langfuse.com/docs/integrations/flowise) | JS/TS no-code builder for customized LLM flows. |
| [Langflow](https://langfuse.com/docs/integrations/langflow) | Python-based UI for LangChain, designed with react-flow to provide an effortless way to experiment and prototype flows. |
| [Superagent](https://langfuse.com/docs/integrations/superagent) | Open Source AI Assistant Framework & API for prototyping and deployment of agents. |
## Questions and feedback
@@ -140,6 +133,7 @@ External projects/packages that integrate with Langfuse:
- [GitHub Discussions](https://github.com/orgs/langfuse/discussions)
- [Feature Requests](https://langfuse.com/idea)
- [Roadmap](https://langfuse.com/roadmap)
### Support and feedback
@@ -178,6 +172,6 @@ This helps us to:
1. Understand how Langfuse is used and improve the most relevant features.
2. Track overall usage for internal and external (e.g. fundraising) reporting.
None of the data is shared with third parties and does not include any sensitive information. We want to be super transparent about this and you can find the exact data we collect [here](/src/features/telemetry/index.ts).
None of the data is shared with third parties and does not include any sensitive information. We want to be super transparent about this and you can find the exact data we collect [here](/web/src/features/telemetry/index.ts).
You can opt-out by setting `TELEMETRY_ENABLED=false`.
+36 -8
View File
@@ -1,20 +1,16 @@
version: "3.5"
services:
langfuse-server:
server:
build:
dockerfile: Dockerfile
args:
- DATABASE_URL=postgresql://postgres:postgres@db:5432/postgres
- NEXTAUTH_SECRET=mysecret
- SALT=mysalt
- NEXTAUTH_URL=http://localhost:3000
dockerfile: ./Dockerfile
context: ./web
depends_on:
- db
- redis
ports:
- "3000:3000"
environment:
- NODE_ENV=production
- DATABASE_URL=postgresql://postgres:postgres@db:5432/postgres
- NEXTAUTH_SECRET=mysecret
- SALT=mysalt
@@ -22,8 +18,40 @@ services:
- TELEMETRY_ENABLED=${TELEMETRY_ENABLED:-true}
- NEXT_PUBLIC_SIGN_UP_DISABLED=${NEXT_PUBLIC_SIGN_UP_DISABLED:-false}
- LANGFUSE_ENABLE_EXPERIMENTAL_FEATURES=${LANGFUSE_ENABLE_EXPERIMENTAL_FEATURES:-false}
- REDIS_URL=${REDIS_URL:-redis}
- REDIS_PORT=${REDIS_PORT:-6379}
- REDIS_AUTH=${REDIS_AUTH:-myredissecret}
restart: always
worker:
build:
dockerfile: ./Dockerfile
context: ./worker
depends_on:
- db
- redis
- server
ports:
- "3030:3030"
environment:
- DATABASE_URL=postgresql://postgres:postgres@db:5432/postgres
- NEXTAUTH_SECRET=mysecret
- TELEMETRY_ENABLED=${TELEMETRY_ENABLED:-true}
- LANGFUSE_ENABLE_EXPERIMENTAL_FEATURES=${LANGFUSE_ENABLE_EXPERIMENTAL_FEATURES:-false}
- PORT=${PORT:-3030}
- REDIS_URL=${REDIS_URL:-redis}
- REDIS_PORT=${REDIS_PORT:-6379}
- REDIS_AUTH=${REDIS_AUTH:-myredissecret}
restart: always
redis:
image: redis
command: >
--requirepass ${REDIS_AUTH:-myredissecret}
restart: always
ports:
- 6379:6379
db:
image: postgres
restart: always
+6
View File
@@ -1,6 +1,12 @@
version: "3.5"
services:
redis:
image: redis
restart: always
ports:
- 6379:6379
db:
image: postgres
restart: always
-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
+44 -8
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,22 +87,38 @@ types:
calculatedInputCost: optional<double>
calculatedOutputCost: optional<double>
calculatedTotalCost: optional<double>
latency: optional<double>
Usage:
docs: Standard interface for usage and cost
properties:
input: optional<integer>
output: optional<integer>
total: optional<integer>
input:
docs: Number of input units (e.g. tokens)
type: optional<integer>
output:
docs: Number of output units (e.g. tokens)
type: optional<integer>
total:
docs: Defaults to input+output if not set
type: optional<integer>
unit: optional<ModelUsageUnit>
inputCost: optional<double>
outputCost: optional<double>
totalCost: optional<double>
inputCost:
docs: USD input cost
type: optional<double>
outputCost:
docs: USD output cost
type: optional<double>
totalCost:
docs: USD total cost, defaults to input+output
type: optional<double>
Score:
properties:
id: string
traceId: string
name: string
value: double
source: ScoreSource
observationId: optional<string>
timestamp: datetime
comment: optional<string>
@@ -129,6 +160,7 @@ types:
# Utilities
ModelUsageUnit:
docs: Unit of usage in Langfuse
enum:
- CHARACTERS
- TOKENS
@@ -152,6 +184,10 @@ types:
enum:
- ACTIVE
- ARCHIVED
ScoreSource:
enum:
- API
- REVIEW
errors:
Error:
@@ -7,12 +7,12 @@ service:
endpoints:
create:
method: POST
docs: Create a dataset item, upserts on id
docs: Create a dataset item
path: /dataset-items
request: CreateDatasetItemRequest
response: commons.DatasetItem
get:
docs: Get a specific dataset item
docs: Get a dataset item
method: GET
path: /dataset-items/{id}
path-parameters:
@@ -26,4 +26,6 @@ types:
datasetName: string
input: unknown
expectedOutput: optional<unknown>
id: optional<string>
id:
type: optional<string>
docs: Dataset items are upserted on their id
+9 -3
View File
@@ -7,7 +7,7 @@ service:
base-path: /api/public
endpoints:
batch:
docs: Ingest multiple events to Langfuse
docs: Batched ingestion for Langfuse Tracing
method: POST
path: /ingestion
request:
@@ -31,8 +31,12 @@ types:
sdk-log: SDKLogEvent
# both are legacy
observation-create: CreateObservationEvent
observation-update: UpdateObservationEvent
observation-create:
type: CreateObservationEvent
docs: Deprecated event type
observation-update:
type: UpdateObservationEvent
docs: Deprecated event type
ObservationType:
enum:
@@ -47,6 +51,7 @@ types:
- OpenAIUsage
OpenAIUsage:
docs: Usage interface of OpenAI for improved compatibility.
properties:
promptTokens: optional<integer>
completionTokens: optional<integer>
@@ -128,6 +133,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
+1 -1
View File
@@ -7,7 +7,7 @@ service:
base-path: /api/public
endpoints:
get:
docs: Get a specific observation
docs: Get a observation
method: GET
path: /observations/{observationId}
path-parameters:
+1
View File
@@ -7,6 +7,7 @@ service:
base-path: /api/public
endpoints:
get:
docs: Get Project associated with API key
method: GET
path: /projects
response: Projects
+8 -3
View File
@@ -7,7 +7,7 @@ service:
base-path: /api/public
endpoints:
get:
docs: Get a specific prompt
docs: Get a prompt
method: GET
path: /prompts
request:
@@ -17,7 +17,7 @@ service:
version: optional<integer>
response: Prompt
create:
docs: Create a specific prompt
docs: Create a prompt
method: POST
path: /prompts
request: CreatePromptRequest
@@ -27,10 +27,15 @@ types:
CreatePromptRequest:
properties:
name: string
isActive: boolean
isActive:
docs: Should the prompt be promoted to production immediately?
type: boolean
prompt: string
config: optional<unknown>
Prompt:
properties:
name: string
version: integer
prompt: string
config: unknown
+19 -2
View File
@@ -7,13 +7,13 @@ service:
base-path: /api/public
endpoints:
create:
docs: Add a score to the database, upserts on id
docs: Create a score
method: POST
path: /scores
request: CreateScoreRequest
response: commons.Score
get:
docs: Get scores
docs: Get a list of scores
method: GET
path: /scores
request:
@@ -24,6 +24,23 @@ service:
userId: optional<string>
name: optional<string>
response: Scores
get-by-id:
docs: Get a score
method: GET
path: /scores/{scoreId}
path-parameters:
scoreId:
type: string
docs: The unique langfuse identifier of a score
response: commons.Score
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:
+2 -2
View File
@@ -27,8 +27,8 @@ service:
userId: optional<string>
name: optional<string>
orderBy:
type: string
docs: Format of the string sort_by=timestamp.asc (id, timestamp, name, userId, release, version, public, bookmarked, sessionId)
type: optional<string>
docs: "Format of the string [field].[asc/desc]. Fields: id, timestamp, name, userId, release, version, public, bookmarked, sessionId. Example: timestamp.asc"
tags:
type: optional<string>
allow-multiple: true
+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
+135 -12
View File
@@ -46,7 +46,7 @@
"_type": "endpoint",
"name": "Create",
"request": {
"description": "Create a dataset item, upserts on id",
"description": "Create a dataset item",
"url": {
"raw": "{{baseUrl}}/api/public/dataset-items",
"host": [
@@ -79,7 +79,7 @@
"_type": "endpoint",
"name": "Get",
"request": {
"description": "Get a specific dataset item",
"description": "Get a dataset item",
"url": {
"raw": "{{baseUrl}}/api/public/dataset-items/:id",
"host": [
@@ -301,7 +301,7 @@
"_type": "endpoint",
"name": "Batch",
"request": {
"description": "Ingest multiple events to Langfuse",
"description": "Batched ingestion for Langfuse Tracing",
"url": {
"raw": "{{baseUrl}}/api/public/ingestion",
"host": [
@@ -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,
@@ -341,7 +400,7 @@
"_type": "endpoint",
"name": "Get",
"request": {
"description": "Get a specific observation",
"description": "Get a observation",
"url": {
"raw": "{{baseUrl}}/api/public/observations/:observationId",
"host": [
@@ -441,7 +500,7 @@
"_type": "endpoint",
"name": "Get",
"request": {
"description": null,
"description": "Get Project associated with API key",
"url": {
"raw": "{{baseUrl}}/api/public/projects",
"host": [
@@ -473,7 +532,7 @@
"_type": "endpoint",
"name": "Get",
"request": {
"description": "Get a specific prompt",
"description": "Get a prompt",
"url": {
"raw": "{{baseUrl}}/api/public/prompts?name=&version=",
"host": [
@@ -509,7 +568,7 @@
"_type": "endpoint",
"name": "Create",
"request": {
"description": "Create a specific prompt",
"description": "Create a prompt",
"url": {
"raw": "{{baseUrl}}/api/public/prompts",
"host": [
@@ -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"
@@ -549,7 +608,7 @@
"_type": "endpoint",
"name": "Create",
"request": {
"description": "Add a score to the database, upserts on id",
"description": "Create a score",
"url": {
"raw": "{{baseUrl}}/api/public/scores",
"host": [
@@ -582,7 +641,7 @@
"_type": "endpoint",
"name": "Get",
"request": {
"description": "Get scores",
"description": "Get a list of scores",
"url": {
"raw": "{{baseUrl}}/api/public/scores?page=&limit=&userId=&name=",
"host": [
@@ -623,6 +682,70 @@
"body": null
},
"response": []
},
{
"_type": "endpoint",
"name": "Get By Id",
"request": {
"description": "Get 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": "GET",
"auth": null,
"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": []
}
]
},
@@ -741,7 +864,7 @@
{
"key": "orderBy",
"value": "",
"description": "Format of the string sort_by=timestamp.asc (id, timestamp, name, userId, release, version, public, bookmarked, sessionId)"
"description": "Format of the string [field].[asc/desc]. Fields: id, timestamp, name, userId, release, version, public, bookmarked, sessionId. Example: timestamp.asc"
},
{
"key": "tags",
+198 -16549
View File
File diff suppressed because it is too large Load Diff
+13 -166
View File
@@ -1,176 +1,23 @@
{
"name": "langfuse-core",
"version": "2.4.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.100.1",
"@sentry/profiling-node": "^7.100.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",
"@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.105.7",
"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",
"dev": "npm run dev --prefix worker/ & npm run dev --prefix web/",
"postinstall": "cd worker && npm install && cd ../web && npm install",
"nuke": "bash ./scripts/nuke.sh",
"dx": "dotenv -e .env -- npm i && npm run infra:dev:up && cd web && npm run db:reset && npm run db:seed:examples && cd .. && npm run dev"
},
"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.19",
"@types/uuid": "^9.0.8",
"@typescript-eslint/eslint-plugin": "^6.21.0",
"@typescript-eslint/parser": "^6.21.0",
"autoprefixer": "^10.4.17",
"@typescript-eslint/eslint-plugin": "^7.0.2",
"@typescript-eslint/parser": "^7.0.2",
"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",
"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.3",
"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"
}
}
}
"prettier": "^3.2.5"
}
}
-603
View File
@@ -1,603 +0,0 @@
import { PrismaClient } from "@prisma/client";
import {
hashSecretKey,
getDisplaySecretKey,
} from "@/src/features/public-api/lib/apiKeys";
import { hash } from "bcryptjs";
import { parseArgs } from "node:util";
import { ModelUsageUnit } from "@/src/constants";
const options = {
environment: { type: "string" },
} as const;
const prisma = new PrismaClient();
const TRACE_VOLUME = 100;
async function main() {
const environment = parseArgs({
options,
}).values.environment;
const user = await prisma.user.upsert({
where: { id: "user-1" },
update: {
name: "Demo User",
email: "demo@langfuse.com",
password: await hash("password", 12),
},
create: {
id: "user-1",
name: "Demo User",
email: "demo@langfuse.com",
password: await hash("password", 12),
},
});
const seedProjectId = "7a88fb47-b4e2-43b8-a06c-a5ce950dc53a";
const project1 = await prisma.project.upsert({
where: { id: seedProjectId },
update: {
name: "llm-app",
},
create: {
id: seedProjectId,
name: "llm-app",
members: {
create: {
role: "OWNER",
userId: user.id,
},
},
},
});
const prompt = await prisma.prompt.upsert({
where: {
projectId_name_version: {
projectId: seedProjectId,
name: "summary-prompt",
version: 1,
},
},
create: {
name: "summary-prompt",
project: { connect: { id: seedProjectId } },
prompt: "prompt {{variable}} {{anotherVariable}}",
isActive: true,
version: 1,
createdBy: "user-1",
},
update: {},
});
const seedApiKey = {
id: "seed-api-key",
secret: process.env.SEED_SECRET_KEY ?? "sk-lf-1234567890",
public: "pk-lf-1234567890",
note: "seeded key",
};
if (!(await prisma.apiKey.findUnique({ where: { id: seedApiKey.id } }))) {
await prisma.apiKey.create({
data: {
note: seedApiKey.note,
id: seedApiKey.id,
publicKey: seedApiKey.public,
hashedSecretKey: await hashSecretKey(seedApiKey.secret),
displaySecretKey: getDisplaySecretKey(seedApiKey.secret),
project: {
connect: {
id: project1.id,
},
},
},
});
}
// Do not run the following for local docker compose setup
if (environment === "examples") {
const project2 = await prisma.project.upsert({
where: { id: "239ad00f-562f-411d-af14-831c75ddd875" },
create: {
name: "demo-app",
apiKeys: {
create: [
{
note: "seeded key",
hashedSecretKey: await hashSecretKey("sk-lf-asdfghjkl"),
displaySecretKey: getDisplaySecretKey("sk-lf-asdfghjkl"),
publicKey: "pk-lf-asdfghjkl",
},
],
},
members: {
create: {
role: "OWNER",
userId: user.id,
},
},
},
update: {},
});
const promptIds: string[] = [];
const prompts = [
{
id: `prompt-${Math.floor(Math.random() * 1000000000)}`,
projectId: project2.id,
createdBy: "user-1",
prompt: "Prompt 1 content",
name: "Prompt 1",
version: 1,
isActive: true,
},
{
id: `prompt-${Math.floor(Math.random() * 1000000000)}`,
projectId: project2.id,
createdBy: "user-1",
prompt: "Prompt 2 content",
name: "Prompt 2",
version: 1,
isActive: true,
},
{
id: `prompt-${Math.floor(Math.random() * 1000000000)}`,
projectId: project2.id,
createdBy: "API",
prompt: "Prompt 3 content",
name: "Prompt 3 by API",
version: 1,
isActive: true,
},
];
for (const prompt of prompts) {
await prisma.prompt.create({
data: {
id: prompt.id,
projectId: prompt.projectId,
createdBy: prompt.createdBy,
prompt: prompt.prompt,
name: prompt.name,
version: prompt.version,
isActive: prompt.isActive,
},
});
promptIds.push(prompt.id);
}
const promptVersionsWithVariables = [
{
id: `prompt-${Math.floor(Math.random() * 1000000000)}`,
projectId: project2.id,
createdBy: "user-1",
prompt: "Prompt 4 version 1 content with {{variable}}",
name: "Prompt 4 with variable",
version: 1,
isActive: false,
},
{
id: `prompt-${Math.floor(Math.random() * 1000000000)}`,
projectId: project2.id,
createdBy: "user-1",
prompt: "Prompt 4 version 2 content with {{variable}}",
name: "Prompt 4 with variable",
version: 2,
isActive: true,
},
{
id: `prompt-${Math.floor(Math.random() * 1000000000)}`,
projectId: project2.id,
createdBy: "user-1",
prompt: "Prompt 4 version 3 content with {{variable}}",
name: "Prompt 4 with variable",
version: 3,
isActive: false,
},
];
for (const version of promptVersionsWithVariables) {
await prisma.prompt.create({
data: {
id: version.id,
projectId: version.projectId,
createdBy: version.createdBy,
prompt: version.prompt,
name: version.name,
version: version.version,
isActive: version.isActive,
},
});
promptIds.push(version.id);
}
const promptName = "Prompt with Longer Name";
const projectId = project2.id;
const createdBy = "user-1";
for (let i = 1; i <= 20; i++) {
const promptId = `prompt-${Math.floor(Math.random() * 1000000000)}`;
await prisma.prompt.create({
data: {
id: promptId,
projectId: projectId,
createdBy: createdBy,
prompt: `${promptName} version ${i} content`,
name: promptName,
version: i,
isActive: i === 20,
},
});
promptIds.push(promptId);
}
const generationIds: string[] = [];
const envTags = [null, "development", "staging", "production"];
const colorTags = [null, "red", "blue", "yellow"];
for (let i = 0; i < TRACE_VOLUME; i++) {
// print progress to console with a progress bar that refreshes every 10 iterations
if ((i + 1) % 10 === 0 || i === TRACE_VOLUME - 1) {
process.stdout.clearLine(0);
process.stdout.cursorTo(0);
process.stdout.write(`Seeding ${i + 1} of ${TRACE_VOLUME}`);
}
// random date within last 90 days, with a linear bias towards more recent dates
const traceTs = new Date(
Date.now() -
Math.floor(Math.random() ** 1.5 * 90 * 24 * 60 * 60 * 1000),
);
const envTag = envTags[Math.floor(Math.random() * envTags.length)];
const colorTag = colorTags[Math.floor(Math.random() * colorTags.length)];
const tags = [envTag, colorTag].filter((tag) => tag !== null);
const projectId = [project1.id, project2.id][i % 2] as string;
const trace = await prisma.trace.create({
data: {
id: `trace-${Math.floor(Math.random() * 1000000000)}`,
timestamp: traceTs,
name: ["generate-outreach", "label-inbound", "draft-response"][
i % 3
] as string,
metadata: {
user: `user-${i}@langfuse.com`,
},
tags: tags as string[],
project: {
connect: { id: projectId },
},
userId: `user-${i % 10}`,
session:
Math.random() > 0.3
? {
connectOrCreate: {
where: {
id_projectId: {
id: `session-${i % 10}`,
projectId: projectId,
},
},
create: {
id: `session-${i % 10}`,
projectId: projectId,
},
},
}
: undefined,
input:
Math.random() > 0.3
? "I'm looking for a React component"
: undefined,
output:
Math.random() > 0.3
? "What kind of component are you looking for?"
: undefined,
scores: {
createMany: {
data: [
...(Math.random() > 0.5
? [
{
name: "feedback",
value: Math.floor(Math.random() * 3) - 1,
timestamp: traceTs,
},
]
: []),
...(Math.random() > 0.7
? [
{
name: "sentiment",
value: Math.floor(Math.random() * 10) - 5,
timestamp: traceTs,
},
]
: []),
],
},
},
},
});
const existingSpanIds: string[] = [];
for (let j = 0; j < Math.floor(Math.random() * 10) + 1; j++) {
// add between 1 and 30 ms to trace timestamp
const spanTsStart = new Date(
traceTs.getTime() + Math.floor(Math.random() * 30),
);
// random duration of upto 30ms
const spanTsEnd = new Date(
spanTsStart.getTime() + Math.floor(Math.random() * 30),
);
const span = await prisma.observation.create({
data: {
type: "SPAN",
id: `span-${Math.floor(Math.random() * 1000000000)}`,
startTime: spanTsStart,
endTime: spanTsEnd,
name: `span-${i}-${j}`,
metadata: {
user: `user-${i}@langfuse.com`,
},
project: { connect: { id: trace.projectId } },
traceId: trace.id,
// if this is the first span or in 50% of cases, add no parent; otherwise randomly select parent from existing spans
...(existingSpanIds.length === 0 || Math.random() > 0.5
? {}
: {
parentObservationId:
existingSpanIds[
Math.floor(Math.random() * existingSpanIds.length)
],
}),
},
});
existingSpanIds.push(span.id);
for (let k = 0; k < Math.floor(Math.random() * 2) + 1; k++) {
// random start and end times within span
const generationTsStart = new Date(
spanTsStart.getTime() +
Math.floor(
Math.random() * (spanTsEnd.getTime() - spanTsStart.getTime()),
),
);
const generationTsEnd = new Date(
generationTsStart.getTime() +
Math.floor(
Math.random() *
(spanTsEnd.getTime() - generationTsStart.getTime()),
),
);
const promptTokens = Math.floor(Math.random() * 1000) + 300;
const completionTokens = Math.floor(Math.random() * 500) + 100;
const models = [
"gpt-3.5-turbo",
"gpt-4",
"gpt-4-32k-0613",
"gpt-3.5-turbo-16k-0613",
"claude-instant-1",
"claude-2.1",
"gpt-4-vision-preview",
"MIXTRAL-8X7B",
];
const model = models[Math.floor(Math.random() * models.length)];
const promptId =
promptIds[Math.floor(Math.random() * promptIds.length)];
const generation = await prisma.observation.create({
data: {
type: "GENERATION",
id: `generation-${Math.floor(Math.random() * 1000000000)}`,
startTime: generationTsStart,
endTime: generationTsEnd,
name: `generation-${i}-${j}-${k}`,
project: { connect: { id: trace.projectId } },
prompt: { connect: { id: promptId } },
input:
Math.random() > 0.5
? [
{
role: "system",
content: "Be a helpful assistant",
},
{
role: "user",
content: "How can i create a React component?",
},
]
: {
input: "How can i create a React component?",
retrievedDocuments: [
{
title: "How to create a React component",
url: "https://www.google.com",
description: "A guide to creating React components",
},
{
title: "React component creation",
url: "https://www.google.com",
description: "A guide to creating React components",
},
],
},
output: {
completion: `Creating a React component can be done in two ways: as a functional component or as a class component. Let's start with a basic example of both.
1. **Functional Component**:
A functional component is just a plain JavaScript function that accepts props as an argument, and returns a React element. Here's how you can create one:
'import React from 'react'; function Greeting(props) { return <h1>Hello, {props.name}</h1>; } export default Greeting;'
To use this component in another file, you can do:
'import Greeting from './Greeting'; function App() { return ( <div> <Greeting name="John" /> </div> ); } export default App;'
2. **Class Component**:
You can also define components as classes in React. These have some additional features compared to functional components:
'import React, { Component } from 'react'; class Greeting extends Component { render() { return <h1>Hello, {this.props.name}</h1>; } } export default Greeting;'
And here's how to use this component:
'import Greeting from './Greeting'; class App extends Component { render() { return ( <div> <Greeting name="John" /> </div> ); } } export default App;'
With the advent of hooks in React, functional components can do everything that class components can do and hence, the community has been favoring functional components over class components.
Remember to import React at the top of your file whenever you're creating a component, because JSX transpiles to 'React.createElement' calls under the hood.`,
},
model: model,
internalModel: model,
modelParameters: {
temperature:
Math.random() > 0.9 ? undefined : Math.random().toFixed(2),
topP:
Math.random() > 0.9 ? undefined : Math.random().toFixed(2),
maxTokens:
Math.random() > 0.9
? undefined
: Math.floor(Math.random() * 1000),
},
metadata: {
user: `user-${i}@langfuse.com`,
},
promptTokens,
completionTokens,
totalTokens: promptTokens + completionTokens,
parentObservationId: span.id,
traceId: trace.id,
...{
...(Math.random() > 0.5 && trace.projectId === prompt.projectId
? { prompt: { connect: { id: prompt.id } } }
: {}),
},
unit: ModelUsageUnit.Tokens,
},
});
if (Math.random() > 0.6)
await prisma.score.create({
data: {
name: "quality",
value: Math.random() * 2 - 1,
observationId: generation.id,
traceId: trace.id,
},
});
if (Math.random() > 0.6)
await prisma.score.create({
data: {
name: "conciseness",
value: Math.random() * 2 - 1,
observationId: generation.id,
traceId: trace.id,
},
});
generationIds.push(generation.id);
for (let l = 0; l < Math.floor(Math.random() * 2); l++) {
// random start time within span
const eventTs = new Date(
spanTsStart.getTime() +
Math.floor(
Math.random() * (spanTsEnd.getTime() - spanTsStart.getTime()),
),
);
await prisma.observation.create({
data: {
type: "EVENT",
id: `event-${Math.floor(Math.random() * 1000000000)}`,
startTime: eventTs,
name: `event-${i}-${j}-${k}-${l}`,
metadata: {
user: `user-${i}@langfuse.com`,
},
parentObservationId: span.id,
traceId: trace.id,
project: { connect: { id: trace.projectId } },
},
});
}
}
}
}
for (let datasetNumber = 0; datasetNumber < 2; datasetNumber++) {
const dataset = await prisma.dataset.create({
data: {
name: `demo-dataset-${datasetNumber}`,
projectId: project2.id,
},
});
for (let datasetRunNumber = 0; datasetRunNumber < 2; datasetRunNumber++) {
const datasetRun = await prisma.datasetRuns.create({
data: {
name: `demo-dataset-run-${datasetRunNumber}`,
datasetId: dataset.id,
},
});
for (let runNumber = 0; runNumber < 10; runNumber++) {
//pick randomly from existingSpanIds
const sourceObservationId =
generationIds[Math.floor(Math.random() * generationIds.length)];
const runObservationId =
generationIds[Math.floor(Math.random() * generationIds.length)];
const datasetItem = await prisma.datasetItem.create({
data: {
datasetId: dataset.id,
sourceObservationId:
Math.random() > 0.5 ? sourceObservationId : undefined,
input: [
{
role: "user",
content: "How can i create a React component?",
},
],
expectedOutput:
"Creating a React component can be done in two ways: as a functional component or as a class component. Let's start with a basic example of both.",
},
});
await prisma.datasetRunItems.create({
data: {
datasetItemId: datasetItem.id,
observationId: runObservationId!,
datasetRunId: datasetRun.id,
},
});
}
}
}
}
}
main()
.then(async () => {
await prisma.$disconnect();
})
.catch(async (e) => {
console.error(e);
await prisma.$disconnect();
process.exit(1);
});
Binary file not shown.

Before

Width:  |  Height:  |  Size: 42 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 37 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 772 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 60 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 312 KiB

+9
View File
@@ -0,0 +1,9 @@
# used to nuke the dev environment for engineers
find . -name 'node_modules' -type d -prune -print -exec rm -rf '{}' \;
find . -name '.next' -type d -prune -print -exec rm -rf '{}' \;
find . -iname "bin" -type d -prune -print -exec rm -rf '{}' \;
find . -iname "dist" -type d -prune -print -exec rm -rf '{}' \;
find . -iname "out" -type d -prune -print -exec rm -rf '{}' \;
find . -iname ".turbo" -type d -prune -print -exec rm -rf '{}' \;
-84
View File
@@ -1,84 +0,0 @@
import { TrashIcon } from "lucide-react";
import { useRouter } from "next/router";
import { useState } from "react";
import { Button } from "@/src/components/ui/button";
import { api } from "@/src/utils/api";
import {
Popover,
PopoverContent,
PopoverTrigger,
} from "@/src/components/ui/popover";
import { useHasAccess } from "@/src/features/rbac/utils/checkAccess";
import { usePostHog } from "posthog-js/react";
export function DeleteTrace({
traceId,
projectId,
isTableAction,
}: {
traceId: string;
projectId: string;
isTableAction?: boolean;
}) {
const [isDeleted, setIsDeleted] = useState(false);
const router = useRouter();
const utils = api.useUtils();
const posthog = usePostHog();
const hasAccess = useHasAccess({ projectId, scope: "traces:delete" });
const mutDeleteTraces = api.traces.deleteMany.useMutation({
onSuccess: () => {
setIsDeleted(true);
void utils.traces.invalidate();
if (!isTableAction) {
void router.push(`/project/${projectId}/traces`);
}
},
});
if (!hasAccess) {
return null;
}
return (
<Popover key={traceId}>
<PopoverTrigger asChild>
{isTableAction ? (
<Button variant="ghost" size="xs">
<TrashIcon className="h-4 w-4" />
</Button>
) : (
<Button variant="outline" type="button" size="icon">
<TrashIcon className="h-4 w-4" />
</Button>
)}
</PopoverTrigger>
<PopoverContent>
<h2 className="text-md mb-3 font-semibold">Please confirm</h2>
<p className="mb-3 text-sm">
This action cannot be undone and removes all the data associated with
this trace.
</p>
<div className="flex justify-end space-x-4">
<Button
type="button"
variant="destructive"
loading={mutDeleteTraces.isLoading || isDeleted}
onClick={() => {
void mutDeleteTraces.mutateAsync({
traceIds: [traceId],
projectId,
});
posthog.capture("trace:delete", {
source: isTableAction ? "table-single-row" : "trace",
});
}}
>
Delete trace
</Button>
</div>
</PopoverContent>
</Popover>
);
}
-1
View File
@@ -1 +0,0 @@
export const VERSION = "v2.4.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,52 +0,0 @@
import { Button } from "@/src/components/ui/button";
import { LockIcon, PlusIcon } from "lucide-react";
import {
Dialog,
DialogContent,
DialogHeader,
DialogTitle,
} from "@/src/components/ui/dialog";
import { useState } from "react";
import { DialogTrigger } from "@radix-ui/react-dialog";
import { NewDatasetForm } from "@/src/features/datasets/components/NewDatasetForm";
import { useHasAccess } from "@/src/features/rbac/utils/checkAccess";
export const NewDatasetButton = (props: {
projectId: string;
datasetId?: string;
className?: string;
}) => {
const [open, setOpen] = useState(false);
const hasAccess = useHasAccess({
projectId: props.projectId,
scope: "datasets:CUD",
});
return (
<Dialog open={hasAccess && open} onOpenChange={setOpen}>
<DialogTrigger asChild>
<Button
variant="secondary"
className={props.className}
disabled={!hasAccess}
>
{hasAccess ? (
<PlusIcon className="-ml-0.5 mr-1.5" aria-hidden="true" />
) : (
<LockIcon className="-ml-0.5 mr-1.5 h-3 w-3" aria-hidden="true" />
)}
New dataset
</Button>
</DialogTrigger>
<DialogContent className="sm:max-w-3xl">
<DialogHeader>
<DialogTitle className="mb-5">Create new dataset</DialogTitle>
</DialogHeader>
<NewDatasetForm
projectId={props.projectId}
onFormSuccess={() => setOpen(false)}
/>
</DialogContent>
</Dialog>
);
};
@@ -1,94 +0,0 @@
import { Button } from "@/src/components/ui/button";
import * as z from "zod";
import { zodResolver } from "@hookform/resolvers/zod";
import { useForm } from "react-hook-form";
import {
Form,
FormControl,
FormField,
FormItem,
FormLabel,
FormMessage,
} from "@/src/components/ui/form";
import { api } from "@/src/utils/api";
import { useState } from "react";
import { usePostHog } from "posthog-js/react";
import { Input } from "@/src/components/ui/input";
const formSchema = z.object({
name: z.string(),
});
export const NewDatasetForm = (props: {
projectId: string;
onFormSuccess?: () => void;
}) => {
const [formError, setFormError] = useState<string | null>(null);
const posthog = usePostHog();
const form = useForm<z.infer<typeof formSchema>>({
resolver: zodResolver(formSchema),
defaultValues: {
name: "",
},
});
const utils = api.useUtils();
const createDatasetMutation = api.datasets.createDataset.useMutation({
onSuccess: () => utils.datasets.invalidate(),
onError: (error) => setFormError(error.message),
});
function onSubmit(values: z.infer<typeof formSchema>) {
posthog.capture("datasets:new_dataset_form_submit");
createDatasetMutation
.mutateAsync({
...values,
projectId: props.projectId,
})
.then(() => {
props.onFormSuccess?.();
form.reset();
})
.catch((error) => {
console.error(error);
});
}
return (
<div>
<Form {...form}>
<form
// eslint-disable-next-line @typescript-eslint/no-misused-promises
onSubmit={form.handleSubmit(onSubmit)}
className="space-y-8"
>
<FormField
control={form.control}
name="name"
render={({ field }) => (
<FormItem>
<FormLabel>Name</FormLabel>
<FormControl>
<Input {...field} />
</FormControl>
<FormMessage />
</FormItem>
)}
/>
<Button
type="submit"
loading={createDatasetMutation.isLoading}
className="w-full"
>
New dataset
</Button>
</form>
</Form>
{formError ? (
<p className="text-red text-center">
<span className="font-bold">Error:</span> {formError}
</p>
) : null}
</div>
);
};
@@ -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;
}
}
+7
View File
@@ -0,0 +1,7 @@
Dockerfile
.dockerignore
node_modules
npm-debug.log
README.md
.next
.git
+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
+20 -20
View File
@@ -34,26 +34,26 @@ const nextConfig = {
],
},
// Required to check authentication status from langfuse.com
...(env.NEXT_PUBLIC_LANGFUSE_CLOUD_REGION !== undefined ?
[
{
source: "/api/auth/session",
headers: [
{
key: "Access-Control-Allow-Origin",
value: "https://langfuse.com",
},
{ key: "Access-Control-Allow-Credentials", value: "true" },
{ key: "Access-Control-Allow-Methods", value: "GET,POST" },
{
key: "Access-Control-Allow-Headers",
value: "Content-Type, Authorization",
},
]
},
] : []
)
]
...(env.NEXT_PUBLIC_LANGFUSE_CLOUD_REGION !== undefined
? [
{
source: "/api/auth/session",
headers: [
{
key: "Access-Control-Allow-Origin",
value: "https://langfuse.com",
},
{ key: "Access-Control-Allow-Credentials", value: "true" },
{ key: "Access-Control-Allow-Methods", value: "GET,POST" },
{
key: "Access-Control-Allow-Headers",
value: "Content-Type, Authorization",
},
],
},
]
: []),
];
},
// webassembly support for @dqbd/tiktoken
+19977
View File
File diff suppressed because it is too large Load Diff
+180
View File
@@ -0,0 +1,180 @@
{
"name": "langfuse-core",
"version": "2.11.2",
"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 && npm run db:worker",
"db:reset": "dotenv -e ../.env npx -- prisma migrate reset",
"db:deploy": "dotenv -e ../.env npx -- prisma migrate deploy",
"db:seed": "dotenv -e ../.env -- npx prisma db seed",
"db:seed:examples": "dotenv -e ../.env -- npx prisma db seed -- --environment examples",
"db:seed:load": "dotenv -e ../.env -- npx prisma db seed -- --environment load",
"release": "dotenv -e ../.env -- release-it",
"models:migrate": "dotenv -e ../.env -- tsx scripts/model-match.ts",
"db:worker": "cp -r prisma/generated ../worker"
},
"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.515.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.10.2",
"@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.102.1",
"@sentry/profiling-node": "^7.102.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.8-canary.1",
"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.10.2",
"prisma-erd-generator": "^1.11.2",
"release-it": "^17.1.1",
"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

+345
View File
@@ -0,0 +1,345 @@
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 ScoreSource = {
API: "API",
REVIEW: "REVIEW"
} as const;
export type ScoreSource = (typeof ScoreSource)[keyof typeof ScoreSource];
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: number | 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;
source: ScoreSource;
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