Compare commits

..
14 Commits
Author SHA1 Message Date
Max Deichmann 9b7f046e71 chore: release v2.76.0
CI/CD / lint (push) Waiting to run
CI/CD / test-docker-build (push) Waiting to run
CI/CD / tests-web (node20, pg12) (push) Waiting to run
CI/CD / tests-web (node20, pg15) (push) Waiting to run
CI/CD / tests-worker (node20, pg12) (push) Waiting to run
CI/CD / tests-worker (node20, pg15) (push) Waiting to run
CI/CD / e2e-tests (push) Waiting to run
CI/CD / e2e-server-tests (push) Waiting to run
CI/CD / all-ci-passed (push) Blocked by required conditions
CI/CD / push-docker-image (push) Blocked by required conditions
release.yml / release (push) Waiting to run
2024-09-03 10:48:10 +02:00
Steffen SchmitzandGitHub fe63704508 feat: fallback to synchronous ingestion if queue unavailable (#3196) 2024-09-03 10:42:59 +02:00
marliessophieandGitHub c39279f677 style(prompts): actions on hover (#3135) 2024-09-03 08:12:39 +00:00
Marc KlingenandGitHub 4206f5025d perf(cloud): pull usage data shown in settings from stripe if available (#3206) 2024-09-02 23:51:13 +00:00
marliessophieandGitHub 0260a4e861 style(dataset_items): make table vs item detail resizeable (#3139) 2024-09-02 08:01:43 +00:00
Max DeichmannandGitHub 244adf0dc3 feat: disable legacy ingestion queue if required (#3183) 2024-09-01 14:24:25 +00:00
dependabot[bot]GitHubdependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
dc6b98d009 chore(deps-dev): bump tsx from 4.18.0 to 4.19.0 (#3172)
Bumps [tsx](https://github.com/privatenumber/tsx) from 4.18.0 to 4.19.0.
- [Release notes](https://github.com/privatenumber/tsx/releases)
- [Changelog](https://github.com/privatenumber/tsx/blob/master/release.config.cjs)
- [Commits](https://github.com/privatenumber/tsx/compare/v4.18.0...v4.19.0)

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

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-09-01 10:07:44 +02:00
dependabot[bot]GitHubdependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
b20fc5a20b chore(deps-dev): bump tailwindcss from 3.4.4 to 3.4.10 (#3173)
Bumps [tailwindcss](https://github.com/tailwindlabs/tailwindcss) from 3.4.4 to 3.4.10.
- [Release notes](https://github.com/tailwindlabs/tailwindcss/releases)
- [Changelog](https://github.com/tailwindlabs/tailwindcss/blob/v3.4.10/CHANGELOG.md)
- [Commits](https://github.com/tailwindlabs/tailwindcss/compare/v3.4.4...v3.4.10)

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

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-08-31 22:36:42 +02:00
Max DeichmannandGitHub f59ad5fd60 chore: upload sentry sourcemaps (#3179) 2024-08-31 18:50:48 +02:00
dependabot[bot]GitHubdependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
06ef41e372 chore(deps): bump pino-pretty from 10.3.1 to 11.2.2 (#3170)
Bumps [pino-pretty](https://github.com/pinojs/pino-pretty) from 10.3.1 to 11.2.2.
- [Release notes](https://github.com/pinojs/pino-pretty/releases)
- [Commits](https://github.com/pinojs/pino-pretty/compare/v10.3.1...v11.2.2)

---
updated-dependencies:
- dependency-name: pino-pretty
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-08-31 17:04:49 +02:00
dependabot[bot]GitHubdependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
e215a2db99 chore(deps-dev): bump @types/lodash from 4.17.5 to 4.17.7 in the lodash group (#3169)
chore(deps-dev): bump @types/lodash in the lodash group

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


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

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

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-08-31 17:00:36 +02:00
Max DeichmannandGitHub 5e247e88c9 fix: do nothing if invalidating org or project without api keys (#3167)
* also run on main branch

* also run on main branch

* also run on main branch

* also run on main branch

* also run on main branch
2024-08-30 22:57:41 +02:00
dependabot[bot]GitHubdependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
8e1e27c868 chore(deps-dev): bump msw from 2.3.1 to 2.4.1 (#3150)
Bumps [msw](https://github.com/mswjs/msw) from 2.3.1 to 2.4.1.
- [Release notes](https://github.com/mswjs/msw/releases)
- [Changelog](https://github.com/mswjs/msw/blob/main/CHANGELOG.md)
- [Commits](https://github.com/mswjs/msw/compare/v2.3.1...v2.4.1)

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

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-08-30 21:52:41 +02:00
dependabot[bot]andGitHub 4648a5be8b chore(deps): bump lucide-react from 0.429.0 to 0.436.0 (#3154) 2024-08-30 21:02:28 +02:00
30 changed files with 548 additions and 312 deletions
+1
View File
@@ -204,5 +204,6 @@ ENCRYPTION_KEY="0000000000000000000000000000000000000000000000000000000000000000
# LANGFUSE_LOG_LEVEL=
# LANGFUSE_LEGACY_INGESTION_WORKER_CONCURRENCY=
# LANGFUSE_ASYNC_INGESTION_PROCESSING="true"
# QUEUE_CONSUMER_LEGACY_INGESTION_QUEUE_IS_ENABLED="true"
## END Langfuse V3 Ingestion
+4
View File
@@ -111,6 +111,10 @@ jobs:
- name: Build
run: pnpm run build
env:
SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_AUTH_TOKEN }}
SENTRY_ORG: ${{ vars.SENTRY_ORG }}
SENTRY_PROJECT: ${{ vars.SENTRY_PROJECT }}
- name: Start Langfuse
run: (pnpm run start&)
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "langfuse",
"version": "2.75.2",
"version": "2.76.0",
"author": "engineering@langfuse.com",
"license": "MIT",
"private": true,
+1 -1
View File
@@ -81,7 +81,7 @@
"devDependencies": {
"@repo/eslint-config": "workspace:*",
"@repo/typescript-config": "workspace:*",
"@types/lodash": "^4.17.5",
"@types/lodash": "^4.17.7",
"@types/node": "^20.11.29",
"@types/nodemailer": "^6.4.15",
"@types/pg": "^8.11.6",
@@ -2,28 +2,32 @@ import { Queue } from "bullmq";
import { QueueName, TQueueJobTypes } from "../queues";
import { createNewRedisInstance } from "./redis";
let legacyIngestionQueue: Queue<
TQueueJobTypes[QueueName.LegacyIngestionQueue]
> | null = null;
export class LegacyIngestionQueue {
private static instance: Queue<
TQueueJobTypes[QueueName.LegacyIngestionQueue]
> | null = null;
export const getLegacyIngestionQueue = () => {
if (legacyIngestionQueue) return legacyIngestionQueue;
public static getInstance(): Queue<
TQueueJobTypes[QueueName.LegacyIngestionQueue]
> | null {
if (LegacyIngestionQueue.instance) return LegacyIngestionQueue.instance;
const newRedis = createNewRedisInstance();
const newRedis = createNewRedisInstance({ enableOfflineQueue: false });
legacyIngestionQueue = newRedis
? new Queue<TQueueJobTypes[QueueName.LegacyIngestionQueue]>(
QueueName.LegacyIngestionQueue,
{
connection: newRedis,
defaultJobOptions: {
removeOnComplete: true,
removeOnFail: 100,
attempts: 5,
LegacyIngestionQueue.instance = newRedis
? new Queue<TQueueJobTypes[QueueName.LegacyIngestionQueue]>(
QueueName.LegacyIngestionQueue,
{
connection: newRedis,
defaultJobOptions: {
removeOnComplete: true,
removeOnFail: 100,
attempts: 5,
},
},
}
)
: null;
)
: null;
return legacyIngestionQueue;
};
return LegacyIngestionQueue.instance;
}
}
+6 -2
View File
@@ -1,11 +1,14 @@
import Redis from "ioredis";
import Redis, { RedisOptions } from "ioredis";
import { env } from "../../env";
export const createNewRedisInstance = () => {
export const createNewRedisInstance = (
additionalOptions: Partial<RedisOptions> = {},
) => {
return env.REDIS_CONNECTION_STRING
? new Redis(env.REDIS_CONNECTION_STRING, {
maxRetriesPerRequest: null,
enableAutoPipelining: env.REDIS_ENABLE_AUTO_PIPELINING === "true",
...additionalOptions,
})
: env.REDIS_HOST
? new Redis({
@@ -14,6 +17,7 @@ export const createNewRedisInstance = () => {
password: String(env.REDIS_AUTH),
maxRetriesPerRequest: null, // Set to `null` to disable retrying
enableAutoPipelining: env.REDIS_ENABLE_AUTO_PIPELINING === "true",
...additionalOptions,
})
: null;
};
+100 -119
View File
@@ -214,8 +214,8 @@ importers:
specifier: workspace:*
version: link:../config-typescript
'@types/lodash':
specifier: ^4.17.5
version: 4.17.5
specifier: ^4.17.7
version: 4.17.7
'@types/node':
specifier: ^20.11.29
version: 20.11.29
@@ -296,7 +296,7 @@ importers:
version: 1.7.18(react-dom@18.2.0(react@18.2.0))(react@18.2.0)
'@headlessui/tailwindcss':
specifier: 0.2.1
version: 0.2.1(tailwindcss@3.4.4(ts-node@10.9.2(@types/node@20.10.5)(typescript@5.4.5)))
version: 0.2.1(tailwindcss@3.4.10(ts-node@10.9.2(@types/node@20.10.5)(typescript@5.4.5)))
'@heroicons/react':
specifier: ^2.1.3
version: 2.1.3(react@18.2.0)
@@ -452,7 +452,7 @@ importers:
version: 0.10.1(typescript@5.4.5)(zod@3.23.8)
'@tailwindcss/container-queries':
specifier: ^0.1.1
version: 0.1.1(tailwindcss@3.4.4(ts-node@10.9.2(@types/node@20.10.5)(typescript@5.4.5)))
version: 0.1.1(tailwindcss@3.4.10(ts-node@10.9.2(@types/node@20.10.5)(typescript@5.4.5)))
'@tanstack/react-query':
specifier: ^4.36.1
version: 4.36.1(react-dom@18.2.0(react@18.2.0))(react@18.2.0)
@@ -461,7 +461,7 @@ importers:
version: 8.13.2(react-dom@18.2.0(react@18.2.0))(react@18.2.0)
'@tremor/react':
specifier: 3.16.2
version: 3.16.2(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(tailwindcss@3.4.4(ts-node@10.9.2(@types/node@20.10.5)(typescript@5.4.5)))
version: 3.16.2(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(tailwindcss@3.4.10(ts-node@10.9.2(@types/node@20.10.5)(typescript@5.4.5)))
'@trpc/client':
specifier: ^10.45.0
version: 10.45.2(@trpc/server@10.45.2)
@@ -541,8 +541,8 @@ importers:
specifier: ^4.17.21
version: 4.17.21
lucide-react:
specifier: ^0.429.0
version: 0.429.0(react@18.2.0)
specifier: ^0.436.0
version: 0.436.0(react@18.2.0)
next:
specifier: ^14.2.6
version: 14.2.6(@babel/core@7.24.3)(@opentelemetry/api@1.8.0)(@playwright/test@1.43.1)(babel-plugin-macros@3.1.0)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)
@@ -620,7 +620,7 @@ importers:
version: 2.5.2
tailwindcss-animate:
specifier: ^1.0.7
version: 1.0.7(tailwindcss@3.4.4(ts-node@10.9.2(@types/node@20.10.5)(typescript@5.4.5)))
version: 1.0.7(tailwindcss@3.4.10(ts-node@10.9.2(@types/node@20.10.5)(typescript@5.4.5)))
use-query-params:
specifier: ^2.2.1
version: 2.2.1(react-dom@18.2.0(react@18.2.0))(react@18.2.0)
@@ -649,7 +649,7 @@ importers:
version: 1.43.1
'@tailwindcss/forms':
specifier: ^0.5.7
version: 0.5.7(tailwindcss@3.4.4(ts-node@10.9.2(@types/node@20.10.5)(typescript@5.4.5)))
version: 0.5.7(tailwindcss@3.4.10(ts-node@10.9.2(@types/node@20.10.5)(typescript@5.4.5)))
'@testing-library/jest-dom':
specifier: ^6.4.6
version: 6.4.6(@jest/globals@29.7.0)(@types/jest@29.5.12)(jest@29.7.0(@types/node@20.10.5)(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@types/node@20.10.5)(typescript@5.4.5)))(vitest@1.5.3(@types/node@20.10.5)(jsdom@20.0.3))
@@ -672,8 +672,8 @@ importers:
specifier: ^29.5.12
version: 29.5.12
'@types/lodash':
specifier: ^4.17.5
version: 4.17.5
specifier: ^4.17.7
version: 4.17.7
'@types/node':
specifier: 20.10.5
version: 20.10.5
@@ -726,8 +726,8 @@ importers:
specifier: ^0.6.6
version: 0.6.6(prettier@3.2.5)
tailwindcss:
specifier: ^3.4.4
version: 3.4.4(ts-node@10.9.2(@types/node@20.10.5)(typescript@5.4.5))
specifier: ^3.4.10
version: 3.4.10(ts-node@10.9.2(@types/node@20.10.5)(typescript@5.4.5))
ts-node:
specifier: ^10.9.2
version: 10.9.2(@types/node@20.10.5)(typescript@5.4.5)
@@ -735,8 +735,8 @@ importers:
specifier: ^4.2.0
version: 4.2.0
tsx:
specifier: ^4.18.0
version: 4.18.0
specifier: ^4.19.0
version: 4.19.0
typescript:
specifier: ^5.4.5
version: 5.4.5
@@ -843,8 +843,8 @@ importers:
specifier: ^10.2.0
version: 10.2.0
pino-pretty:
specifier: ^10.3.1
version: 10.3.1
specifier: ^11.2.2
version: 11.2.2
stripe:
specifier: ^16.8.0
version: 16.8.0
@@ -874,8 +874,8 @@ importers:
specifier: ^4.19.3
version: 4.19.3
'@types/lodash':
specifier: ^4.17.5
version: 4.17.5
specifier: ^4.17.7
version: 4.17.7
'@types/node':
specifier: ^20.11.19
version: 20.11.29
@@ -904,8 +904,8 @@ importers:
specifier: ^0.11.0
version: 0.11.0(kysely@0.27.4)(pg@8.11.5)
msw:
specifier: ^2.3.1
version: 2.3.1(typescript@5.4.5)
specifier: ^2.4.1
version: 2.4.1(graphql@16.9.0)(typescript@5.4.5)
nodemon:
specifier: ^3.1.3
version: 3.1.3
@@ -919,8 +919,8 @@ importers:
specifier: ^6.2.0
version: 6.2.0(typescript@5.4.5)
tsx:
specifier: ^4.18.0
version: 4.18.0
specifier: ^4.19.0
version: 4.19.0
typescript:
specifier: ^5.4.5
version: 5.4.5
@@ -1399,6 +1399,9 @@ packages:
'@bundled-es-modules/statuses@1.0.1':
resolution: {integrity: sha512-yn7BklA5acgcBr+7w064fGV+SGIFySjCKpqjcWgBAIfrAkY+4GQTJJHQMeT3V/sgz23VTEVV8TtOmkvJAhFVfg==}
'@bundled-es-modules/tough-cookie@0.1.6':
resolution: {integrity: sha512-dvMHbL464C0zI+Yqxbz6kZ5TOEp7GLW+pry/RWndAR8MJQAXZ2rPmIs8tziTZjeIyhSNZgZbCePtfSbdWqStJw==}
'@chevrotain/cst-dts-gen@10.5.0':
resolution: {integrity: sha512-lhmC/FyqQ2o7pGK4Om+hzuDrm9rhFYIJ/AXoQBeongmn870Xeb0L6oGEiuR8nohFNL5sMaQEJWCxr1oIVIVXrw==}
@@ -2233,10 +2236,6 @@ packages:
cpu: [x64]
os: [win32]
'@mswjs/cookies@1.1.0':
resolution: {integrity: sha512-0ZcCVQxifZmhwNBoQIrystCb+2sWBY2Zw8lpfJBPCHGCA/HWqehITeCRVIv4VMy8MPlaHo2w2pTHFV2pFfqKPw==}
engines: {node: '>=18'}
'@mswjs/interceptors@0.29.1':
resolution: {integrity: sha512-3rDakgJZ77+RiQUuSK69t1F0m8BQKA8Vh5DCS5V0DWvNY67zob2JhhQrhCO0AKLGINTRSFd1tBaHcJTkhefoSw==}
engines: {node: '>=18'}
@@ -4882,8 +4881,8 @@ packages:
'@types/koa__router@12.0.3':
resolution: {integrity: sha512-5YUJVv6NwM1z7m6FuYpKfNLTZ932Z6EF6xy2BbtpJSyn13DKNQEkXVffFVSnJHxvwwWh2SAeumpjAYUELqgjyw==}
'@types/lodash@4.17.5':
resolution: {integrity: sha512-MBIOHVZqVqgfro1euRDWX7OO0fBVUUMrN6Pwm8LQsz8cWhEpihlvR70ENj3f40j58TNxZaWv2ndSkInykNBBJw==}
'@types/lodash@4.17.7':
resolution: {integrity: sha512-8wTvZawATi/lsmNu10/j2hk1KEP0IvjubqPE3cu1Xz7xfXXt5oCq3SNUz4fMIP4XGF9Ky+Ue2tBA3hcS7LSBlA==}
'@types/mdast@4.0.4':
resolution: {integrity: sha512-kGaNbPh1k7AFzgpud/gMdvIm5xuECykRR+JnWKQno9TAXVa6WIVCGTPvYGekIDL4uwCZQSYbUxNBSb1aUo79oA==}
@@ -6638,10 +6637,6 @@ packages:
engines: {node: '>=18'}
hasBin: true
escalade@3.1.2:
resolution: {integrity: sha512-ErCHMCae19vR8vQGe50xIsVomy19rg6gFu3+r3jkEO46suLMWBksvVyoGgQV+jOfl84ZSOSlmv6Gxa89PmTGmA==}
engines: {node: '>=6'}
escalade@3.2.0:
resolution: {integrity: sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==}
engines: {node: '>=6'}
@@ -7245,9 +7240,6 @@ packages:
resolution: {integrity: sha512-g0QYk1dZBxGwk+Ngc+ltRH2IBp2f7zBkBMBJZCDerh6EhlhSR6+9irMCuT/09zD6qkarHUSn529sK/yL4S27mg==}
engines: {node: '>= 0.4'}
get-tsconfig@4.7.6:
resolution: {integrity: sha512-ZAqrLlu18NbDdRaHq+AKXzAmqIUPswPWKUchfytdAjiRFnCe5ojG2bstg6mRiZabkKfCoL/e98pbBELIV/YCeA==}
get-tsconfig@4.8.0:
resolution: {integrity: sha512-Pgba6TExTZ0FJAn1qkJAjIeKoDJ3CsI2ChuLohJnZl/tTU8MVrq3b+2t5UOPfRa4RMsorClBjJALkJUMjG1PAw==}
@@ -8551,8 +8543,8 @@ packages:
resolution: {integrity: sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA==}
engines: {node: '>=12'}
lucide-react@0.429.0:
resolution: {integrity: sha512-548DahFy7Ey+0OPlOyZ6ipnbGJzewv8gq2DxDrnAzWZ4RN4+3XyIwQXhD4AQvuREFjS1EnbAgOMaYB0VQyaK1g==}
lucide-react@0.436.0:
resolution: {integrity: sha512-N292bIxoqm1aObAg0MzFtvhYwgQE6qnIOWx/GLj5ONgcTPH6N0fD9bVq/GfdeC9ZORBXozt/XeEKDpiB3x3vlQ==}
peerDependencies:
react: ^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0-rc
@@ -8900,13 +8892,16 @@ packages:
msgpackr@1.10.1:
resolution: {integrity: sha512-r5VRLv9qouXuLiIBrLpl2d5ZvPt8svdQTl5/vMvE4nzDMyEX4sgW5yWhuBBj5UmgwOTWj8CIdSXn5sAfsHAWIQ==}
msw@2.3.1:
resolution: {integrity: sha512-ocgvBCLn/5l3jpl1lssIb3cniuACJLoOfZu01e3n5dbJrpA5PeeWn28jCLgQDNt6d7QT8tF2fYRzm9JoEHtiig==}
msw@2.4.1:
resolution: {integrity: sha512-HXcoQPzYTwEmVk+BGIcRa0vLabBT+J20SSSeYh/QfajaK5ceA6dlD4ZZjfz2dqGEq4vRNCPLP6eXsB94KllPFg==}
engines: {node: '>=18'}
hasBin: true
peerDependencies:
graphql: '>= 16.8.x'
typescript: '>= 4.7.x'
peerDependenciesMeta:
graphql:
optional: true
typescript:
optional: true
@@ -9467,17 +9462,14 @@ packages:
resolution: {integrity: sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==}
engines: {node: '>=0.10.0'}
pino-abstract-transport@1.1.0:
resolution: {integrity: sha512-lsleG3/2a/JIWUtf9Q5gUNErBqwIu1tUKTT3dUzaf5DySw9ra1wcqKjJjLX1VTY64Wk1eEOYsVGSaGfCK85ekA==}
pino-abstract-transport@1.2.0:
resolution: {integrity: sha512-Guhh8EZfPCfH+PMXAb6rKOjGQEoy0xlAIn+irODG5kgfYV+BQ0rGYYWTIel3P5mmyXqkYkPmdIkywsn6QKUR1Q==}
pino-http@10.2.0:
resolution: {integrity: sha512-am03BxnV3Ckx68OkbH0iZs3indsrH78wncQ6w1w51KroIbvJZNImBKX2X1wjdY8lSyaJ0UrX/dnO2DY3cTeCRw==}
pino-pretty@10.3.1:
resolution: {integrity: sha512-az8JbIYeN/1iLj2t0jR9DV48/LQ3RC6hZPpapKPkb84Q+yTidMCpgWxIT3N0flnBDilyBQ1luWNpOeJptjdp/g==}
pino-pretty@11.2.2:
resolution: {integrity: sha512-2FnyGir8nAJAqD3srROdrF1J5BIcMT4nwj7hHSc60El6Uxlym00UbCCd8pYIterstVBFlMyF1yFV8XdGIPbj4A==}
hasBin: true
pino-std-serializers@7.0.0:
@@ -10407,9 +10399,6 @@ packages:
solid-js: ^1.2
swr-store: ^0.10
sonic-boom@3.8.0:
resolution: {integrity: sha512-ybz6OYOUjoQQCQ/i4LU8kaToD8ACtYP+Cj5qd2AO36bwbdewxWJ3ArmJ2cr6AvxlL2o0PqnCcPGUgkILbfkaCA==}
sonic-boom@4.0.1:
resolution: {integrity: sha512-hTSD/6JMLyT4r9zeof6UtuBDpjJ9sO08/nmS5djaA9eozT9oOlNdpXSnzcgj4FTqpk3nkLrs61l4gip9r1HCrQ==}
@@ -10715,8 +10704,8 @@ packages:
peerDependencies:
tailwindcss: '>=3.0.0 || insiders'
tailwindcss@3.4.4:
resolution: {integrity: sha512-ZoyXOdJjISB7/BcLTR6SEsLgKtDStYyYZVLsUtWChO4Ps20CBad7lfJKVDiejocV4ME1hLmyY0WJE3hSDcmQ2A==}
tailwindcss@3.4.10:
resolution: {integrity: sha512-KWZkVPm7yJRhdu4SRSl9d4AK2wM3a50UsvgHZO7xY77NQr2V+fIrEuoDGQcbvswWvFGbS2f6e+jC/6WJm1Dl0w==}
engines: {node: '>=14.0.0'}
hasBin: true
@@ -10835,6 +10824,10 @@ packages:
resolution: {integrity: sha512-aX/y5pVRkfRnfmuX+OdbSdXvPe6ieKX/G2s7e98f4poJHnqH3281gDPm/metm6E/WRamfx7WC4HUqkWHfQHprw==}
engines: {node: '>=6'}
tough-cookie@4.1.4:
resolution: {integrity: sha512-Loo5UUvLD9ScZ6jh8beX1T6sO1w2/MpCRpEP7V280GKMVUQ0Jzar2U3UJPsrdbziLEMMhu3Ujnq//rhiFuIeag==}
engines: {node: '>=6'}
tr46@0.0.3:
resolution: {integrity: sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==}
@@ -10903,8 +10896,8 @@ packages:
peerDependencies:
typescript: '>=2.8.0 || >= 3.2.0-dev || >= 3.3.0-dev || >= 3.4.0-dev || >= 3.5.0-dev || >= 3.6.0-dev || >= 3.6.0-beta || >= 3.7.0-dev || >= 3.7.0-beta'
tsx@4.18.0:
resolution: {integrity: sha512-a1jaKBSVQkd6yEc1/NI7G6yHFfefIcuf3QJST7ZEyn4oQnxLYrZR5uZAM8UrwUa3Ge8suiZHcNS1gNrEvmobqg==}
tsx@4.19.0:
resolution: {integrity: sha512-bV30kM7bsLZKZIOCHeMNVMJ32/LuJzLVajkQI/qf92J2Qr08ueLQvW00PUZGiuLPP760UINwupgUj8qrSCPUKg==}
engines: {node: '>=18.0.0'}
hasBin: true
@@ -11479,11 +11472,6 @@ packages:
resolution: {integrity: sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==}
engines: {node: '>= 6'}
yaml@2.4.1:
resolution: {integrity: sha512-pIXzoImaqmfOrL7teGUBt/T7ZDnyeGBWyXQBvOVhLkWLN37GXv8NMLK406UY6dS51JfcQHsmcW5cJ441bHg6Lg==}
engines: {node: '>= 14'}
hasBin: true
yaml@2.4.5:
resolution: {integrity: sha512-aBx2bnqDzVOyNKfsysjA2ms5ZlnjSAW2eG3/L5G/CSujfjLJTJsEw1bGw8kCf04KodQWk1pxlGnZ56CRxiawmg==}
engines: {node: '>= 14'}
@@ -12422,6 +12410,11 @@ snapshots:
dependencies:
statuses: 2.0.1
'@bundled-es-modules/tough-cookie@0.1.6':
dependencies:
'@types/tough-cookie': 4.0.5
tough-cookie: 4.1.4
'@chevrotain/cst-dts-gen@10.5.0':
dependencies:
'@chevrotain/gast': 10.5.0
@@ -12936,9 +12929,9 @@ snapshots:
react: 18.2.0
react-dom: 18.2.0(react@18.2.0)
'@headlessui/tailwindcss@0.2.1(tailwindcss@3.4.4(ts-node@10.9.2(@types/node@20.10.5)(typescript@5.4.5)))':
'@headlessui/tailwindcss@0.2.1(tailwindcss@3.4.10(ts-node@10.9.2(@types/node@20.10.5)(typescript@5.4.5)))':
dependencies:
tailwindcss: 3.4.4(ts-node@10.9.2(@types/node@20.10.5)(typescript@5.4.5))
tailwindcss: 3.4.10(ts-node@10.9.2(@types/node@20.10.5)(typescript@5.4.5))
'@heroicons/react@2.1.3(react@18.2.0)':
dependencies:
@@ -13211,7 +13204,7 @@ snapshots:
'@jridgewell/gen-mapping@0.3.5':
dependencies:
'@jridgewell/set-array': 1.2.1
'@jridgewell/sourcemap-codec': 1.4.15
'@jridgewell/sourcemap-codec': 1.5.0
'@jridgewell/trace-mapping': 0.3.25
'@jridgewell/resolve-uri@3.1.2': {}
@@ -13230,7 +13223,7 @@ snapshots:
'@jridgewell/trace-mapping@0.3.25':
dependencies:
'@jridgewell/resolve-uri': 3.1.2
'@jridgewell/sourcemap-codec': 1.4.15
'@jridgewell/sourcemap-codec': 1.5.0
'@jridgewell/trace-mapping@0.3.9':
dependencies:
@@ -13549,8 +13542,6 @@ snapshots:
'@msgpackr-extract/msgpackr-extract-win32-x64@3.0.3':
optional: true
'@mswjs/cookies@1.1.0': {}
'@mswjs/interceptors@0.29.1':
dependencies:
'@open-draft/deferred-promise': 2.2.0
@@ -16527,14 +16518,14 @@ snapshots:
optionalDependencies:
typescript: 5.4.5
'@tailwindcss/container-queries@0.1.1(tailwindcss@3.4.4(ts-node@10.9.2(@types/node@20.10.5)(typescript@5.4.5)))':
'@tailwindcss/container-queries@0.1.1(tailwindcss@3.4.10(ts-node@10.9.2(@types/node@20.10.5)(typescript@5.4.5)))':
dependencies:
tailwindcss: 3.4.4(ts-node@10.9.2(@types/node@20.10.5)(typescript@5.4.5))
tailwindcss: 3.4.10(ts-node@10.9.2(@types/node@20.10.5)(typescript@5.4.5))
'@tailwindcss/forms@0.5.7(tailwindcss@3.4.4(ts-node@10.9.2(@types/node@20.10.5)(typescript@5.4.5)))':
'@tailwindcss/forms@0.5.7(tailwindcss@3.4.10(ts-node@10.9.2(@types/node@20.10.5)(typescript@5.4.5)))':
dependencies:
mini-svg-data-uri: 1.4.4
tailwindcss: 3.4.4(ts-node@10.9.2(@types/node@20.10.5)(typescript@5.4.5))
tailwindcss: 3.4.10(ts-node@10.9.2(@types/node@20.10.5)(typescript@5.4.5))
'@tanstack/query-core@4.36.1': {}
@@ -16603,11 +16594,11 @@ snapshots:
'@tootallnate/quickjs-emscripten@0.23.0': {}
'@tremor/react@3.16.2(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(tailwindcss@3.4.4(ts-node@10.9.2(@types/node@20.10.5)(typescript@5.4.5)))':
'@tremor/react@3.16.2(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(tailwindcss@3.4.10(ts-node@10.9.2(@types/node@20.10.5)(typescript@5.4.5)))':
dependencies:
'@floating-ui/react': 0.19.2(react-dom@18.2.0(react@18.2.0))(react@18.2.0)
'@headlessui/react': 1.7.19(react-dom@18.2.0(react@18.2.0))(react@18.2.0)
'@headlessui/tailwindcss': 0.2.1(tailwindcss@3.4.4(ts-node@10.9.2(@types/node@20.10.5)(typescript@5.4.5)))
'@headlessui/tailwindcss': 0.2.1(tailwindcss@3.4.10(ts-node@10.9.2(@types/node@20.10.5)(typescript@5.4.5)))
date-fns: 2.30.0
react: 18.2.0
react-day-picker: 8.10.1(date-fns@2.30.0)(react@18.2.0)
@@ -16877,7 +16868,7 @@ snapshots:
dependencies:
'@types/koa': 2.14.0
'@types/lodash@4.17.5': {}
'@types/lodash@4.17.7': {}
'@types/mdast@4.0.4':
dependencies:
@@ -19044,8 +19035,6 @@ snapshots:
'@esbuild/win32-ia32': 0.23.1
'@esbuild/win32-x64': 0.23.1
escalade@3.1.2: {}
escalade@3.2.0: {}
escape-goat@4.0.0: {}
@@ -19127,7 +19116,7 @@ snapshots:
eslint-module-utils: 2.8.1(@typescript-eslint/parser@7.12.0(eslint@8.57.0)(typescript@5.4.5))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.1(@typescript-eslint/parser@7.12.0(eslint@8.57.0)(typescript@5.4.5))(eslint-plugin-import@2.29.1)(eslint@8.57.0))(eslint@8.57.0)
eslint-plugin-import: 2.29.1(@typescript-eslint/parser@7.12.0(eslint@8.57.0)(typescript@5.4.5))(eslint-import-resolver-typescript@3.6.1)(eslint@8.57.0)
fast-glob: 3.3.2
get-tsconfig: 4.7.6
get-tsconfig: 4.8.0
is-core-module: 2.15.1
is-glob: 4.0.3
transitivePeerDependencies:
@@ -19144,7 +19133,7 @@ snapshots:
eslint-module-utils: 2.8.1(@typescript-eslint/parser@7.2.0(eslint@8.57.0)(typescript@5.4.5))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.1(@typescript-eslint/parser@7.2.0(eslint@8.57.0)(typescript@5.4.5))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.29.1)(eslint@8.57.0))(eslint@8.57.0)
eslint-plugin-import: 2.29.1(@typescript-eslint/parser@7.12.0(eslint@8.57.0)(typescript@5.4.5))(eslint-import-resolver-typescript@3.6.1)(eslint@8.57.0)
fast-glob: 3.3.2
get-tsconfig: 4.7.6
get-tsconfig: 4.8.0
is-core-module: 2.15.1
is-glob: 4.0.3
transitivePeerDependencies:
@@ -19805,10 +19794,6 @@ snapshots:
es-errors: 1.3.0
get-intrinsic: 1.2.4
get-tsconfig@4.7.6:
dependencies:
resolve-pkg-maps: 1.0.0
get-tsconfig@4.8.0:
dependencies:
resolve-pkg-maps: 1.0.0
@@ -21426,7 +21411,7 @@ snapshots:
lru-cache@7.18.3: {}
lucide-react@0.429.0(react@18.2.0):
lucide-react@0.436.0(react@18.2.0):
dependencies:
react: 18.2.0
@@ -21981,18 +21966,17 @@ snapshots:
optionalDependencies:
msgpackr-extract: 3.0.3
msw@2.3.1(typescript@5.4.5):
msw@2.4.1(graphql@16.9.0)(typescript@5.4.5):
dependencies:
'@bundled-es-modules/cookie': 2.0.0
'@bundled-es-modules/statuses': 1.0.1
'@bundled-es-modules/tough-cookie': 0.1.6
'@inquirer/confirm': 3.1.6
'@mswjs/cookies': 1.1.0
'@mswjs/interceptors': 0.29.1
'@open-draft/until': 2.1.0
'@types/cookie': 0.6.0
'@types/statuses': 2.0.5
chalk: 4.1.2
graphql: 16.9.0
headers-polyfill: 4.0.3
is-node-process: 1.2.0
outvariant: 1.4.2
@@ -22001,6 +21985,7 @@ snapshots:
type-fest: 4.18.2
yargs: 17.7.2
optionalDependencies:
graphql: 16.9.0
typescript: 5.4.5
mustache@4.2.0: {}
@@ -22603,11 +22588,6 @@ snapshots:
pify@2.3.0: {}
pino-abstract-transport@1.1.0:
dependencies:
readable-stream: 4.5.2
split2: 4.2.0
pino-abstract-transport@1.2.0:
dependencies:
readable-stream: 4.5.2
@@ -22620,7 +22600,7 @@ snapshots:
pino-std-serializers: 7.0.0
process-warning: 3.0.0
pino-pretty@10.3.1:
pino-pretty@11.2.2:
dependencies:
colorette: 2.0.20
dateformat: 4.6.3
@@ -22630,11 +22610,11 @@ snapshots:
joycon: 3.1.1
minimist: 1.2.8
on-exit-leak-free: 2.1.2
pino-abstract-transport: 1.1.0
pino-abstract-transport: 1.2.0
pump: 3.0.0
readable-stream: 4.5.2
secure-json-parse: 2.7.0
sonic-boom: 3.8.0
sonic-boom: 4.0.1
strip-json-comments: 3.1.1
pino-std-serializers@7.0.0: {}
@@ -22677,29 +22657,29 @@ snapshots:
possible-typed-array-names@1.0.0: {}
postcss-import@15.1.0(postcss@8.4.38):
postcss-import@15.1.0(postcss@8.4.41):
dependencies:
postcss: 8.4.38
postcss: 8.4.41
postcss-value-parser: 4.2.0
read-cache: 1.0.0
resolve: 1.22.8
postcss-js@4.0.1(postcss@8.4.38):
postcss-js@4.0.1(postcss@8.4.41):
dependencies:
camelcase-css: 2.0.1
postcss: 8.4.38
postcss: 8.4.41
postcss-load-config@4.0.2(postcss@8.4.38)(ts-node@10.9.2(@types/node@20.10.5)(typescript@5.4.5)):
postcss-load-config@4.0.2(postcss@8.4.41)(ts-node@10.9.2(@types/node@20.10.5)(typescript@5.4.5)):
dependencies:
lilconfig: 3.1.1
yaml: 2.4.1
yaml: 2.4.5
optionalDependencies:
postcss: 8.4.38
postcss: 8.4.41
ts-node: 10.9.2(@types/node@20.10.5)(typescript@5.4.5)
postcss-nested@6.0.1(postcss@8.4.38):
postcss-nested@6.0.1(postcss@8.4.41):
dependencies:
postcss: 8.4.38
postcss: 8.4.41
postcss-selector-parser: 6.0.16
postcss-selector-parser@6.0.16:
@@ -23669,10 +23649,6 @@ snapshots:
solid-js: 1.8.18
swr-store: 0.10.6
sonic-boom@3.8.0:
dependencies:
atomic-sleep: 1.0.0
sonic-boom@4.0.1:
dependencies:
atomic-sleep: 1.0.0
@@ -23906,7 +23882,7 @@ snapshots:
dependencies:
'@jridgewell/gen-mapping': 0.3.5
commander: 4.1.1
glob: 10.3.12
glob: 10.4.5
lines-and-columns: 1.2.4
mz: 2.7.0
pirates: 4.0.6
@@ -23985,11 +23961,11 @@ snapshots:
tailwind-merge@2.5.2: {}
tailwindcss-animate@1.0.7(tailwindcss@3.4.4(ts-node@10.9.2(@types/node@20.10.5)(typescript@5.4.5))):
tailwindcss-animate@1.0.7(tailwindcss@3.4.10(ts-node@10.9.2(@types/node@20.10.5)(typescript@5.4.5))):
dependencies:
tailwindcss: 3.4.4(ts-node@10.9.2(@types/node@20.10.5)(typescript@5.4.5))
tailwindcss: 3.4.10(ts-node@10.9.2(@types/node@20.10.5)(typescript@5.4.5))
tailwindcss@3.4.4(ts-node@10.9.2(@types/node@20.10.5)(typescript@5.4.5)):
tailwindcss@3.4.10(ts-node@10.9.2(@types/node@20.10.5)(typescript@5.4.5)):
dependencies:
'@alloc/quick-lru': 5.2.0
arg: 5.0.2
@@ -24005,11 +23981,11 @@ snapshots:
normalize-path: 3.0.0
object-hash: 3.0.0
picocolors: 1.0.1
postcss: 8.4.38
postcss-import: 15.1.0(postcss@8.4.38)
postcss-js: 4.0.1(postcss@8.4.38)
postcss-load-config: 4.0.2(postcss@8.4.38)(ts-node@10.9.2(@types/node@20.10.5)(typescript@5.4.5))
postcss-nested: 6.0.1(postcss@8.4.38)
postcss: 8.4.41
postcss-import: 15.1.0(postcss@8.4.41)
postcss-js: 4.0.1(postcss@8.4.41)
postcss-load-config: 4.0.2(postcss@8.4.41)(ts-node@10.9.2(@types/node@20.10.5)(typescript@5.4.5))
postcss-nested: 6.0.1(postcss@8.4.41)
postcss-selector-parser: 6.0.16
resolve: 1.22.8
sucrase: 3.35.0
@@ -24127,6 +24103,13 @@ snapshots:
universalify: 0.2.0
url-parse: 1.5.10
tough-cookie@4.1.4:
dependencies:
psl: 1.9.0
punycode: 2.3.1
universalify: 0.2.0
url-parse: 1.5.10
tr46@0.0.3: {}
tr46@3.0.0:
@@ -24213,10 +24196,10 @@ snapshots:
tslib: 1.14.1
typescript: 5.4.5
tsx@4.18.0:
tsx@4.19.0:
dependencies:
esbuild: 0.23.1
get-tsconfig: 4.7.6
get-tsconfig: 4.8.0
optionalDependencies:
fsevents: 2.3.3
@@ -24401,7 +24384,7 @@ snapshots:
update-browserslist-db@1.0.13(browserslist@4.23.0):
dependencies:
browserslist: 4.23.0
escalade: 3.1.2
escalade: 3.2.0
picocolors: 1.0.1
update-browserslist-db@1.1.0(browserslist@4.23.3):
@@ -24872,8 +24855,6 @@ snapshots:
yaml@1.10.2: {}
yaml@2.4.1: {}
yaml@2.4.5: {}
yargs-parser@21.1.1: {}
@@ -24881,7 +24862,7 @@ snapshots:
yargs@17.7.1:
dependencies:
cliui: 8.0.1
escalade: 3.1.2
escalade: 3.2.0
get-caller-file: 2.0.5
require-directory: 2.1.1
string-width: 4.2.3
@@ -24891,7 +24872,7 @@ snapshots:
yargs@17.7.2:
dependencies:
cliui: 8.0.1
escalade: 3.1.2
escalade: 3.2.0
get-caller-file: 2.0.5
require-directory: 2.1.1
string-width: 4.2.3
+31 -26
View File
@@ -149,39 +149,44 @@ const nextConfig = {
},
};
const sentryOptions = {
// Additional config options for the Sentry Webpack plugin. Keep in mind that
// the following options are set automatically, and overriding them is not
// recommended:
// release, url, authToken, configFile, stripPrefix,
// urlPrefix, include, ignore
export default withSentryConfig(nextConfig, {
// For all available options, see:
// https://github.com/getsentry/sentry-webpack-plugin#options
org: process.env.SENTRY_ORG,
project: process.env.SENTRY_PROJECT,
silent: true, // Suppresses all logs
authToken: env.SENTRY_AUTH_TOKEN,
// Only print logs for uploading source maps in CI
silent: !process.env.CI,
// For all available options, see:
// https://github.com/getsentry/sentry-webpack-plugin#options.
// https://docs.sentry.io/platforms/javascript/guides/nextjs/manual-setup/
// See the sections below for information on the following options:
// 'Configure Source Maps':
// - disableServerWebpackPlugin
// - disableClientWebpackPlugin
// - hideSourceMaps
hideSourceMaps: true,
// - widenClientFileUpload
// 'Configure Legacy Browser Support':
// - transpileClientSDK
// 'Configure Serverside Auto-instrumentation':
// - autoInstrumentServerFunctions
// - excludeServerRoutes
// 'Configure Tunneling':
// - tunnelRoute
// Upload a larger set of source maps for prettier stack traces (increases build time)
widenClientFileUpload: true,
// An auth token is required for uploading source maps.
authToken: env.SENTRY_AUTH_TOKEN,
// Automatically annotate React components to show their full name in breadcrumbs and session replay
reactComponentAnnotation: {
enabled: true,
},
// Route browser requests to Sentry through a Next.js rewrite to circumvent ad-blockers.
// This can increase your server load as well as your hosting bill.
// Note: Check that the configured route will not match with your Next.js middleware, otherwise reporting of client-
// side errors will fail.
tunnelRoute: "/api/monitoring-tunnel",
};
export default withSentryConfig(nextConfig, sentryOptions);
// Hides source maps from generated client bundles
hideSourceMaps: true,
// Automatically tree-shake Sentry logger statements to reduce bundle size
disableLogger: true,
// Enables automatic instrumentation of Vercel Cron Monitors. (Does not yet work with App Router route handlers.)
// See the following for more information:
// https://docs.sentry.io/product/crons/
// https://vercel.com/docs/cron-jobs
automaticVercelMonitors: false,
});
+5 -5
View File
@@ -1,6 +1,6 @@
{
"name": "web",
"version": "2.75.2",
"version": "2.76.0",
"private": true,
"license": "MIT",
"engines": {
@@ -110,7 +110,7 @@
"kysely": "^0.27.4",
"langchain": "^0.2.6",
"lodash": "^4.17.21",
"lucide-react": "^0.429.0",
"lucide-react": "^0.436.0",
"next": "^14.2.6",
"next-auth": "^4.24.7",
"next-query-params": "^5.0.0",
@@ -154,7 +154,7 @@
"@types/dompurify": "^3.0.5",
"@types/eslint": "^8.56.7",
"@types/jest": "^29.5.12",
"@types/lodash": "^4.17.5",
"@types/lodash": "^4.17.7",
"@types/node": "20.10.5",
"@types/react": "^18.2.79",
"@types/react-dom": "^18.2.25",
@@ -172,10 +172,10 @@
"postcss": "^8.4.38",
"prettier": "^3.2.5",
"prettier-plugin-tailwindcss": "^0.6.6",
"tailwindcss": "^3.4.4",
"tailwindcss": "^3.4.10",
"ts-node": "^10.9.2",
"tsconfig-paths": "^4.2.0",
"tsx": "^4.18.0",
"tsx": "^4.19.0",
"typescript": "^5.4.5",
"wait-for-expect": "^3.0.2"
},
+161
View File
@@ -548,6 +548,167 @@ describe("Authenticate API calls", () => {
});
});
describe("invalidates api keys in redis", () => {
const redis = new Redis("redis://:myredissecret@127.0.0.1:6379", {
maxRetriesPerRequest: null,
});
beforeEach(async () => {
// if we do not remove the key, it will remain in the cache and
// calling the test twice will not add the key to the cache
const keys = await redis.keys("api-key*");
console.log("before each deleting keys", keys);
if (keys.length > 0) {
console.log("before each deleting keys. actually deleting", keys);
await redis.del(keys);
}
});
afterEach(async () => {
// if we do not remove the key, it will remain in the cache and
// calling the test twice will not add the key to the cache
const keys = await redis.keys("api-key*");
console.log("after each deleting keys", keys);
if (keys.length > 0) {
await redis.del(keys);
}
});
afterAll(async () => {
redis.disconnect();
});
it("should invalidate organization API keys in redis", async () => {
await createAPIKey();
// put keys into cache
await new ApiAuthService(prisma, redis).verifyAuthHeaderAndReturnScope(
"Basic cGstbGYtMTIzNDU2Nzg5MDpzay1sZi0xMjM0NTY3ODkw",
);
await new ApiAuthService(prisma, redis).verifyAuthHeaderAndReturnScope(
"Basic cGstbGYtMTIzNDU2Nzg5MDpzay1sZi0xMjM0NTY3ODkw",
);
const apiKey = await prisma.apiKey.findUnique({
where: { publicKey: "pk-lf-1234567890" },
});
expect(apiKey).not.toBeNull();
const cachedKey = await redis.get(
`api-key:${apiKey?.fastHashedSecretKey}`,
);
expect(cachedKey).not.toBeNull();
await new ApiAuthService(prisma, redis).invalidateOrgApiKeys(
"seed-org-id",
);
const invalidatedCachedKey = await redis.get(
`api-key:${apiKey?.fastHashedSecretKey}`,
);
expect(invalidatedCachedKey).toBeNull();
});
it("if no keys in redis, invalidating org keys should do nothing", async () => {
await createAPIKey();
await prisma.apiKey.update({
where: { publicKey: "pk-lf-1234567890" },
data: {
fastHashedSecretKey: Math.random().toString(36).substring(2, 15),
},
});
await new ApiAuthService(prisma, redis).invalidateOrgApiKeys(
"seed-org-id",
);
const keys = await redis.keys("api-key*");
expect(keys.length).toBe(0);
});
it("if no keys in redis, invalidating org keys without fast hash should do nothing", async () => {
await createAPIKey();
await new ApiAuthService(prisma, redis).invalidateOrgApiKeys(
"seed-org-id",
);
const keys = await redis.keys("api-key*");
expect(keys.length).toBe(0);
});
it("should invalidate project API keys in redis", async () => {
await createAPIKey();
// put keys into cache
await new ApiAuthService(prisma, redis).verifyAuthHeaderAndReturnScope(
"Basic cGstbGYtMTIzNDU2Nzg5MDpzay1sZi0xMjM0NTY3ODkw",
);
await new ApiAuthService(prisma, redis).verifyAuthHeaderAndReturnScope(
"Basic cGstbGYtMTIzNDU2Nzg5MDpzay1sZi0xMjM0NTY3ODkw",
);
const apiKey = await prisma.apiKey.findUnique({
where: { publicKey: "pk-lf-1234567890" },
});
expect(apiKey).not.toBeNull();
const cachedKey = await redis.get(
`api-key:${apiKey?.fastHashedSecretKey}`,
);
expect(cachedKey).not.toBeNull();
await new ApiAuthService(prisma, redis).invalidateProjectApiKeys(
"7a88fb47-b4e2-43b8-a06c-a5ce950dc53a",
);
const invalidatedCachedKey = await redis.get(
`api-key:${apiKey?.fastHashedSecretKey}`,
);
expect(invalidatedCachedKey).toBeNull();
});
it("if no keys in redis, invalidating project keys should do nothing", async () => {
await createAPIKey();
await prisma.apiKey.update({
where: { publicKey: "pk-lf-1234567890" },
data: {
fastHashedSecretKey: Math.random().toString(36).substring(2, 15),
},
});
await new ApiAuthService(prisma, redis).invalidateProjectApiKeys(
"7a88fb47-b4e2-43b8-a06c-a5ce950dc53a",
);
const keys = await redis.keys("api-key*");
expect(keys.length).toBe(0);
});
it("if no keys in redis, invalidating project keys without fast hash should do nothing", async () => {
await createAPIKey();
await prisma.apiKey.update({
where: { publicKey: "pk-lf-1234567890" },
data: {
fastHashedSecretKey: Math.random().toString(36).substring(2, 15),
},
});
await new ApiAuthService(prisma, redis).invalidateProjectApiKeys(
"7a88fb47-b4e2-43b8-a06c-a5ce950dc53a",
);
const keys = await redis.keys("api-key*");
expect(keys.length).toBe(0);
});
});
const createAPIKey = async () => {
const seedApiKey = {
id: "seed-api-key",
@@ -186,7 +186,6 @@ export const ObservationPreview = (props: {
source="generation"
generation={observationWithInputAndOutput.data}
analyticsEventName="trace_detail:test_in_playground_button_click"
fullWidth
/>
)}
{observationWithInputAndOutput.data ? (
+1 -1
View File
@@ -1 +1 @@
export const VERSION = "v2.75.2";
export const VERSION = "v2.76.0";
@@ -83,7 +83,7 @@ const OrganizationUsageChart = () => {
<>
<Text>
{usage.data.billingPeriod
? `Observations in billing period`
? `Observations in current billing period`
: "Observations / last 30d"}
</Text>
<Metric>{numberFormatter(usage.data.countObservations, 0)}</Metric>
@@ -214,18 +214,6 @@ export const cloudBillingRouter = createTRPCRouter({
session: ctx.session,
});
const thirtyDaysAgo = new Date();
thirtyDaysAgo.setDate(thirtyDaysAgo.getDate() - 30);
thirtyDaysAgo.setHours(0, 0, 0, 0);
let billingPeriod: {
start: Date;
end: Date;
} | null = null;
let upcomingInvoice: {
usdAmount: number;
date: Date;
} | null = null;
const organization = await ctx.prisma.organization.findUnique({
where: {
id: input.orgId,
@@ -249,35 +237,49 @@ export const cloudBillingRouter = createTRPCRouter({
parsedOrg.cloudConfig.stripe.activeSubscriptionId,
);
if (subscription) {
billingPeriod = {
const billingPeriod = {
start: new Date(subscription.current_period_start * 1000),
end: new Date(subscription.current_period_end * 1000),
};
const stripeInvoice = await stripeClient.invoices.retrieveUpcoming({
subscription: parsedOrg.cloudConfig.stripe.activeSubscriptionId,
});
upcomingInvoice = {
const upcomingInvoice = {
usdAmount: stripeInvoice.amount_due / 100,
date: new Date(stripeInvoice.period_end * 1000),
};
const usage = stripeInvoice.lines.data.reduce((acc, line) => {
if (line.quantity) {
return acc + line.quantity;
}
return acc;
}, 0);
return {
countObservations: usage,
billingPeriod,
upcomingInvoice,
};
}
}
// For non-Stripe subscriptions, we can only get usage from the LangFuse API
const thirtyDaysAgo = new Date();
thirtyDaysAgo.setDate(thirtyDaysAgo.getDate() - 30);
thirtyDaysAgo.setHours(0, 0, 0, 0);
const usage = await ctx.prisma.observation.count({
where: {
project: {
orgId: input.orgId,
},
startTime: {
gte: billingPeriod?.start ?? thirtyDaysAgo,
gte: thirtyDaysAgo,
},
},
});
return {
countObservations: usage,
billingPeriod,
upcomingInvoice,
};
}),
});
@@ -32,7 +32,7 @@ type JumpToPlaygroundButtonProps = (
analyticsEventName: "trace_detail:test_in_playground_button_click";
}
) & {
fullWidth?: boolean;
variant?: "outline" | "secondary";
};
export const JumpToPlaygroundButton: React.FC<JumpToPlaygroundButtonProps> = (
@@ -61,17 +61,14 @@ export const JumpToPlaygroundButton: React.FC<JumpToPlaygroundButtonProps> = (
return (
<Button
variant={props.fullWidth ? "secondary" : "outline"}
variant={props.variant ?? "secondary"}
title="Test in LLM playground"
size={!props.fullWidth ? "icon" : undefined}
onClick={handleClick}
asChild
>
<Link href={`/project/${projectId}/playground`}>
<Terminal className="h-4 w-4" />
{props.fullWidth ? (
<span className="ml-2">Test in playground</span>
) : null}
<span className="ml-2">Test in playground</span>
</Link>
</Button>
);
@@ -150,7 +150,7 @@ export const EditDatasetItem = ({
field.onChange(v);
}}
editable={hasAccess}
className="max-h-[600px] overflow-y-auto"
className="max-h-[500px] overflow-y-auto"
/>
</FormControl>
<FormMessage />
@@ -171,7 +171,7 @@ export const EditDatasetItem = ({
field.onChange(v);
}}
editable={hasAccess}
className="max-h-[600px] overflow-y-auto"
className="max-h-[500px] overflow-y-auto"
/>
</FormControl>
<FormMessage />
@@ -193,7 +193,7 @@ export const EditDatasetItem = ({
field.onChange(v);
}}
editable={hasAccess}
className="max-h-[300px] overflow-y-auto"
className="max-h-[200px] overflow-y-auto"
/>
</FormControl>
<FormMessage />
@@ -83,8 +83,6 @@ export function SetPromptVersionLabels({ prompt }: { prompt: Prompt }) {
setIsOpen(false);
};
if (!hasAccess) return null;
return (
<Popover
key={prompt.id}
@@ -98,8 +96,10 @@ export function SetPromptVersionLabels({ prompt }: { prompt: Prompt }) {
<Button
variant="outline"
size="icon"
className="h-7 w-7 px-0"
aria-label="Set prompt labels"
title="Set prompt labels"
disabled={!hasAccess}
>
<TagIcon className="h-4 w-4" />
</Button>
@@ -160,7 +160,7 @@ export function SetPromptVersionLabels({ prompt }: { prompt: Prompt }) {
) : (
<Button
variant="ghost"
className="mt-2 w-full justify-start px-2 py-1 text-sm font-normal"
className="mt-2 w-full justify-start px-2 py-1 text-sm font-normal"
onClick={() => setIsAddingLabel(true)}
>
<PlusIcon className="mr-2 h-4 w-4" />
@@ -62,6 +62,7 @@ export function DeletePromptVersion({
variant="outline"
type="button"
size="icon"
className="h-7 w-7 px-0"
disabled={!hasAccess}
>
<Trash className="h-4 w-4" />
@@ -1,4 +1,3 @@
import { Pencil } from "lucide-react";
import Link from "next/link";
import { useRouter } from "next/router";
import { NumberParam, useQueryParam } from "use-query-params";
@@ -7,10 +6,8 @@ import Header from "@/src/components/layouts/header";
import { OpenAiMessageView } from "@/src/components/trace/IOPreview";
import { Tabs, TabsList, TabsTrigger } from "@/src/components/ui/tabs";
import { Badge } from "@/src/components/ui/badge";
import { Button } from "@/src/components/ui/button";
import { CodeView, JSONView } from "@/src/components/ui/CodeJsonViewer";
import { DetailPageNav } from "@/src/features/navigate-detail-pages/DetailPageNav";
import { DeletePromptVersion } from "@/src/features/prompts/components/delete-prompt-version";
import { PromptType } from "@/src/features/prompts/server/utils/validation";
import useProjectIdFromURL from "@/src/hooks/useProjectIdFromURL";
import { api } from "@/src/utils/api";
@@ -18,7 +15,6 @@ import { extractVariables } from "@/src/utils/string";
import { ScrollArea } from "@radix-ui/react-scroll-area";
import { TagPromptDetailsPopover } from "@/src/features/tag/components/TagPromptDetailsPopover";
import { PromptHistoryNode } from "./prompt-history";
import { SetPromptVersionLabels } from "@/src/features/prompts/components/SetPromptVersionLabels";
import Generations from "@/src/components/table/use-cases/generations";
import {
Accordion,
@@ -26,14 +22,12 @@ import {
AccordionItem,
AccordionTrigger,
} from "@/src/components/ui/accordion";
import { usePostHogClientCapture } from "@/src/features/posthog-analytics/usePostHogClientCapture";
import { JumpToPlaygroundButton } from "@/src/ee/features/playground/page/components/JumpToPlaygroundButton";
import { ChatMlArraySchema } from "@/src/components/schemas/ChatMlSchema";
import { CommentList } from "@/src/features/comments/CommentList";
export const PromptDetail = () => {
const projectId = useProjectIdFromURL();
const capture = usePostHogClientCapture();
const promptName = decodeURIComponent(useRouter().query.promptName as string);
const [currentPromptVersion, setCurrentPromptVersion] = useQueryParam(
"version",
@@ -107,31 +101,11 @@ export const PromptDetail = () => {
]}
actionButtons={
<>
<SetPromptVersionLabels prompt={prompt} />
<JumpToPlaygroundButton
source="prompt"
prompt={prompt}
analyticsEventName="prompt_detail:test_in_playground_button_click"
/>
<Button
variant="outline"
size="icon"
onClick={() => capture("prompts:update_form_open")}
asChild
>
<Link
href={`/project/${projectId}/prompts/new?promptId=${encodeURIComponent(prompt.id)}`}
>
<Pencil className="h-4 w-4" />
</Link>
</Button>
<DeletePromptVersion
promptVersionId={prompt.id}
version={prompt.version}
countVersions={promptHistory.data.totalCount}
/>
<DetailPageNav
key="nav"
@@ -242,6 +216,7 @@ export const PromptDetail = () => {
prompts={promptHistory.data.promptVersions}
currentPromptVersion={prompt.version}
setCurrentPromptVersion={setCurrentPromptVersion}
totalCount={promptHistory.data.totalCount}
/>
</ScrollArea>
</div>
@@ -1,7 +1,15 @@
import { StatusBadge } from "@/src/components/layouts/status-badge";
import { Button } from "@/src/components/ui/button";
import { usePostHogClientCapture } from "@/src/features/posthog-analytics/usePostHogClientCapture";
import { DeletePromptVersion } from "@/src/features/prompts/components/delete-prompt-version";
import { SetPromptVersionLabels } from "@/src/features/prompts/components/SetPromptVersionLabels";
import { PRODUCTION_LABEL } from "@/src/features/prompts/constants";
import { useHasProjectAccess } from "@/src/features/rbac/utils/checkProjectAccess";
import { type RouterOutputs } from "@/src/utils/api";
import { Pencil, PencilOff } from "lucide-react";
import Link from "next/link";
import { type NextRouter, useRouter } from "next/router";
import { useState } from "react";
const PromptHistoryTraceNode = (props: {
index: number;
@@ -10,7 +18,14 @@ const PromptHistoryTraceNode = (props: {
setCurrentPromptVersion: (version: number | undefined) => void;
router: NextRouter;
projectId: string;
totalCount: number;
}) => {
const capture = usePostHogClientCapture();
const [isHovered, setIsHovered] = useState(false);
const hasAccess = useHasProjectAccess({
projectId: props.projectId,
scope: "prompts:CUD",
});
const { prompt } = props;
let badges: JSX.Element[] = prompt.labels
.sort((a, b) =>
@@ -21,7 +36,7 @@ const PromptHistoryTraceNode = (props: {
: a.localeCompare(b),
)
.map((label) => {
return <StatusBadge type={label} key={label} />;
return <StatusBadge type={label} key={label} className="h-6" />;
});
return (
@@ -29,19 +44,63 @@ const PromptHistoryTraceNode = (props: {
className={`group mb-2 flex cursor-pointer flex-col gap-1 rounded-sm p-2 hover:bg-primary-foreground ${
props.currentPromptVersion === prompt.version ? "bg-muted" : ""
}`}
onMouseEnter={() => setIsHovered(true)}
onMouseLeave={() => setIsHovered(false)}
onClick={() => {
props.index === 0
? props.setCurrentPromptVersion(undefined)
: props.setCurrentPromptVersion(prompt.version);
}}
>
<div className="flex flex-wrap items-center gap-2">
<span className="rounded-sm bg-input p-1 text-xs">
Version {prompt.version}
</span>
{badges}
<div className="grid grid-cols-[auto,1fr] items-start gap-2">
<div
className={`grid grid-cols-[auto,1fr] items-start ${isHovered ? "h-full" : "h-7"}`}
>
<span className="flex h-6 text-nowrap rounded-sm bg-input p-1 text-xs">
Version {prompt.version}
</span>
{Boolean(prompt.labels.length) && (
<div className="ml-2 flex h-full flex-wrap gap-1 overflow-auto">
{badges}
</div>
)}
</div>
{isHovered && (
<div className="flex flex-row space-x-1">
<SetPromptVersionLabels prompt={prompt} />
{hasAccess ? (
<Button
variant="outline"
size="icon"
className="h-7 w-7 px-0"
onClick={() => {
capture("prompts:update_form_open");
}}
>
<Link
href={`/project/${props.projectId}/prompts/new?promptId=${encodeURIComponent(prompt.id)}`}
>
<Pencil className="h-4 w-4" />
</Link>
</Button>
) : (
<Button
variant="outline"
size="icon"
className="h-7 w-7 px-0"
disabled
>
<PencilOff className="h-4 w-4" />
</Button>
)}
<DeletePromptVersion
promptVersionId={prompt.id}
version={prompt.version}
countVersions={props.totalCount}
/>
</div>
)}
</div>
<div className="flex gap-2">
<span className="text-xs text-muted-foreground">
{prompt.createdAt.toLocaleString()}
@@ -60,6 +119,7 @@ export const PromptHistoryNode = (props: {
prompts: RouterOutputs["prompts"]["allVersions"]["promptVersions"];
currentPromptVersion: number | undefined;
setCurrentPromptVersion: (id: number | undefined) => void;
totalCount: number;
}) => {
const router = useRouter();
const projectId = router.query.projectId as string;
@@ -74,6 +134,7 @@ export const PromptHistoryNode = (props: {
setCurrentPromptVersion={props.setCurrentPromptVersion}
router={router}
projectId={projectId}
totalCount={props.totalCount}
/>
))}
</div>
@@ -15,7 +15,7 @@ import {
import { type NextApiResponse } from "next";
// Business Logic
// - rate limit strategy is based on org-id, org plan, and resources. Rate limits are appliead in buckets of minutes.
// - rate limit strategy is based on org-id, org plan, and resources. Rate limits are applied in buckets of minutes.
// - rate limits are not applied for self hosters and are also not applied when Redis is not available
// - infos for rate-limits are taken from the API access scope. Info for this scope is stored alongside API Keys in Redis for efficient access.
// - isRateLimited returns false for self-hosters
+10 -4
View File
@@ -35,10 +35,18 @@ export class ApiAuthService {
private async invalidate(apiKeys: ApiKey[], identifier: string) {
const hashKeys = apiKeys.map((key) => key.fastHashedSecretKey);
const filteredHashKeys = hashKeys.filter((hash): hash is string =>
Boolean(hash),
);
if (filteredHashKeys.length === 0) {
console.log("No valid keys to invalidate");
return;
}
if (this.redis) {
console.log(`Invalidating API keys in redis for ${identifier}`);
await this.redis.del(
hashKeys
filteredHashKeys
.filter((hash): hash is string => Boolean(hash))
.map((hash) => this.createRedisKey(hash)),
);
@@ -81,9 +89,7 @@ export class ApiAuthService {
// if redis is available, delete the key from there as well
// delete from redis even if caching is disabled via env for consistency
if (this.redis && apiKey.fastHashedSecretKey) {
await this.redis.del(this.createRedisKey(apiKey.fastHashedSecretKey));
}
this.invalidate([apiKey], `key ${id}`);
await this.prisma.apiKey.delete({
where: {
+1 -1
View File
@@ -13,7 +13,7 @@ export async function telemetry() {
try {
// Only run in prod
if (process.env.NODE_ENV !== "production") return;
// Do not run in Lanfuse cloud, separate telemetry is used
// Do not run in Langfuse cloud, separate telemetry is used
if (process.env.NEXT_PUBLIC_LANGFUSE_CLOUD_REGION !== undefined) return;
// Check if telemetry is not disabled, except for EE
if (
+41 -30
View File
@@ -3,7 +3,6 @@ import { type NextApiRequest, type NextApiResponse } from "next";
import { z } from "zod";
import { LangfuseNotFoundError, InternalServerError } from "@langfuse/shared";
import {
getLegacyIngestionQueue,
eventTypes,
ingestionEvent,
traceException,
@@ -13,6 +12,7 @@ import {
handleBatch,
recordIncrement,
getCurrentSpan,
LegacyIngestionQueue,
} from "@langfuse/shared/src/server";
import {
SdkLogProcessor,
@@ -141,43 +141,54 @@ export default async function handler(
if (env.LANGFUSE_ASYNC_INGESTION_PROCESSING === "true" && redis) {
// this function MUST NOT return but send the HTTP response directly
const queue = getLegacyIngestionQueue();
const queue = LegacyIngestionQueue.getInstance();
if (queue) {
// still need to check auth scope for all events individually
const failedAccessScope = accessCheckPerEvent(sortedBatch, authCheck);
await queue.add(
QueueJobs.LegacyIngestionJob,
{
payload: { data: sortedBatch, authCheck: authCheck },
id: randomUUID(),
timestamp: new Date(),
name: QueueJobs.LegacyIngestionJob as const,
},
{
removeOnFail: 1_000_000,
removeOnComplete: true,
attempts: 5,
backoff: {
type: "exponential",
delay: 1000,
let addToQueueFailed = false;
try {
await queue.add(
QueueJobs.LegacyIngestionJob,
{
payload: { data: sortedBatch, authCheck: authCheck },
id: randomUUID(),
timestamp: new Date(),
name: QueueJobs.LegacyIngestionJob as const,
},
},
);
{
removeOnFail: 1_000_000,
removeOnComplete: true,
attempts: 5,
backoff: {
type: "exponential",
delay: 1000,
},
},
);
} catch (e: unknown) {
console.warn(
"Failed to add batch to queue, falling back to sync processing",
e,
);
addToQueueFailed = true;
}
return handleBatchResult(
[
...validationErrors,
...failedAccessScope.map((e) => ({
id: e.id,
error: "Access Scope Denied",
})),
], // we are not sending additional server errors to the client in case of early return
sortedBatch.map((event) => ({ id: event.id, result: event })),
res,
);
if (!addToQueueFailed) {
return handleBatchResult(
[
...validationErrors,
...failedAccessScope.map((e) => ({
id: e.id,
error: "Access Scope Denied",
})),
], // we are not sending additional server errors to the client in case of early return
sortedBatch.map((event) => ({ id: event.id, result: event })),
res,
);
}
} else {
console.error(
"Ingestion queue not initialized, falling back to sync processing",
@@ -1,6 +1,11 @@
import { FullScreenPage } from "@/src/components/layouts/full-screen-page";
import Header from "@/src/components/layouts/header";
import { Button } from "@/src/components/ui/button";
import {
ResizableHandle,
ResizablePanel,
ResizablePanelGroup,
} from "@/src/components/ui/resizable";
import { DatasetRunItemsTable } from "@/src/features/datasets/components/DatasetRunItemsTable";
import { EditDatasetItem } from "@/src/features/datasets/components/EditDatasetItem";
import { DetailPageNav } from "@/src/features/navigate-detail-pages/DetailPageNav";
@@ -68,13 +73,28 @@ export default function Dataset() {
</>
}
/>
<EditDatasetItem projectId={projectId} datasetItem={item.data ?? null} />
<Header title="Runs" level="h3" />
<DatasetRunItemsTable
projectId={projectId}
datasetItemId={itemId}
datasetId={datasetId}
/>
<ResizablePanelGroup direction="vertical">
<ResizablePanel
minSize={10}
defaultSize={50}
className="!overflow-y-auto"
>
<EditDatasetItem
projectId={projectId}
datasetItem={item.data ?? null}
/>
</ResizablePanel>
<ResizableHandle withHandle className="bg-border" />
<ResizablePanel minSize={10} className="flex flex-col space-y-4">
<Header title="Runs" level="h3" />
<DatasetRunItemsTable
projectId={projectId}
datasetItemId={itemId}
datasetId={datasetId}
/>
</ResizablePanel>
</ResizablePanelGroup>
</FullScreenPage>
);
}
+5 -5
View File
@@ -1,6 +1,6 @@
{
"name": "worker",
"version": "2.75.2",
"version": "2.76.0",
"description": "",
"license": "MIT",
"private": true,
@@ -50,7 +50,7 @@
"pg": "^8.11.5",
"pino": "^9.2.0",
"pino-http": "^10.2.0",
"pino-pretty": "^10.3.1",
"pino-pretty": "^11.2.2",
"stripe": "^16.8.0",
"tiktoken": "^1.0.15",
"uuid": "^9.0.1",
@@ -62,7 +62,7 @@
"@types/cors": "^2.8.17",
"@types/express": "^4.17.21",
"@types/express-serve-static-core": "^4.19.3",
"@types/lodash": "^4.17.5",
"@types/lodash": "^4.17.7",
"@types/node": "^20.11.19",
"@types/pg": "^8.11.6",
"@types/uuid": "^9.0.8",
@@ -72,12 +72,12 @@
"eslint-config-standard": "^17.1.0",
"eslint-plugin-prettier": "^5.1.3",
"kysely-codegen": "^0.11.0",
"msw": "^2.3.1",
"msw": "^2.4.1",
"nodemon": "^3.1.3",
"prettier": "^3.2.5",
"ts-node": "^10.9.2",
"tsc-watch": "^6.2.0",
"tsx": "^4.18.0",
"tsx": "^4.19.0",
"typescript": "^5.4.5",
"vite": "^5.2.13",
"vitest": "^1.5.3"
+11 -13
View File
@@ -35,25 +35,23 @@ app.use("/api", api);
app.use(middlewares.notFound);
app.use(middlewares.errorHandler);
logger.info("Eval Job Creator started", evalJobCreator?.isRunning());
logger.info(`Eval Job Creator started: ${evalJobCreator?.isRunning()}`);
logger.info("Eval Job Executor started", evalJobExecutor?.isRunning());
logger.info(`Eval Job Executor started: ${evalJobExecutor?.isRunning()}`);
logger.info(
"Batch Export Job Executor started",
batchExportJobExecutor?.isRunning()
);
logger.info("Repeat Queue Executor started", repeatQueueExecutor?.isRunning());
logger.info(
"Flush Ingestion Queue Executor started",
ingestionQueueExecutor?.isRunning()
`Batch Export Job Executor started: ${batchExportJobExecutor?.isRunning()}`
);
logger.info(
"Legacy Ingestion Executor started",
legacyIngestionExecutor?.isRunning()
`Repeat Queue Executor started: ${repeatQueueExecutor?.isRunning()}`
);
logger.info(
"Cloud Usage Metering Job Executor started",
cloudUsageMeteringJobExecutor?.isRunning()
`Flush Ingestion Queue Executor started: ${ingestionQueueExecutor?.isRunning()}`
);
logger.info(
`Legacy Ingestion Executor started: ${legacyIngestionExecutor?.isRunning()}`
);
logger.info(
`Cloud Usage Metering Job Executor started: ${cloudUsageMeteringJobExecutor?.isRunning()}`
);
evalJobCreator?.on("failed", logQueueWorkerError);
+1 -1
View File
@@ -1 +1 @@
export const VERSION = "v2.75.2";
export const VERSION = "v2.76.0";
+3
View File
@@ -75,6 +75,9 @@ const EnvSchema = z.object({
.positive()
.default(5),
STRIPE_SECRET_KEY: z.string().optional(),
QUEUE_CONSUMER_LEGACY_INGESTION_QUEUE_IS_ENABLED: z
.enum(["true", "false"])
.default("true"),
});
export const env = EnvSchema.parse(process.env);
+14 -11
View File
@@ -1,7 +1,7 @@
import { Job, Worker } from "bullmq";
import {
traceException,
getLegacyIngestionQueue,
LegacyIngestionQueue,
instrumentAsync,
QueueName,
recordIncrement,
@@ -36,13 +36,13 @@ const createLegacyIngestionExecutor = () => {
try {
const startTime = Date.now();
logger.info(
`Processing legacy ingestion for payload ${JSON.stringify(job.data.payload)}`
`Processing legacy ingestion for payload ${JSON.stringify(job.data.payload)}`,
);
// Log wait time
const waitTime = Date.now() - job.timestamp;
logger.debug(
`Received flush request after ${waitTime} ms for ${job.data.payload.authCheck.scope.projectId}`
`Received flush request after ${waitTime} ms for ${job.data.payload.authCheck.scope.projectId}`,
);
recordIncrement("legacy_ingestion_processing_request");
@@ -53,17 +53,17 @@ const createLegacyIngestionExecutor = () => {
const result = await handleBatch(
job.data.payload.data,
job.data.payload.authCheck,
tokenCount
tokenCount,
);
// send out REDIS requests to worker for all trace types
await sendToWorkerIfEnvironmentConfigured(
result.results,
job.data.payload.authCheck.scope.projectId
job.data.payload.authCheck.scope.projectId,
);
// Log queue size
await getLegacyIngestionQueue()
await LegacyIngestionQueue.getInstance()
?.count()
.then((count) => {
logger.info(`Legacy Ingestion flush queue length: ${count}`);
@@ -76,26 +76,29 @@ const createLegacyIngestionExecutor = () => {
recordHistogram(
"legacy_ingestion_processing_time",
Date.now() - startTime,
{ unit: "milliseconds" }
{ unit: "milliseconds" },
);
} catch (e) {
logger.error(
e,
`Failed job Evaluation for traceId ${job.data.payload} ${e}`
`Failed job Evaluation for traceId ${job.data.payload} ${e}`,
);
traceException(e);
throw e;
}
}
},
);
},
{
connection: redisInstance,
concurrency: env.LANGFUSE_LEGACY_INGESTION_WORKER_CONCURRENCY, // n ingestion batches at a time
}
},
);
}
return null;
};
export const legacyIngestionExecutor = createLegacyIngestionExecutor();
export const legacyIngestionExecutor =
env.QUEUE_CONSUMER_LEGACY_INGESTION_QUEUE_IS_ENABLED === "true"
? createLegacyIngestionExecutor()
: null;