Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8584f93307 | ||
|
|
08e576f815 | ||
|
|
f64da44b20 | ||
|
|
c720a4d896 | ||
|
|
b59b61a5cf | ||
|
|
3f18e42fef | ||
|
|
31a9be6f1d | ||
|
|
fe6d8a38ec | ||
|
|
d4fecbb2a6 | ||
|
|
d92ba1d26b | ||
|
|
61c2312f12 | ||
|
|
862a1395d5 | ||
|
|
21e9e17e93 | ||
|
|
5b32d4b9a2 | ||
|
|
ea5617d621 | ||
|
|
9c72fddc20 |
+1
-1
@@ -33,7 +33,7 @@
|
||||
"@repo/eslint-config": "*",
|
||||
"@repo/typescript-config": "*",
|
||||
"@types/node": "^20.11.29",
|
||||
"@typescript-eslint/parser": "^7.6.0",
|
||||
"@typescript-eslint/parser": "^7.7.0",
|
||||
"eslint": "^8.57.0",
|
||||
"eslint-config-prettier": "^9.1.0",
|
||||
"eslint-config-standard": "^17.1.0",
|
||||
|
||||
+2
-8
@@ -1,15 +1,9 @@
|
||||
import { z } from "zod";
|
||||
import { env as sharedEnv } from "@langfuse/shared";
|
||||
|
||||
const EnvSchema = z.object({
|
||||
NEXT_PUBLIC_LANGFUSE_CLOUD_REGION: z.string().optional(),
|
||||
ENCRYPTION_KEY: z
|
||||
.string()
|
||||
.length(
|
||||
64,
|
||||
"ENCRYPTION_KEY must be 256 bits, 64 string characters in hex format, generate via: openssl rand -hex 32"
|
||||
)
|
||||
.optional(),
|
||||
ADMIN_API_KEY: z.string().optional(),
|
||||
});
|
||||
|
||||
export const env = EnvSchema.parse(process.env);
|
||||
export const env = { ...sharedEnv, ...EnvSchema.parse(process.env) };
|
||||
|
||||
+1
-1
@@ -6,8 +6,8 @@ import Auth0Provider from "next-auth/providers/auth0";
|
||||
import AzureADProvider from "next-auth/providers/azure-ad";
|
||||
import { isEeAvailable } from "..";
|
||||
import { prisma } from "@langfuse/shared/src/db";
|
||||
import { encrypt, decrypt } from "@langfuse/shared/encryption";
|
||||
import { SsoProviderSchema } from "./types";
|
||||
import { decrypt, encrypt } from "../encryption";
|
||||
import { type NextApiRequest, type NextApiResponse } from "next";
|
||||
import { env } from "../env";
|
||||
|
||||
|
||||
@@ -47,12 +47,13 @@ types:
|
||||
properties:
|
||||
date: date
|
||||
countTraces: integer
|
||||
countObservations: 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
|
||||
model: optional<string>
|
||||
inputUsage: integer
|
||||
outputUsage: integer
|
||||
totalUsage: integer
|
||||
|
||||
@@ -24,10 +24,10 @@ service:
|
||||
query-parameters:
|
||||
page:
|
||||
type: optional<integer>
|
||||
docs: page number, starts at 1
|
||||
docs: Page number, starts at 1.
|
||||
limit:
|
||||
type: optional<integer>
|
||||
docs: limit of items per page
|
||||
docs: Limit of items per page. If you encounter api issues due to too large page sizes, try to reduce the limit.
|
||||
name: optional<string>
|
||||
userId: optional<string>
|
||||
type: optional<string>
|
||||
|
||||
@@ -21,10 +21,10 @@ service:
|
||||
query-parameters:
|
||||
page:
|
||||
type: optional<integer>
|
||||
docs: page number, starts at 1
|
||||
docs: Page number, starts at 1.
|
||||
limit:
|
||||
type: optional<integer>
|
||||
docs: limit of items per page
|
||||
docs: Limit of items per page. If you encounter api issues due to too large page sizes, try to reduce the limit.
|
||||
userId: optional<string>
|
||||
name: optional<string>
|
||||
fromTimestamp:
|
||||
|
||||
@@ -16,7 +16,7 @@ service:
|
||||
docs: The unique langfuse identifier of a trace
|
||||
response: commons.TraceWithFullDetails
|
||||
list:
|
||||
docs: Get list of traces
|
||||
docs: Get list of traces.
|
||||
method: GET
|
||||
path: /traces
|
||||
request:
|
||||
@@ -24,10 +24,10 @@ service:
|
||||
query-parameters:
|
||||
page:
|
||||
type: optional<integer>
|
||||
docs: page number, starts at 1
|
||||
docs: Page number, starts at 1
|
||||
limit:
|
||||
type: optional<integer>
|
||||
docs: limit of items per page
|
||||
docs: Limit of items per page. If you encounter api issues due to too large page sizes, try to reduce the limit.
|
||||
userId: optional<string>
|
||||
name: optional<string>
|
||||
fromTimestamp:
|
||||
|
||||
@@ -493,12 +493,12 @@
|
||||
{
|
||||
"key": "page",
|
||||
"value": "",
|
||||
"description": "page number, starts at 1"
|
||||
"description": "Page number, starts at 1."
|
||||
},
|
||||
{
|
||||
"key": "limit",
|
||||
"value": "",
|
||||
"description": "limit of items per page"
|
||||
"description": "Limit of items per page. If you encounter api issues due to too large page sizes, try to reduce the limit."
|
||||
},
|
||||
{
|
||||
"key": "name",
|
||||
@@ -707,12 +707,12 @@
|
||||
{
|
||||
"key": "page",
|
||||
"value": "",
|
||||
"description": "page number, starts at 1"
|
||||
"description": "Page number, starts at 1."
|
||||
},
|
||||
{
|
||||
"key": "limit",
|
||||
"value": "",
|
||||
"description": "limit of items per page"
|
||||
"description": "Limit of items per page. If you encounter api issues due to too large page sizes, try to reduce the limit."
|
||||
},
|
||||
{
|
||||
"key": "userId",
|
||||
@@ -900,7 +900,7 @@
|
||||
"_type": "endpoint",
|
||||
"name": "List",
|
||||
"request": {
|
||||
"description": "Get list of traces",
|
||||
"description": "Get list of traces.",
|
||||
"url": {
|
||||
"raw": "{{baseUrl}}/api/public/traces?page=&limit=&userId=&name=&fromTimestamp=&orderBy=&tags=",
|
||||
"host": [
|
||||
@@ -915,12 +915,12 @@
|
||||
{
|
||||
"key": "page",
|
||||
"value": "",
|
||||
"description": "page number, starts at 1"
|
||||
"description": "Page number, starts at 1"
|
||||
},
|
||||
{
|
||||
"key": "limit",
|
||||
"value": "",
|
||||
"description": "limit of items per page"
|
||||
"description": "Limit of items per page. If you encounter api issues due to too large page sizes, try to reduce the limit."
|
||||
},
|
||||
{
|
||||
"key": "userId",
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "langfuse",
|
||||
"version": "2.29.2",
|
||||
"version": "2.30.2",
|
||||
"author": "engineering@langfuse.com",
|
||||
"license": "MIT",
|
||||
"private": true,
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
],
|
||||
"devDependencies": {
|
||||
"@typescript-eslint/eslint-plugin": "^7.1.0",
|
||||
"@typescript-eslint/parser": "^7.6.0",
|
||||
"@typescript-eslint/parser": "^7.7.0",
|
||||
"@vercel/style-guide": "^6.0.0",
|
||||
"eslint-config-next": "^14.2.1",
|
||||
"eslint-config-prettier": "^9.1.0",
|
||||
|
||||
@@ -20,6 +20,10 @@
|
||||
"./src/server/auth": {
|
||||
"import": "./dist/src/server/auth.js",
|
||||
"require": "./dist/src/server/auth.js"
|
||||
},
|
||||
"./encryption": {
|
||||
"import": "./dist/src/encryption/index.js",
|
||||
"require": "./dist/src/encryption/index.js"
|
||||
}
|
||||
},
|
||||
"scripts": {
|
||||
@@ -28,6 +32,7 @@
|
||||
"lint": "eslint . --ext .js,.jsx,.ts,.tsx",
|
||||
"lint:fix": "eslint . --ext .js,.jsx,.ts,.tsx --fix",
|
||||
"db:migrate": "DISABLE_ERD=false dotenv -e ../../.env -- npx prisma migrate dev",
|
||||
"db:push": "DISABLE_ERD=false dotenv -e ../../.env -- npx prisma db push",
|
||||
"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",
|
||||
@@ -58,7 +63,7 @@
|
||||
"@types/node": "^20.11.29",
|
||||
"@types/pg": "^8.11.5",
|
||||
"@types/uuid": "^9.0.8",
|
||||
"@typescript-eslint/parser": "^7.6.0",
|
||||
"@typescript-eslint/parser": "^7.7.0",
|
||||
"eslint": "^8.57.0",
|
||||
"eslint-config-prettier": "^9.1.0",
|
||||
"eslint-config-standard": "^17.1.0",
|
||||
@@ -72,6 +77,6 @@
|
||||
"ts-node": "^10.9.2",
|
||||
"tsc-watch": "^6.2.0",
|
||||
"typescript": "^5.4.5",
|
||||
"vitest": "^1.3.1"
|
||||
"vitest": "^1.5.0"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -292,6 +292,13 @@ export type ObservationView = {
|
||||
calculated_total_cost: string | null;
|
||||
latency: number | null;
|
||||
};
|
||||
export type PosthogIntegration = {
|
||||
project_id: string;
|
||||
encrypted_posthog_api_key: string;
|
||||
posthog_host_name: string;
|
||||
last_sync_at: Timestamp | null;
|
||||
enabled: boolean;
|
||||
};
|
||||
export type Pricing = {
|
||||
id: string;
|
||||
model_name: string;
|
||||
@@ -422,6 +429,7 @@ export type DB = {
|
||||
models: Model;
|
||||
observations: Observation;
|
||||
observations_view: ObservationView;
|
||||
posthog_integrations: PosthogIntegration;
|
||||
pricings: Pricing;
|
||||
projects: Project;
|
||||
prompts: Prompt;
|
||||
|
||||
+16
@@ -0,0 +1,16 @@
|
||||
-- CreateTable
|
||||
CREATE TABLE "posthog_integrations" (
|
||||
"project_id" TEXT NOT NULL,
|
||||
"encrypted_posthog_api_key" TEXT NOT NULL,
|
||||
"posthog_host_name" TEXT NOT NULL,
|
||||
"last_sync_at" TIMESTAMP(3),
|
||||
"enabled" BOOLEAN NOT NULL,
|
||||
|
||||
CONSTRAINT "posthog_integrations_pkey" PRIMARY KEY ("project_id")
|
||||
);
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "posthog_integrations_project_id_idx" ON "posthog_integrations"("project_id");
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "posthog_integrations" ADD CONSTRAINT "posthog_integrations_project_id_fkey" FOREIGN KEY ("project_id") REFERENCES "projects"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||
@@ -92,25 +92,26 @@ model VerificationToken {
|
||||
}
|
||||
|
||||
model Project {
|
||||
id String @id @default(cuid())
|
||||
createdAt DateTime @default(now()) @map("created_at")
|
||||
updatedAt DateTime @default(now()) @updatedAt @map("updated_at")
|
||||
name String
|
||||
cloudConfig Json? @map("cloud_config") // Langfuse Cloud, for zod schema see projectsRouter.ts
|
||||
members Membership[]
|
||||
traces Trace[]
|
||||
observations Observation[]
|
||||
apiKeys ApiKey[]
|
||||
dataset Dataset[]
|
||||
RawEvents Events[]
|
||||
invitations MembershipInvitation[]
|
||||
sessions TraceSession[]
|
||||
Prompt Prompt[]
|
||||
Model Model[]
|
||||
AuditLog AuditLog[]
|
||||
EvalTemplate EvalTemplate[]
|
||||
JobConfiguration JobConfiguration[]
|
||||
JobExecution JobExecution[]
|
||||
id String @id @default(cuid())
|
||||
createdAt DateTime @default(now()) @map("created_at")
|
||||
updatedAt DateTime @default(now()) @updatedAt @map("updated_at")
|
||||
name String
|
||||
cloudConfig Json? @map("cloud_config") // Langfuse Cloud, for zod schema see projectsRouter.ts
|
||||
members Membership[]
|
||||
traces Trace[]
|
||||
observations Observation[]
|
||||
apiKeys ApiKey[]
|
||||
dataset Dataset[]
|
||||
RawEvents Events[]
|
||||
invitations MembershipInvitation[]
|
||||
sessions TraceSession[]
|
||||
Prompt Prompt[]
|
||||
Model Model[]
|
||||
AuditLog AuditLog[]
|
||||
EvalTemplate EvalTemplate[]
|
||||
JobConfiguration JobConfiguration[]
|
||||
JobExecution JobExecution[]
|
||||
PosthogIntegration PosthogIntegration[]
|
||||
|
||||
@@map("projects")
|
||||
}
|
||||
@@ -699,3 +700,15 @@ model SsoConfig {
|
||||
|
||||
@@map("sso_configs")
|
||||
}
|
||||
|
||||
model PosthogIntegration {
|
||||
projectId String @id @map("project_id")
|
||||
project Project @relation(fields: [projectId], references: [id], onDelete: Cascade)
|
||||
encryptedPosthogApiKey String @map("encrypted_posthog_api_key")
|
||||
posthogHostName String @map("posthog_host_name")
|
||||
lastSyncAt DateTime? @map("last_sync_at")
|
||||
enabled Boolean
|
||||
|
||||
@@index([projectId])
|
||||
@@map("posthog_integrations")
|
||||
}
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
import { z } from "zod";
|
||||
|
||||
const EnvSchema = z.object({
|
||||
ENCRYPTION_KEY: z
|
||||
.string()
|
||||
.length(
|
||||
64,
|
||||
"ENCRYPTION_KEY must be 256 bits, 64 string characters in hex format, generate via: openssl rand -hex 32"
|
||||
)
|
||||
.optional(),
|
||||
});
|
||||
|
||||
export const env = EnvSchema.parse(process.env);
|
||||
@@ -9,6 +9,7 @@ export * from "./filterToPrisma";
|
||||
export * from "./tracesTable";
|
||||
export * from "./server/auth";
|
||||
export * from "./observationsTable";
|
||||
export { env } from "./env";
|
||||
|
||||
// llm api
|
||||
export * from "./server/llm/types";
|
||||
|
||||
Generated
+97
-97
@@ -49,8 +49,8 @@ importers:
|
||||
specifier: ^20.11.29
|
||||
version: 20.11.29
|
||||
'@typescript-eslint/parser':
|
||||
specifier: ^7.6.0
|
||||
version: 7.6.0(eslint@8.57.0)(typescript@5.4.5)
|
||||
specifier: ^7.7.0
|
||||
version: 7.7.0(eslint@8.57.0)(typescript@5.4.5)
|
||||
eslint:
|
||||
specifier: ^8.57.0
|
||||
version: 8.57.0
|
||||
@@ -80,10 +80,10 @@ importers:
|
||||
devDependencies:
|
||||
'@typescript-eslint/eslint-plugin':
|
||||
specifier: ^7.1.0
|
||||
version: 7.3.1(@typescript-eslint/parser@7.6.0)(eslint@8.57.0)(typescript@5.4.5)
|
||||
version: 7.3.1(@typescript-eslint/parser@7.7.0)(eslint@8.57.0)(typescript@5.4.5)
|
||||
'@typescript-eslint/parser':
|
||||
specifier: ^7.6.0
|
||||
version: 7.6.0(eslint@8.57.0)(typescript@5.4.5)
|
||||
specifier: ^7.7.0
|
||||
version: 7.7.0(eslint@8.57.0)(typescript@5.4.5)
|
||||
'@vercel/style-guide':
|
||||
specifier: ^6.0.0
|
||||
version: 6.0.0(eslint@8.57.0)(prettier@3.2.5)(typescript@5.4.5)
|
||||
@@ -160,8 +160,8 @@ importers:
|
||||
specifier: ^9.0.8
|
||||
version: 9.0.8
|
||||
'@typescript-eslint/parser':
|
||||
specifier: ^7.6.0
|
||||
version: 7.6.0(eslint@8.57.0)(typescript@5.4.5)
|
||||
specifier: ^7.7.0
|
||||
version: 7.7.0(eslint@8.57.0)(typescript@5.4.5)
|
||||
eslint:
|
||||
specifier: ^8.57.0
|
||||
version: 8.57.0
|
||||
@@ -202,8 +202,8 @@ importers:
|
||||
specifier: ^5.4.5
|
||||
version: 5.4.5
|
||||
vitest:
|
||||
specifier: ^1.3.1
|
||||
version: 1.4.0(@types/node@20.11.29)
|
||||
specifier: ^1.5.0
|
||||
version: 1.5.0(@types/node@20.11.29)
|
||||
|
||||
web:
|
||||
dependencies:
|
||||
@@ -451,8 +451,8 @@ importers:
|
||||
specifier: ^5.0.1
|
||||
version: 5.0.1(react@18.2.0)
|
||||
react-responsive:
|
||||
specifier: ^9.0.2
|
||||
version: 9.0.2(react@18.2.0)
|
||||
specifier: ^10.0.0
|
||||
version: 10.0.0(react@18.2.0)
|
||||
react18-json-view:
|
||||
specifier: ^0.2.8-canary.6
|
||||
version: 0.2.8-canary.6(react@18.2.0)
|
||||
@@ -529,10 +529,10 @@ importers:
|
||||
version: 9.0.8
|
||||
'@typescript-eslint/eslint-plugin':
|
||||
specifier: ^6.21.0
|
||||
version: 6.21.0(@typescript-eslint/parser@7.6.0)(eslint@8.57.0)(typescript@5.4.5)
|
||||
version: 6.21.0(@typescript-eslint/parser@7.7.0)(eslint@8.57.0)(typescript@5.4.5)
|
||||
'@typescript-eslint/parser':
|
||||
specifier: ^7.6.0
|
||||
version: 7.6.0(eslint@8.57.0)(typescript@5.4.5)
|
||||
specifier: ^7.7.0
|
||||
version: 7.7.0(eslint@8.57.0)(typescript@5.4.5)
|
||||
autoprefixer:
|
||||
specifier: ^10.4.19
|
||||
version: 10.4.19(postcss@8.4.38)
|
||||
@@ -670,8 +670,8 @@ importers:
|
||||
specifier: ^8.11.5
|
||||
version: 8.11.5
|
||||
'@typescript-eslint/parser':
|
||||
specifier: ^7.6.0
|
||||
version: 7.6.0(eslint@8.57.0)(typescript@5.4.5)
|
||||
specifier: ^7.7.0
|
||||
version: 7.7.0(eslint@8.57.0)(typescript@5.4.5)
|
||||
eslint:
|
||||
specifier: ^8.57.0
|
||||
version: 8.57.0
|
||||
@@ -706,8 +706,8 @@ importers:
|
||||
specifier: ^5.2.9
|
||||
version: 5.2.9(@types/node@20.11.29)
|
||||
vitest:
|
||||
specifier: ^1.3.1
|
||||
version: 1.4.0(@types/node@20.11.29)
|
||||
specifier: ^1.5.0
|
||||
version: 1.5.0(@types/node@20.11.29)
|
||||
|
||||
packages:
|
||||
|
||||
@@ -6438,7 +6438,7 @@ packages:
|
||||
dev: true
|
||||
optional: true
|
||||
|
||||
/@typescript-eslint/eslint-plugin@6.21.0(@typescript-eslint/parser@7.6.0)(eslint@8.57.0)(typescript@5.4.5):
|
||||
/@typescript-eslint/eslint-plugin@6.21.0(@typescript-eslint/parser@7.7.0)(eslint@8.57.0)(typescript@5.4.5):
|
||||
resolution: {integrity: sha512-oy9+hTPCUFpngkEZUSzbf9MxI65wbKFoQYsgPdILTfbUldp5ovUuphZVe4i30emU9M/kP+T64Di0mxl7dSw3MA==}
|
||||
engines: {node: ^16.0.0 || >=18.0.0}
|
||||
peerDependencies:
|
||||
@@ -6450,7 +6450,7 @@ packages:
|
||||
optional: true
|
||||
dependencies:
|
||||
'@eslint-community/regexpp': 4.10.0
|
||||
'@typescript-eslint/parser': 7.6.0(eslint@8.57.0)(typescript@5.4.5)
|
||||
'@typescript-eslint/parser': 7.7.0(eslint@8.57.0)(typescript@5.4.5)
|
||||
'@typescript-eslint/scope-manager': 6.21.0
|
||||
'@typescript-eslint/type-utils': 6.21.0(eslint@8.57.0)(typescript@5.4.5)
|
||||
'@typescript-eslint/utils': 6.21.0(eslint@8.57.0)(typescript@5.4.5)
|
||||
@@ -6467,7 +6467,7 @@ packages:
|
||||
- supports-color
|
||||
dev: true
|
||||
|
||||
/@typescript-eslint/eslint-plugin@7.3.1(@typescript-eslint/parser@7.6.0)(eslint@8.57.0)(typescript@5.4.5):
|
||||
/@typescript-eslint/eslint-plugin@7.3.1(@typescript-eslint/parser@7.7.0)(eslint@8.57.0)(typescript@5.4.5):
|
||||
resolution: {integrity: sha512-STEDMVQGww5lhCuNXVSQfbfuNII5E08QWkvAw5Qwf+bj2WT+JkG1uc+5/vXA3AOYMDHVOSpL+9rcbEUiHIm2dw==}
|
||||
engines: {node: ^18.18.0 || >=20.0.0}
|
||||
peerDependencies:
|
||||
@@ -6479,7 +6479,7 @@ packages:
|
||||
optional: true
|
||||
dependencies:
|
||||
'@eslint-community/regexpp': 4.10.0
|
||||
'@typescript-eslint/parser': 7.6.0(eslint@8.57.0)(typescript@5.4.5)
|
||||
'@typescript-eslint/parser': 7.7.0(eslint@8.57.0)(typescript@5.4.5)
|
||||
'@typescript-eslint/scope-manager': 7.3.1
|
||||
'@typescript-eslint/type-utils': 7.3.1(eslint@8.57.0)(typescript@5.4.5)
|
||||
'@typescript-eslint/utils': 7.3.1(eslint@8.57.0)(typescript@5.4.5)
|
||||
@@ -6517,8 +6517,8 @@ packages:
|
||||
- supports-color
|
||||
dev: true
|
||||
|
||||
/@typescript-eslint/parser@7.6.0(eslint@8.57.0)(typescript@5.4.5):
|
||||
resolution: {integrity: sha512-usPMPHcwX3ZoPWnBnhhorc14NJw9J4HpSXQX4urF2TPKG0au0XhJoZyX62fmvdHONUkmyUe74Hzm1//XA+BoYg==}
|
||||
/@typescript-eslint/parser@7.7.0(eslint@8.57.0)(typescript@5.4.5):
|
||||
resolution: {integrity: sha512-fNcDm3wSwVM8QYL4HKVBggdIPAy9Q41vcvC/GtDobw3c4ndVT3K6cqudUmjHPw8EAp4ufax0o58/xvWaP2FmTg==}
|
||||
engines: {node: ^18.18.0 || >=20.0.0}
|
||||
peerDependencies:
|
||||
eslint: ^8.56.0
|
||||
@@ -6527,10 +6527,10 @@ packages:
|
||||
typescript:
|
||||
optional: true
|
||||
dependencies:
|
||||
'@typescript-eslint/scope-manager': 7.6.0
|
||||
'@typescript-eslint/types': 7.6.0
|
||||
'@typescript-eslint/typescript-estree': 7.6.0(typescript@5.4.5)
|
||||
'@typescript-eslint/visitor-keys': 7.6.0
|
||||
'@typescript-eslint/scope-manager': 7.7.0
|
||||
'@typescript-eslint/types': 7.7.0
|
||||
'@typescript-eslint/typescript-estree': 7.7.0(typescript@5.4.5)
|
||||
'@typescript-eslint/visitor-keys': 7.7.0
|
||||
debug: 4.3.4(supports-color@5.5.0)
|
||||
eslint: 8.57.0
|
||||
typescript: 5.4.5
|
||||
@@ -6570,12 +6570,12 @@ packages:
|
||||
'@typescript-eslint/visitor-keys': 7.3.1
|
||||
dev: true
|
||||
|
||||
/@typescript-eslint/scope-manager@7.6.0:
|
||||
resolution: {integrity: sha512-ngttyfExA5PsHSx0rdFgnADMYQi+Zkeiv4/ZxGYUWd0nLs63Ha0ksmp8VMxAIC0wtCFxMos7Lt3PszJssG/E6w==}
|
||||
/@typescript-eslint/scope-manager@7.7.0:
|
||||
resolution: {integrity: sha512-/8INDn0YLInbe9Wt7dK4cXLDYp0fNHP5xKLHvZl3mOT5X17rK/YShXaiNmorl+/U4VKCVIjJnx4Ri5b0y+HClw==}
|
||||
engines: {node: ^18.18.0 || >=20.0.0}
|
||||
dependencies:
|
||||
'@typescript-eslint/types': 7.6.0
|
||||
'@typescript-eslint/visitor-keys': 7.6.0
|
||||
'@typescript-eslint/types': 7.7.0
|
||||
'@typescript-eslint/visitor-keys': 7.7.0
|
||||
dev: true
|
||||
|
||||
/@typescript-eslint/type-utils@6.21.0(eslint@8.57.0)(typescript@5.4.5):
|
||||
@@ -6638,8 +6638,8 @@ packages:
|
||||
engines: {node: ^18.18.0 || >=20.0.0}
|
||||
dev: true
|
||||
|
||||
/@typescript-eslint/types@7.6.0:
|
||||
resolution: {integrity: sha512-h02rYQn8J+MureCvHVVzhl69/GAfQGPQZmOMjG1KfCl7o3HtMSlPaPUAPu6lLctXI5ySRGIYk94clD/AUMCUgQ==}
|
||||
/@typescript-eslint/types@7.7.0:
|
||||
resolution: {integrity: sha512-G01YPZ1Bd2hn+KPpIbrAhEWOn5lQBrjxkzHkWvP6NucMXFtfXoevK82hzQdpfuQYuhkvFDeQYbzXCjR1z9Z03w==}
|
||||
engines: {node: ^18.18.0 || >=20.0.0}
|
||||
dev: true
|
||||
|
||||
@@ -6730,8 +6730,8 @@ packages:
|
||||
- supports-color
|
||||
dev: true
|
||||
|
||||
/@typescript-eslint/typescript-estree@7.6.0(typescript@5.4.5):
|
||||
resolution: {integrity: sha512-+7Y/GP9VuYibecrCQWSKgl3GvUM5cILRttpWtnAu8GNL9j11e4tbuGZmZjJ8ejnKYyBRb2ddGQ3rEFCq3QjMJw==}
|
||||
/@typescript-eslint/typescript-estree@7.7.0(typescript@5.4.5):
|
||||
resolution: {integrity: sha512-8p71HQPE6CbxIBy2kWHqM1KGrC07pk6RJn40n0DSc6bMOBBREZxSDJ+BmRzc8B5OdaMh1ty3mkuWRg4sCFiDQQ==}
|
||||
engines: {node: ^18.18.0 || >=20.0.0}
|
||||
peerDependencies:
|
||||
typescript: '*'
|
||||
@@ -6739,8 +6739,8 @@ packages:
|
||||
typescript:
|
||||
optional: true
|
||||
dependencies:
|
||||
'@typescript-eslint/types': 7.6.0
|
||||
'@typescript-eslint/visitor-keys': 7.6.0
|
||||
'@typescript-eslint/types': 7.7.0
|
||||
'@typescript-eslint/visitor-keys': 7.7.0
|
||||
debug: 4.3.4(supports-color@5.5.0)
|
||||
globby: 11.1.0
|
||||
is-glob: 4.0.3
|
||||
@@ -6842,11 +6842,11 @@ packages:
|
||||
eslint-visitor-keys: 3.4.3
|
||||
dev: true
|
||||
|
||||
/@typescript-eslint/visitor-keys@7.6.0:
|
||||
resolution: {integrity: sha512-4eLB7t+LlNUmXzfOu1VAIAdkjbu5xNSerURS9X/S5TUKWFRpXRQZbmtPqgKmYx8bj3J0irtQXSiWAOY82v+cgw==}
|
||||
/@typescript-eslint/visitor-keys@7.7.0:
|
||||
resolution: {integrity: sha512-h0WHOj8MhdhY8YWkzIF30R379y0NqyOHExI9N9KCzvmu05EgG4FumeYa3ccfKUSphyWkWQE1ybVrgz/Pbam6YA==}
|
||||
engines: {node: ^18.18.0 || >=20.0.0}
|
||||
dependencies:
|
||||
'@typescript-eslint/types': 7.6.0
|
||||
'@typescript-eslint/types': 7.7.0
|
||||
eslint-visitor-keys: 3.4.3
|
||||
dev: true
|
||||
|
||||
@@ -6944,14 +6944,14 @@ packages:
|
||||
'@babel/core': 7.24.3
|
||||
'@babel/eslint-parser': 7.24.1(@babel/core@7.24.3)(eslint@8.57.0)
|
||||
'@rushstack/eslint-patch': 1.7.2
|
||||
'@typescript-eslint/eslint-plugin': 7.3.1(@typescript-eslint/parser@7.6.0)(eslint@8.57.0)(typescript@5.4.5)
|
||||
'@typescript-eslint/parser': 7.6.0(eslint@8.57.0)(typescript@5.4.5)
|
||||
'@typescript-eslint/eslint-plugin': 7.3.1(@typescript-eslint/parser@7.7.0)(eslint@8.57.0)(typescript@5.4.5)
|
||||
'@typescript-eslint/parser': 7.7.0(eslint@8.57.0)(typescript@5.4.5)
|
||||
eslint: 8.57.0
|
||||
eslint-config-prettier: 9.1.0(eslint@8.57.0)
|
||||
eslint-import-resolver-alias: 1.1.2(eslint-plugin-import@2.29.1)
|
||||
eslint-import-resolver-typescript: 3.6.1(@typescript-eslint/parser@7.6.0)(eslint-plugin-import@2.29.1)(eslint@8.57.0)
|
||||
eslint-import-resolver-typescript: 3.6.1(@typescript-eslint/parser@7.7.0)(eslint-plugin-import@2.29.1)(eslint@8.57.0)
|
||||
eslint-plugin-eslint-comments: 3.2.0(eslint@8.57.0)
|
||||
eslint-plugin-import: 2.29.1(@typescript-eslint/parser@7.6.0)(eslint-import-resolver-typescript@3.6.1)(eslint@8.57.0)
|
||||
eslint-plugin-import: 2.29.1(@typescript-eslint/parser@7.7.0)(eslint-import-resolver-typescript@3.6.1)(eslint@8.57.0)
|
||||
eslint-plugin-jest: 27.9.0(@typescript-eslint/eslint-plugin@7.3.1)(eslint@8.57.0)(typescript@5.4.5)
|
||||
eslint-plugin-jsx-a11y: 6.8.0(eslint@8.57.0)
|
||||
eslint-plugin-playwright: 1.5.4(eslint-plugin-jest@27.9.0)(eslint@8.57.0)
|
||||
@@ -6972,38 +6972,38 @@ packages:
|
||||
- vitest
|
||||
dev: true
|
||||
|
||||
/@vitest/expect@1.4.0:
|
||||
resolution: {integrity: sha512-Jths0sWCJZ8BxjKe+p+eKsoqev1/T8lYcrjavEaz8auEJ4jAVY0GwW3JKmdVU4mmNPLPHixh4GNXP7GFtAiDHA==}
|
||||
/@vitest/expect@1.5.0:
|
||||
resolution: {integrity: sha512-0pzuCI6KYi2SIC3LQezmxujU9RK/vwC1U9R0rLuGlNGcOuDWxqWKu6nUdFsX9tH1WU0SXtAxToOsEjeUn1s3hA==}
|
||||
dependencies:
|
||||
'@vitest/spy': 1.4.0
|
||||
'@vitest/utils': 1.4.0
|
||||
'@vitest/spy': 1.5.0
|
||||
'@vitest/utils': 1.5.0
|
||||
chai: 4.4.1
|
||||
dev: true
|
||||
|
||||
/@vitest/runner@1.4.0:
|
||||
resolution: {integrity: sha512-EDYVSmesqlQ4RD2VvWo3hQgTJ7ZrFQ2VSJdfiJiArkCerDAGeyF1i6dHkmySqk573jLp6d/cfqCN+7wUB5tLgg==}
|
||||
/@vitest/runner@1.5.0:
|
||||
resolution: {integrity: sha512-7HWwdxXP5yDoe7DTpbif9l6ZmDwCzcSIK38kTSIt6CFEpMjX4EpCgT6wUmS0xTXqMI6E/ONmfgRKmaujpabjZQ==}
|
||||
dependencies:
|
||||
'@vitest/utils': 1.4.0
|
||||
'@vitest/utils': 1.5.0
|
||||
p-limit: 5.0.0
|
||||
pathe: 1.1.2
|
||||
dev: true
|
||||
|
||||
/@vitest/snapshot@1.4.0:
|
||||
resolution: {integrity: sha512-saAFnt5pPIA5qDGxOHxJ/XxhMFKkUSBJmVt5VgDsAqPTX6JP326r5C/c9UuCMPoXNzuudTPsYDZCoJ5ilpqG2A==}
|
||||
/@vitest/snapshot@1.5.0:
|
||||
resolution: {integrity: sha512-qpv3fSEuNrhAO3FpH6YYRdaECnnRjg9VxbhdtPwPRnzSfHVXnNzzrpX4cJxqiwgRMo7uRMWDFBlsBq4Cr+rO3A==}
|
||||
dependencies:
|
||||
magic-string: 0.30.9
|
||||
pathe: 1.1.2
|
||||
pretty-format: 29.7.0
|
||||
dev: true
|
||||
|
||||
/@vitest/spy@1.4.0:
|
||||
resolution: {integrity: sha512-Ywau/Qs1DzM/8Uc+yA77CwSegizMlcgTJuYGAi0jujOteJOUf1ujunHThYo243KG9nAyWT3L9ifPYZ5+As/+6Q==}
|
||||
/@vitest/spy@1.5.0:
|
||||
resolution: {integrity: sha512-vu6vi6ew5N5MMHJjD5PoakMRKYdmIrNJmyfkhRpQt5d9Ewhw9nZ5Aqynbi3N61bvk9UvZ5UysMT6ayIrZ8GA9w==}
|
||||
dependencies:
|
||||
tinyspy: 2.2.1
|
||||
dev: true
|
||||
|
||||
/@vitest/utils@1.4.0:
|
||||
resolution: {integrity: sha512-mx3Yd1/6e2Vt/PUC98DcqTirtfxUyAZ32uK82r8rZzbtBeBo+nqgnjx/LvqQdWsrvNtm14VmurNgcf4nqY5gJg==}
|
||||
/@vitest/utils@1.5.0:
|
||||
resolution: {integrity: sha512-BDU0GNL8MWkRkSRdNFvCUCAVOeHaUlVJ9Tx0TYBZyXaaOTmGtUFObzchCivIBrIwKzvZA7A9sCejVhXM2aY98A==}
|
||||
dependencies:
|
||||
diff-sequences: 29.6.3
|
||||
estree-walker: 3.0.3
|
||||
@@ -9224,7 +9224,7 @@ packages:
|
||||
eslint: 8.57.0
|
||||
eslint-import-resolver-node: 0.3.9
|
||||
eslint-import-resolver-typescript: 3.6.1(@typescript-eslint/parser@7.2.0)(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.29.1)(eslint@8.57.0)
|
||||
eslint-plugin-import: 2.29.1(@typescript-eslint/parser@7.6.0)(eslint-import-resolver-typescript@3.6.1)(eslint@8.57.0)
|
||||
eslint-plugin-import: 2.29.1(@typescript-eslint/parser@7.7.0)(eslint-import-resolver-typescript@3.6.1)(eslint@8.57.0)
|
||||
eslint-plugin-jsx-a11y: 6.8.0(eslint@8.57.0)
|
||||
eslint-plugin-react: 7.34.1(eslint@8.57.0)
|
||||
eslint-plugin-react-hooks: 4.6.0(eslint@8.57.0)
|
||||
@@ -9253,7 +9253,7 @@ packages:
|
||||
eslint-plugin-promise: ^6.0.0
|
||||
dependencies:
|
||||
eslint: 8.57.0
|
||||
eslint-plugin-import: 2.29.1(@typescript-eslint/parser@7.6.0)(eslint-import-resolver-typescript@3.6.1)(eslint@8.57.0)
|
||||
eslint-plugin-import: 2.29.1(@typescript-eslint/parser@7.7.0)(eslint-import-resolver-typescript@3.6.1)(eslint@8.57.0)
|
||||
eslint-plugin-n: 16.6.2(eslint@8.57.0)
|
||||
eslint-plugin-promise: 6.1.1(eslint@8.57.0)
|
||||
dev: true
|
||||
@@ -9273,7 +9273,7 @@ packages:
|
||||
peerDependencies:
|
||||
eslint-plugin-import: '>=1.4.0'
|
||||
dependencies:
|
||||
eslint-plugin-import: 2.29.1(@typescript-eslint/parser@7.6.0)(eslint-import-resolver-typescript@3.6.1)(eslint@8.57.0)
|
||||
eslint-plugin-import: 2.29.1(@typescript-eslint/parser@7.7.0)(eslint-import-resolver-typescript@3.6.1)(eslint@8.57.0)
|
||||
dev: true
|
||||
|
||||
/eslint-import-resolver-node@0.3.9:
|
||||
@@ -9297,7 +9297,7 @@ packages:
|
||||
enhanced-resolve: 5.16.0
|
||||
eslint: 8.57.0
|
||||
eslint-module-utils: 2.8.1(@typescript-eslint/parser@7.2.0)(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.1)(eslint@8.57.0)
|
||||
eslint-plugin-import: 2.29.1(@typescript-eslint/parser@7.6.0)(eslint-import-resolver-typescript@3.6.1)(eslint@8.57.0)
|
||||
eslint-plugin-import: 2.29.1(@typescript-eslint/parser@7.7.0)(eslint-import-resolver-typescript@3.6.1)(eslint@8.57.0)
|
||||
fast-glob: 3.3.2
|
||||
get-tsconfig: 4.7.3
|
||||
is-core-module: 2.13.1
|
||||
@@ -9309,7 +9309,7 @@ packages:
|
||||
- supports-color
|
||||
dev: true
|
||||
|
||||
/eslint-import-resolver-typescript@3.6.1(@typescript-eslint/parser@7.6.0)(eslint-plugin-import@2.29.1)(eslint@8.57.0):
|
||||
/eslint-import-resolver-typescript@3.6.1(@typescript-eslint/parser@7.7.0)(eslint-plugin-import@2.29.1)(eslint@8.57.0):
|
||||
resolution: {integrity: sha512-xgdptdoi5W3niYeuQxKmzVDTATvLYqhpwmykwsh7f6HIOStGWEIL9iqZgQDF9u9OEzrRwR8no5q2VT+bjAujTg==}
|
||||
engines: {node: ^14.18.0 || >=16.0.0}
|
||||
peerDependencies:
|
||||
@@ -9319,8 +9319,8 @@ packages:
|
||||
debug: 4.3.4(supports-color@5.5.0)
|
||||
enhanced-resolve: 5.16.0
|
||||
eslint: 8.57.0
|
||||
eslint-module-utils: 2.8.1(@typescript-eslint/parser@7.6.0)(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.1)(eslint@8.57.0)
|
||||
eslint-plugin-import: 2.29.1(@typescript-eslint/parser@7.6.0)(eslint-import-resolver-typescript@3.6.1)(eslint@8.57.0)
|
||||
eslint-module-utils: 2.8.1(@typescript-eslint/parser@7.7.0)(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.1)(eslint@8.57.0)
|
||||
eslint-plugin-import: 2.29.1(@typescript-eslint/parser@7.7.0)(eslint-import-resolver-typescript@3.6.1)(eslint@8.57.0)
|
||||
fast-glob: 3.3.2
|
||||
get-tsconfig: 4.7.3
|
||||
is-core-module: 2.13.1
|
||||
@@ -9362,7 +9362,7 @@ packages:
|
||||
- supports-color
|
||||
dev: true
|
||||
|
||||
/eslint-module-utils@2.8.1(@typescript-eslint/parser@7.6.0)(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.1)(eslint@8.57.0):
|
||||
/eslint-module-utils@2.8.1(@typescript-eslint/parser@7.7.0)(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.1)(eslint@8.57.0):
|
||||
resolution: {integrity: sha512-rXDXR3h7cs7dy9RNpUlQf80nX31XWJEyGq1tRMo+6GsO5VmTe4UTwtmonAD4ZkAsrfMVDA2wlGJ3790Ys+D49Q==}
|
||||
engines: {node: '>=4'}
|
||||
peerDependencies:
|
||||
@@ -9383,11 +9383,11 @@ packages:
|
||||
eslint-import-resolver-webpack:
|
||||
optional: true
|
||||
dependencies:
|
||||
'@typescript-eslint/parser': 7.6.0(eslint@8.57.0)(typescript@5.4.5)
|
||||
'@typescript-eslint/parser': 7.7.0(eslint@8.57.0)(typescript@5.4.5)
|
||||
debug: 3.2.7
|
||||
eslint: 8.57.0
|
||||
eslint-import-resolver-node: 0.3.9
|
||||
eslint-import-resolver-typescript: 3.6.1(@typescript-eslint/parser@7.6.0)(eslint-plugin-import@2.29.1)(eslint@8.57.0)
|
||||
eslint-import-resolver-typescript: 3.6.1(@typescript-eslint/parser@7.7.0)(eslint-plugin-import@2.29.1)(eslint@8.57.0)
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
dev: true
|
||||
@@ -9415,7 +9415,7 @@ packages:
|
||||
ignore: 5.3.1
|
||||
dev: true
|
||||
|
||||
/eslint-plugin-import@2.29.1(@typescript-eslint/parser@7.6.0)(eslint-import-resolver-typescript@3.6.1)(eslint@8.57.0):
|
||||
/eslint-plugin-import@2.29.1(@typescript-eslint/parser@7.7.0)(eslint-import-resolver-typescript@3.6.1)(eslint@8.57.0):
|
||||
resolution: {integrity: sha512-BbPC0cuExzhiMo4Ff1BTVwHpjjv28C5R+btTOGaCRC7UEz801up0JadwkeSk5Ued6TG34uaczuVuH6qyy5YUxw==}
|
||||
engines: {node: '>=4'}
|
||||
peerDependencies:
|
||||
@@ -9425,7 +9425,7 @@ packages:
|
||||
'@typescript-eslint/parser':
|
||||
optional: true
|
||||
dependencies:
|
||||
'@typescript-eslint/parser': 7.6.0(eslint@8.57.0)(typescript@5.4.5)
|
||||
'@typescript-eslint/parser': 7.7.0(eslint@8.57.0)(typescript@5.4.5)
|
||||
array-includes: 3.1.7
|
||||
array.prototype.findlastindex: 1.2.4
|
||||
array.prototype.flat: 1.3.2
|
||||
@@ -9434,7 +9434,7 @@ packages:
|
||||
doctrine: 2.1.0
|
||||
eslint: 8.57.0
|
||||
eslint-import-resolver-node: 0.3.9
|
||||
eslint-module-utils: 2.8.1(@typescript-eslint/parser@7.6.0)(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.1)(eslint@8.57.0)
|
||||
eslint-module-utils: 2.8.1(@typescript-eslint/parser@7.7.0)(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.1)(eslint@8.57.0)
|
||||
hasown: 2.0.2
|
||||
is-core-module: 2.13.1
|
||||
is-glob: 4.0.3
|
||||
@@ -9463,7 +9463,7 @@ packages:
|
||||
jest:
|
||||
optional: true
|
||||
dependencies:
|
||||
'@typescript-eslint/eslint-plugin': 7.3.1(@typescript-eslint/parser@7.6.0)(eslint@8.57.0)(typescript@5.4.5)
|
||||
'@typescript-eslint/eslint-plugin': 7.3.1(@typescript-eslint/parser@7.7.0)(eslint@8.57.0)(typescript@5.4.5)
|
||||
'@typescript-eslint/utils': 5.62.0(eslint@8.57.0)(typescript@5.4.5)
|
||||
eslint: 8.57.0
|
||||
transitivePeerDependencies:
|
||||
@@ -9671,7 +9671,7 @@ packages:
|
||||
vitest:
|
||||
optional: true
|
||||
dependencies:
|
||||
'@typescript-eslint/eslint-plugin': 7.3.1(@typescript-eslint/parser@7.6.0)(eslint@8.57.0)(typescript@5.4.5)
|
||||
'@typescript-eslint/eslint-plugin': 7.3.1(@typescript-eslint/parser@7.7.0)(eslint@8.57.0)(typescript@5.4.5)
|
||||
'@typescript-eslint/utils': 7.3.1(eslint@8.57.0)(typescript@5.4.5)
|
||||
eslint: 8.57.0
|
||||
transitivePeerDependencies:
|
||||
@@ -12868,8 +12868,8 @@ packages:
|
||||
hasBin: true
|
||||
dev: false
|
||||
|
||||
/matchmediaquery@0.3.1:
|
||||
resolution: {integrity: sha512-Hlk20WQHRIm9EE9luN1kjRjYXAQToHOIAHPJn9buxBwuhfTHoKUcX+lXBbxc85DVQfXYbEQ4HcwQdd128E3qHQ==}
|
||||
/matchmediaquery@0.4.2:
|
||||
resolution: {integrity: sha512-wrZpoT50ehYOudhDjt/YvUJc6eUzcdFPdmbizfgvswCKNHD1/OBOHYJpHie+HXpu6bSkEGieFMYk6VuutaiRfA==}
|
||||
dependencies:
|
||||
css-mediaquery: 0.1.2
|
||||
dev: false
|
||||
@@ -14701,17 +14701,17 @@ packages:
|
||||
use-sidecar: 1.1.2(@types/react@18.2.79)(react@18.2.0)
|
||||
dev: false
|
||||
|
||||
/react-responsive@9.0.2(react@18.2.0):
|
||||
resolution: {integrity: sha512-+4CCab7z8G8glgJoRjAwocsgsv6VA2w7JPxFWHRc7kvz8mec1/K5LutNC2MG28Mn8mu6+bu04XZxHv5gyfT7xQ==}
|
||||
engines: {node: '>=0.10'}
|
||||
/react-responsive@10.0.0(react@18.2.0):
|
||||
resolution: {integrity: sha512-N6/UiRLGQyGUqrarhBZmrSmHi2FXSD++N5VbSKsBBvWfG0ZV7asvUBluSv5lSzdMyEVjzZ6Y8DL4OHABiztDOg==}
|
||||
engines: {node: '>=14'}
|
||||
peerDependencies:
|
||||
react: '>=16.8.0'
|
||||
dependencies:
|
||||
hyphenate-style-name: 1.0.4
|
||||
matchmediaquery: 0.3.1
|
||||
matchmediaquery: 0.4.2
|
||||
prop-types: 15.8.1
|
||||
react: 18.2.0
|
||||
shallow-equal: 1.2.1
|
||||
shallow-equal: 3.1.0
|
||||
dev: false
|
||||
|
||||
/react-smooth@4.0.0(react-dom@18.2.0)(react@18.2.0):
|
||||
@@ -15314,8 +15314,8 @@ packages:
|
||||
resolution: {integrity: sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==}
|
||||
dev: false
|
||||
|
||||
/shallow-equal@1.2.1:
|
||||
resolution: {integrity: sha512-S4vJDjHHMBaiZuT9NPb616CSmLf618jawtv3sufLl6ivK8WocjAo58cXwbRV1cgqxH0Qbv+iUt6m05eqEa2IRA==}
|
||||
/shallow-equal@3.1.0:
|
||||
resolution: {integrity: sha512-pfVOw8QZIXpMbhBWvzBISicvToTiM5WBF1EeAUZDDSb5Dt29yl4AYbyywbJFSEsRUMr7gJaxqCdr4L3tQf9wVg==}
|
||||
dev: false
|
||||
|
||||
/shebang-command@2.0.0:
|
||||
@@ -16035,8 +16035,8 @@ packages:
|
||||
resolution: {integrity: sha512-N8hW3PG/3aOoZAN5V/NSAEDz0ZixDSSt5b/a05iqtpgfLWMSVuCo7w0k2vVvEjdrIoeGqZzweX2WlyioNIHchA==}
|
||||
dev: true
|
||||
|
||||
/tinypool@0.8.2:
|
||||
resolution: {integrity: sha512-SUszKYe5wgsxnNOVlBYO6IC+8VGWdVGZWAqUxp3UErNBtptZvWbwyUOyzNL59zigz2rCA92QiL3wvG+JDSdJdQ==}
|
||||
/tinypool@0.8.4:
|
||||
resolution: {integrity: sha512-i11VH5gS6IFeLY3gMBQ00/MmLncVP7JLXOw1vlgkytLmJK7QnEr7NXf0LBdxfmNPAeyetukOk0bOYrJrFGjYJQ==}
|
||||
engines: {node: '>=14.0.0'}
|
||||
dev: true
|
||||
|
||||
@@ -16657,8 +16657,8 @@ packages:
|
||||
d3-timer: 3.0.1
|
||||
dev: false
|
||||
|
||||
/vite-node@1.4.0(@types/node@20.11.29):
|
||||
resolution: {integrity: sha512-VZDAseqjrHgNd4Kh8icYHWzTKSCZMhia7GyHfhtzLW33fZlG9SwsB6CEhgyVOWkJfJ2pFLrp/Gj1FSfAiqH9Lw==}
|
||||
/vite-node@1.5.0(@types/node@20.11.29):
|
||||
resolution: {integrity: sha512-tV8h6gMj6vPzVCa7l+VGq9lwoJjW8Y79vst8QZZGiuRAfijU+EEWuc0kFpmndQrWhMMhet1jdSF+40KSZUqIIw==}
|
||||
engines: {node: ^18.0.0 || >=20.0.0}
|
||||
hasBin: true
|
||||
dependencies:
|
||||
@@ -16714,15 +16714,15 @@ packages:
|
||||
fsevents: 2.3.3
|
||||
dev: true
|
||||
|
||||
/vitest@1.4.0(@types/node@20.11.29):
|
||||
resolution: {integrity: sha512-gujzn0g7fmwf83/WzrDTnncZt2UiXP41mHuFYFrdwaLRVQ6JYQEiME2IfEjU3vcFL3VKa75XhI3lFgn+hfVsQw==}
|
||||
/vitest@1.5.0(@types/node@20.11.29):
|
||||
resolution: {integrity: sha512-d8UKgR0m2kjdxDWX6911uwxout6GHS0XaGH1cksSIVVG8kRlE7G7aBw7myKQCvDI5dT4j7ZMa+l706BIORMDLw==}
|
||||
engines: {node: ^18.0.0 || >=20.0.0}
|
||||
hasBin: true
|
||||
peerDependencies:
|
||||
'@edge-runtime/vm': '*'
|
||||
'@types/node': ^18.0.0 || >=20.0.0
|
||||
'@vitest/browser': 1.4.0
|
||||
'@vitest/ui': 1.4.0
|
||||
'@vitest/browser': 1.5.0
|
||||
'@vitest/ui': 1.5.0
|
||||
happy-dom: '*'
|
||||
jsdom: '*'
|
||||
peerDependenciesMeta:
|
||||
@@ -16740,11 +16740,11 @@ packages:
|
||||
optional: true
|
||||
dependencies:
|
||||
'@types/node': 20.11.29
|
||||
'@vitest/expect': 1.4.0
|
||||
'@vitest/runner': 1.4.0
|
||||
'@vitest/snapshot': 1.4.0
|
||||
'@vitest/spy': 1.4.0
|
||||
'@vitest/utils': 1.4.0
|
||||
'@vitest/expect': 1.5.0
|
||||
'@vitest/runner': 1.5.0
|
||||
'@vitest/snapshot': 1.5.0
|
||||
'@vitest/spy': 1.5.0
|
||||
'@vitest/utils': 1.5.0
|
||||
acorn-walk: 8.3.2
|
||||
chai: 4.4.1
|
||||
debug: 4.3.4(supports-color@5.5.0)
|
||||
@@ -16756,9 +16756,9 @@ packages:
|
||||
std-env: 3.7.0
|
||||
strip-literal: 2.0.0
|
||||
tinybench: 2.6.0
|
||||
tinypool: 0.8.2
|
||||
tinypool: 0.8.4
|
||||
vite: 5.2.9(@types/node@20.11.29)
|
||||
vite-node: 1.4.0(@types/node@20.11.29)
|
||||
vite-node: 1.5.0(@types/node@20.11.29)
|
||||
why-is-node-running: 2.2.2
|
||||
transitivePeerDependencies:
|
||||
- less
|
||||
|
||||
@@ -590,14 +590,16 @@ paths:
|
||||
parameters:
|
||||
- name: page
|
||||
in: query
|
||||
description: page number, starts at 1
|
||||
description: Page number, starts at 1.
|
||||
required: false
|
||||
schema:
|
||||
type: integer
|
||||
nullable: true
|
||||
- name: limit
|
||||
in: query
|
||||
description: limit of items per page
|
||||
description: >-
|
||||
Limit of items per page. If you encounter api issues due to too
|
||||
large page sizes, try to reduce the limit.
|
||||
required: false
|
||||
schema:
|
||||
type: integer
|
||||
@@ -865,14 +867,16 @@ paths:
|
||||
parameters:
|
||||
- name: page
|
||||
in: query
|
||||
description: page number, starts at 1
|
||||
description: Page number, starts at 1.
|
||||
required: false
|
||||
schema:
|
||||
type: integer
|
||||
nullable: true
|
||||
- name: limit
|
||||
in: query
|
||||
description: limit of items per page
|
||||
description: >-
|
||||
Limit of items per page. If you encounter api issues due to too
|
||||
large page sizes, try to reduce the limit.
|
||||
required: false
|
||||
schema:
|
||||
type: integer
|
||||
@@ -1132,21 +1136,23 @@ paths:
|
||||
security: *ref_0
|
||||
/api/public/traces:
|
||||
get:
|
||||
description: Get list of traces
|
||||
description: Get list of traces.
|
||||
operationId: trace_list
|
||||
tags:
|
||||
- Trace
|
||||
parameters:
|
||||
- name: page
|
||||
in: query
|
||||
description: page number, starts at 1
|
||||
description: Page number, starts at 1
|
||||
required: false
|
||||
schema:
|
||||
type: integer
|
||||
nullable: true
|
||||
- name: limit
|
||||
in: query
|
||||
description: limit of items per page
|
||||
description: >-
|
||||
Limit of items per page. If you encounter api issues due to too
|
||||
large page sizes, try to reduce the limit.
|
||||
required: false
|
||||
schema:
|
||||
type: integer
|
||||
@@ -2391,6 +2397,8 @@ components:
|
||||
type: string
|
||||
countTraces:
|
||||
type: integer
|
||||
countObservations:
|
||||
type: integer
|
||||
totalCost:
|
||||
type: number
|
||||
format: double
|
||||
@@ -2401,6 +2409,7 @@ components:
|
||||
required:
|
||||
- date
|
||||
- countTraces
|
||||
- countObservations
|
||||
- totalCost
|
||||
- usage
|
||||
UsageByModel:
|
||||
@@ -2412,6 +2421,7 @@ components:
|
||||
properties:
|
||||
model:
|
||||
type: string
|
||||
nullable: true
|
||||
inputUsage:
|
||||
type: integer
|
||||
outputUsage:
|
||||
@@ -2419,7 +2429,6 @@ components:
|
||||
totalUsage:
|
||||
type: integer
|
||||
required:
|
||||
- model
|
||||
- inputUsage
|
||||
- outputUsage
|
||||
- totalUsage
|
||||
|
||||
+3
-3
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "web",
|
||||
"version": "2.29.2",
|
||||
"version": "2.30.2",
|
||||
"private": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
@@ -102,7 +102,7 @@
|
||||
"react-dom": "18.2.0",
|
||||
"react-hook-form": "^7.51.3",
|
||||
"react-icons": "^5.0.1",
|
||||
"react-responsive": "^9.0.2",
|
||||
"react-responsive": "^10.0.0",
|
||||
"react18-json-view": "^0.2.8-canary.6",
|
||||
"sonner": "^1.4.41",
|
||||
"superjson": "2.2.1",
|
||||
@@ -129,7 +129,7 @@
|
||||
"@types/react-dom": "^18.2.25",
|
||||
"@types/uuid": "^9.0.8",
|
||||
"@typescript-eslint/eslint-plugin": "^6.21.0",
|
||||
"@typescript-eslint/parser": "^7.6.0",
|
||||
"@typescript-eslint/parser": "^7.7.0",
|
||||
"autoprefixer": "^10.4.19",
|
||||
"dotenv-cli": "^7.3.0",
|
||||
"eslint": "^8.56.0",
|
||||
|
||||
@@ -49,16 +49,16 @@ describe("/api/public/datasets and /api/public/dataset-items API Endpoints", ()
|
||||
});
|
||||
afterEach(async () => await pruneDatabase());
|
||||
|
||||
it("should create and get a dataset", async () => {
|
||||
it("should create and get a dataset, include special characters", async () => {
|
||||
await makeAPICall("POST", "/api/public/datasets", {
|
||||
name: "dataset-name",
|
||||
name: "dataset + name",
|
||||
description: "dataset-description",
|
||||
metadata: { foo: "bar" },
|
||||
});
|
||||
|
||||
const dbDataset = await prisma.dataset.findMany({
|
||||
where: {
|
||||
name: "dataset-name",
|
||||
name: "dataset + name",
|
||||
},
|
||||
});
|
||||
|
||||
@@ -66,12 +66,12 @@ describe("/api/public/datasets and /api/public/dataset-items API Endpoints", ()
|
||||
|
||||
const getDataset = await makeAPICall(
|
||||
"GET",
|
||||
`/api/public/datasets/dataset-name`,
|
||||
`/api/public/datasets/${encodeURIComponent("dataset + name")}`,
|
||||
);
|
||||
|
||||
expect(getDataset.status).toBe(200);
|
||||
expect(getDataset.body).toMatchObject({
|
||||
name: "dataset-name",
|
||||
name: "dataset + name",
|
||||
description: "dataset-description",
|
||||
metadata: { foo: "bar" },
|
||||
});
|
||||
@@ -225,20 +225,20 @@ describe("/api/public/datasets and /api/public/dataset-items API Endpoints", ()
|
||||
expect(dbDatasetItem?.metadata).toMatchObject(["hello-world"]);
|
||||
});
|
||||
|
||||
it("should create and get a dataset run", async () => {
|
||||
it("should create and get a dataset run, include special characters", async () => {
|
||||
const dataset = await makeAPICall<{ id: string }>(
|
||||
"POST",
|
||||
"/api/public/datasets",
|
||||
{
|
||||
name: "dataset-name",
|
||||
name: "dataset name",
|
||||
},
|
||||
);
|
||||
expect(dataset.status).toBe(200);
|
||||
expect(dataset.body).toMatchObject({
|
||||
name: "dataset-name",
|
||||
name: "dataset name",
|
||||
});
|
||||
await makeAPICall("POST", "/api/public/dataset-items", {
|
||||
datasetName: "dataset-name",
|
||||
datasetName: "dataset name",
|
||||
id: "dataset-item-id",
|
||||
input: { key: "value" },
|
||||
expectedOutput: { key: "value" },
|
||||
@@ -287,14 +287,14 @@ describe("/api/public/datasets and /api/public/dataset-items API Endpoints", ()
|
||||
{
|
||||
datasetItemId: "dataset-item-id",
|
||||
observationId: observationId,
|
||||
runName: "run-only-observation",
|
||||
runName: "run + only + observation",
|
||||
runDescription: "run-description",
|
||||
metadata: { key: "value" },
|
||||
},
|
||||
);
|
||||
const dbRunObservation = await prisma.datasetRuns.findFirst({
|
||||
where: {
|
||||
name: "run-only-observation",
|
||||
name: "run + only + observation",
|
||||
},
|
||||
include: {
|
||||
datasetRunItems: true,
|
||||
@@ -313,21 +313,22 @@ describe("/api/public/datasets and /api/public/dataset-items API Endpoints", ()
|
||||
|
||||
const getRunAPI = await makeAPICall(
|
||||
"GET",
|
||||
`/api/public/datasets/dataset-name/runs/run-only-observation`,
|
||||
|
||||
`/api/public/datasets/${encodeURIComponent("dataset name")}/runs/${encodeURIComponent("run + only + observation")}`,
|
||||
);
|
||||
expect(getRunAPI.status).toBe(200);
|
||||
expect(getRunAPI.body).toMatchObject({
|
||||
name: "run-only-observation",
|
||||
name: "run + only + observation",
|
||||
description: "run-description",
|
||||
metadata: { key: "value" },
|
||||
datasetId: dataset.body.id,
|
||||
datasetName: "dataset-name",
|
||||
datasetName: "dataset name",
|
||||
datasetRunItems: expect.arrayContaining([
|
||||
expect.objectContaining({
|
||||
datasetItemId: "dataset-item-id",
|
||||
observationId: observationId,
|
||||
traceId: traceId,
|
||||
datasetRunName: "run-only-observation",
|
||||
datasetRunName: "run + only + observation",
|
||||
}),
|
||||
]),
|
||||
});
|
||||
|
||||
@@ -56,6 +56,49 @@ describe("/api/public/prompts API Endpoint", () => {
|
||||
expect(fetchedObservations.body.tags).toEqual([]);
|
||||
});
|
||||
|
||||
it("should fetch a prompt with special character", async () => {
|
||||
const promptId = uuidv4();
|
||||
|
||||
await prisma.prompt.create({
|
||||
data: {
|
||||
id: promptId,
|
||||
name: "prompt + name",
|
||||
prompt: "prompt",
|
||||
isActive: true,
|
||||
version: 1,
|
||||
config: {
|
||||
temperature: 0.1,
|
||||
},
|
||||
project: {
|
||||
connect: { id: "7a88fb47-b4e2-43b8-a06c-a5ce950dc53a" },
|
||||
},
|
||||
createdBy: "user-1",
|
||||
},
|
||||
});
|
||||
|
||||
const fetchedObservations = await makeAPICall(
|
||||
"GET",
|
||||
`/api/public/prompts?name=${encodeURIComponent("prompt + name")}&version=1`,
|
||||
undefined,
|
||||
);
|
||||
|
||||
expect(fetchedObservations.status).toBe(200);
|
||||
|
||||
if (!isPrompt(fetchedObservations.body)) {
|
||||
throw new Error("Expected body to be a prompt");
|
||||
}
|
||||
|
||||
expect(fetchedObservations.body.id).toBe(promptId);
|
||||
expect(fetchedObservations.body.name).toBe("prompt + name");
|
||||
expect(fetchedObservations.body.prompt).toBe("prompt");
|
||||
expect(fetchedObservations.body.type).toBe("text");
|
||||
expect(fetchedObservations.body.version).toBe(1);
|
||||
expect(fetchedObservations.body.isActive).toBe(true);
|
||||
expect(fetchedObservations.body.createdBy).toBe("user-1");
|
||||
expect(fetchedObservations.body.config).toEqual({ temperature: 0.1 });
|
||||
expect(fetchedObservations.body.tags).toEqual([]);
|
||||
});
|
||||
|
||||
it("should fetch active prompt only if no prompt version is given", async () => {
|
||||
const promptId = uuidv4();
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { AlertTriangle } from "lucide-react";
|
||||
import { AlertTriangle, Check } from "lucide-react";
|
||||
|
||||
import { VERSION } from "@/src/constants";
|
||||
import { env } from "@/src/env.mjs";
|
||||
@@ -42,10 +42,16 @@ export const LangfuseLogo = ({
|
||||
"flex items-center gap-2 self-stretch rounded-md px-3 py-2 text-xs ring-1 lg:-mx-2",
|
||||
env.NEXT_PUBLIC_LANGFUSE_CLOUD_REGION === "STAGING"
|
||||
? "bg-blue-100 text-blue-500 ring-blue-500"
|
||||
: "bg-red-100 text-red-500 ring-red-500",
|
||||
: env.NEXT_PUBLIC_LANGFUSE_CLOUD_REGION === "DEV"
|
||||
? "bg-green-100 text-green-500 ring-green-500"
|
||||
: "bg-red-100 text-red-500 ring-red-500",
|
||||
)}
|
||||
>
|
||||
<AlertTriangle size={16} />
|
||||
{env.NEXT_PUBLIC_LANGFUSE_CLOUD_REGION === "DEV" ? (
|
||||
<Check size={16} />
|
||||
) : (
|
||||
<AlertTriangle size={16} />
|
||||
)}
|
||||
<span className="whitespace-nowrap">
|
||||
{["EU", "US"].includes(env.NEXT_PUBLIC_LANGFUSE_CLOUD_REGION)
|
||||
? `PROD-${env.NEXT_PUBLIC_LANGFUSE_CLOUD_REGION}`
|
||||
|
||||
@@ -18,6 +18,7 @@ export default function Header({
|
||||
featureBetaURL?: string;
|
||||
actionButtons?: React.ReactNode;
|
||||
level?: "h2" | "h3";
|
||||
className?: string;
|
||||
}) {
|
||||
const router = useRouter();
|
||||
const session = useSession();
|
||||
@@ -42,7 +43,7 @@ export default function Header({
|
||||
[...props.breadcrumb.map((i) => i.href).filter(Boolean)].pop();
|
||||
|
||||
return (
|
||||
<div className={cn(level === "h2" ? "mb-4" : "mb-2")}>
|
||||
<div className={cn(level === "h2" ? "mb-4" : "mb-2", props.className)}>
|
||||
<div>
|
||||
{backHref ? (
|
||||
<nav className="sm:hidden" aria-label="Back">
|
||||
|
||||
@@ -461,13 +461,15 @@ export default function Layout(props: PropsWithChildren) {
|
||||
href={
|
||||
env.NEXT_PUBLIC_LANGFUSE_CLOUD_REGION === "EU"
|
||||
? "https://langfuse.com/docs/demo"
|
||||
: "https://docs-staging.langfuse.com/docs/demo"
|
||||
: "https://docs-staging.langfuse.com/docs/demo" // staging
|
||||
}
|
||||
target="_blank"
|
||||
>
|
||||
{env.NEXT_PUBLIC_LANGFUSE_CLOUD_REGION === "EU"
|
||||
? "Use Chat ↗"
|
||||
: "Use Chat (staging) ↗"}
|
||||
{
|
||||
env.NEXT_PUBLIC_LANGFUSE_CLOUD_REGION === "EU"
|
||||
? "Use Chat ↗"
|
||||
: "Use Chat (staging) ↗" // staging
|
||||
}
|
||||
</Link>
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
@@ -487,6 +487,7 @@ export default function GenerationsTable({ projectId }: GenerationsTableProps) {
|
||||
observationId={observationId}
|
||||
traceId={traceId}
|
||||
io="input"
|
||||
singleLine={rowHeight === "s"}
|
||||
/>
|
||||
);
|
||||
},
|
||||
@@ -505,6 +506,7 @@ export default function GenerationsTable({ projectId }: GenerationsTableProps) {
|
||||
observationId={observationId}
|
||||
traceId={traceId}
|
||||
io="output"
|
||||
singleLine={rowHeight === "s"}
|
||||
/>
|
||||
);
|
||||
},
|
||||
@@ -518,7 +520,9 @@ export default function GenerationsTable({ projectId }: GenerationsTableProps) {
|
||||
const values = row.getValue(
|
||||
"metadata",
|
||||
) as GenerationsTableRow["metadata"];
|
||||
return !!values ? <IOTableCell data={values} /> : null;
|
||||
return !!values ? (
|
||||
<IOTableCell data={values} singleLine={rowHeight === "s"} />
|
||||
) : null;
|
||||
},
|
||||
enableHiding: true,
|
||||
defaultHidden: true,
|
||||
@@ -673,10 +677,12 @@ const GenerationsIOCell = ({
|
||||
traceId,
|
||||
observationId,
|
||||
io,
|
||||
singleLine = false,
|
||||
}: {
|
||||
traceId: string;
|
||||
observationId: string;
|
||||
io: "input" | "output";
|
||||
singleLine: boolean;
|
||||
}) => {
|
||||
const observation = api.observations.byId.useQuery(
|
||||
{
|
||||
@@ -700,6 +706,7 @@ const GenerationsIOCell = ({
|
||||
io === "output" ? observation.data?.output : observation.data?.input
|
||||
}
|
||||
className={cn(io === "output" && "bg-green-50")}
|
||||
singleLine={singleLine}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -193,7 +193,11 @@ export default function ScoresTable({
|
||||
enableHiding: true,
|
||||
cell: ({ row }) => {
|
||||
const value = row.getValue("comment") as ScoresTableRow["comment"];
|
||||
return value !== undefined && <IOTableCell data={value} />;
|
||||
return (
|
||||
value !== undefined && (
|
||||
<IOTableCell data={value} singleLine={rowHeight === "s"} />
|
||||
)
|
||||
);
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
@@ -5,7 +5,7 @@ import { TraceTableMultiSelectAction } from "@/src/components/table/data-table-m
|
||||
import { DataTableToolbar } from "@/src/components/table/data-table-toolbar";
|
||||
import TableLink from "@/src/components/table/table-link";
|
||||
import { type LangfuseColumnDef } from "@/src/components/table/types";
|
||||
import { TagTracePopver } from "@/src/features/tag/components/TagTracePopver";
|
||||
import { TagTracePopover } from "@/src/features/tag/components/TagTracePopver";
|
||||
import { TokenUsageBadge } from "@/src/components/token-usage-badge";
|
||||
import { Checkbox } from "@/src/components/ui/checkbox";
|
||||
import useColumnVisibility from "@/src/features/column-visibility/hooks/useColumnVisibility";
|
||||
@@ -469,7 +469,13 @@ export default function TracesTable({
|
||||
id: "input",
|
||||
cell: ({ row }) => {
|
||||
const traceId: string = row.getValue("id");
|
||||
return <TracesIOCell traceId={traceId} io="input" />;
|
||||
return (
|
||||
<TracesIOCell
|
||||
traceId={traceId}
|
||||
io="input"
|
||||
singleLine={rowHeight === "s"}
|
||||
/>
|
||||
);
|
||||
},
|
||||
enableHiding: true,
|
||||
defaultHidden: true,
|
||||
@@ -480,7 +486,13 @@ export default function TracesTable({
|
||||
id: "output",
|
||||
cell: ({ row }) => {
|
||||
const traceId: string = row.getValue("id");
|
||||
return <TracesIOCell traceId={traceId} io="output" />;
|
||||
return (
|
||||
<TracesIOCell
|
||||
traceId={traceId}
|
||||
io="output"
|
||||
singleLine={rowHeight === "s"}
|
||||
/>
|
||||
);
|
||||
},
|
||||
enableHiding: true,
|
||||
defaultHidden: true,
|
||||
@@ -490,7 +502,7 @@ export default function TracesTable({
|
||||
header: "Metadata",
|
||||
cell: ({ row }) => {
|
||||
const values: string = row.getValue("metadata");
|
||||
return <IOTableCell data={values} />;
|
||||
return <IOTableCell data={values} singleLine={rowHeight === "s"} />;
|
||||
},
|
||||
enableHiding: true,
|
||||
},
|
||||
@@ -540,7 +552,7 @@ export default function TracesTable({
|
||||
const filterOptionTags = traceFilterOptions.data?.tags ?? [];
|
||||
const allTags = filterOptionTags.map((t) => t.value);
|
||||
return (
|
||||
<TagTracePopver
|
||||
<TagTracePopover
|
||||
tags={tags}
|
||||
availableTags={allTags}
|
||||
projectId={projectId}
|
||||
@@ -643,9 +655,11 @@ export default function TracesTable({
|
||||
const TracesIOCell = ({
|
||||
traceId,
|
||||
io,
|
||||
singleLine = false,
|
||||
}: {
|
||||
traceId: string;
|
||||
io: "input" | "output";
|
||||
singleLine?: boolean;
|
||||
}) => {
|
||||
const trace = api.traces.byId.useQuery(
|
||||
{ traceId: traceId },
|
||||
@@ -664,6 +678,7 @@ const TracesIOCell = ({
|
||||
isLoading={trace.isLoading}
|
||||
data={io === "output" ? trace.data?.output : trace.data?.input}
|
||||
className={cn(io === "output" && "bg-green-50")}
|
||||
singleLine={singleLine}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -110,18 +110,29 @@ export const IOTableCell = ({
|
||||
data,
|
||||
isLoading = false,
|
||||
className,
|
||||
singleLine = false,
|
||||
}: {
|
||||
data: unknown;
|
||||
isLoading?: boolean;
|
||||
className?: string;
|
||||
singleLine?: boolean;
|
||||
}) => {
|
||||
return (
|
||||
<>
|
||||
{isLoading ? (
|
||||
<JsonSkeleton className="h-full w-[400px] overflow-hidden px-2 py-1" />
|
||||
) : singleLine ? (
|
||||
<div
|
||||
className={cn(
|
||||
"h-full w-[400px] self-stretch overflow-hidden overflow-y-auto truncate rounded-sm border px-2 py-0.5",
|
||||
className,
|
||||
)}
|
||||
>
|
||||
{stringifyJsonNode(data)}
|
||||
</div>
|
||||
) : (
|
||||
<JSONView
|
||||
json={data}
|
||||
json={stringifyJsonNode(data)}
|
||||
className={cn(
|
||||
"h-full w-[400px] self-stretch overflow-y-auto rounded-sm ",
|
||||
className,
|
||||
|
||||
@@ -1 +1 @@
|
||||
export const VERSION = "v2.29.2";
|
||||
export const VERSION = "v2.30.2";
|
||||
|
||||
@@ -15,7 +15,8 @@ export type AuditableResource =
|
||||
| "session"
|
||||
| "apiKey"
|
||||
| "evalTemplate"
|
||||
| "job";
|
||||
| "job"
|
||||
| "posthogIntegration";
|
||||
|
||||
type AuditLog = {
|
||||
resourceType: AuditableResource;
|
||||
|
||||
@@ -12,18 +12,25 @@ const regions =
|
||||
flag: "🇪🇺",
|
||||
},
|
||||
]
|
||||
: [
|
||||
{
|
||||
name: "US",
|
||||
hostname: "us.cloud.langfuse.com",
|
||||
flag: "🇺🇸",
|
||||
},
|
||||
{
|
||||
name: "EU",
|
||||
hostname: "cloud.langfuse.com",
|
||||
flag: "🇪🇺",
|
||||
},
|
||||
];
|
||||
: env.NEXT_PUBLIC_LANGFUSE_CLOUD_REGION === "DEV"
|
||||
? [
|
||||
{
|
||||
name: "DEV",
|
||||
flag: "🚧",
|
||||
},
|
||||
]
|
||||
: [
|
||||
{
|
||||
name: "US",
|
||||
hostname: "us.cloud.langfuse.com",
|
||||
flag: "🇺🇸",
|
||||
},
|
||||
{
|
||||
name: "EU",
|
||||
hostname: "cloud.langfuse.com",
|
||||
flag: "🇪🇺",
|
||||
},
|
||||
];
|
||||
|
||||
export function CloudRegionSwitch({
|
||||
isSignUpPage,
|
||||
@@ -66,7 +73,8 @@ export function CloudRegionSwitch({
|
||||
send_instantly: true,
|
||||
},
|
||||
);
|
||||
window.location.hostname = region.hostname;
|
||||
if ("hostname" in region)
|
||||
window.location.hostname = region.hostname;
|
||||
}}
|
||||
>
|
||||
<span className="mr-2 text-xl leading-none">{region.flag}</span>
|
||||
|
||||
@@ -155,7 +155,9 @@ export function DatasetItemsTable({
|
||||
enableHiding: true,
|
||||
cell: ({ row }) => {
|
||||
const input = row.getValue("input") as RowData["input"];
|
||||
return !!input ? <IOTableCell data={input} /> : null;
|
||||
return !!input ? (
|
||||
<IOTableCell data={input} singleLine={rowHeight === "s"} />
|
||||
) : null;
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -168,7 +170,11 @@ export function DatasetItemsTable({
|
||||
"expectedOutput",
|
||||
) as RowData["expectedOutput"];
|
||||
return !!expectedOutput ? (
|
||||
<IOTableCell data={expectedOutput} className="bg-green-50" />
|
||||
<IOTableCell
|
||||
data={expectedOutput}
|
||||
className="bg-green-50"
|
||||
singleLine={rowHeight === "s"}
|
||||
/>
|
||||
) : null;
|
||||
},
|
||||
},
|
||||
@@ -179,7 +185,9 @@ export function DatasetItemsTable({
|
||||
enableHiding: true,
|
||||
cell: ({ row }) => {
|
||||
const metadata = row.getValue("metadata") as RowData["metadata"];
|
||||
return !!metadata ? <IOTableCell data={metadata} /> : null;
|
||||
return !!metadata ? (
|
||||
<IOTableCell data={metadata} singleLine={rowHeight === "s"} />
|
||||
) : null;
|
||||
},
|
||||
},
|
||||
{
|
||||
|
||||
@@ -161,6 +161,7 @@ export function DatasetRunItemsTable(
|
||||
traceId={trace.traceId}
|
||||
observationId={trace.observationId}
|
||||
io="input"
|
||||
singleLine={rowHeight === "s"}
|
||||
/>
|
||||
) : null;
|
||||
},
|
||||
@@ -177,6 +178,7 @@ export function DatasetRunItemsTable(
|
||||
traceId={trace.traceId}
|
||||
observationId={trace.observationId}
|
||||
io="output"
|
||||
singleLine={rowHeight === "s"}
|
||||
/>
|
||||
) : null;
|
||||
},
|
||||
@@ -194,6 +196,7 @@ export function DatasetRunItemsTable(
|
||||
datasetId={props.datasetId}
|
||||
datasetItemId={datasetItemId}
|
||||
io="expectedOutput"
|
||||
singleLine={rowHeight === "s"}
|
||||
/>
|
||||
) : null;
|
||||
},
|
||||
@@ -271,10 +274,12 @@ const TraceObservationIOCell = ({
|
||||
traceId,
|
||||
observationId,
|
||||
io,
|
||||
singleLine = false,
|
||||
}: {
|
||||
traceId: string;
|
||||
observationId?: string;
|
||||
io: "input" | "output";
|
||||
singleLine?: boolean;
|
||||
}) => {
|
||||
// conditionally fetch the trace or observation depending on the presence of observationId
|
||||
const trace = api.traces.byId.useQuery(
|
||||
@@ -312,6 +317,7 @@ const TraceObservationIOCell = ({
|
||||
isLoading={!!!observationId ? trace.isLoading : observation.isLoading}
|
||||
data={io === "output" ? data?.output : data?.input}
|
||||
className={cn(io === "output" && "bg-green-50")}
|
||||
singleLine={singleLine}
|
||||
/>
|
||||
);
|
||||
};
|
||||
@@ -321,11 +327,13 @@ const DatasetItemIOCell = ({
|
||||
datasetId,
|
||||
datasetItemId,
|
||||
io,
|
||||
singleLine = false,
|
||||
}: {
|
||||
projectId: string;
|
||||
datasetId: string;
|
||||
datasetItemId: string;
|
||||
io: "expectedOutput" | "input";
|
||||
singleLine?: boolean;
|
||||
}) => {
|
||||
const datasetItem = api.datasets.itemById.useQuery(
|
||||
{
|
||||
@@ -351,6 +359,7 @@ const DatasetItemIOCell = ({
|
||||
? datasetItem.data?.expectedOutput
|
||||
: datasetItem.data?.input
|
||||
}
|
||||
singleLine={singleLine}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -139,7 +139,9 @@ export function DatasetRunsTable(props: {
|
||||
enableHiding: true,
|
||||
cell: ({ row }) => {
|
||||
const metadata: RowData["metadata"] = row.getValue("metadata");
|
||||
return !!metadata ? <IOTableCell data={metadata} /> : null;
|
||||
return !!metadata ? (
|
||||
<IOTableCell data={metadata} singleLine={rowHeight === "s"} />
|
||||
) : null;
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
@@ -113,7 +113,9 @@ export function DatasetsTable(props: { projectId: string }) {
|
||||
enableHiding: true,
|
||||
cell: ({ row }) => {
|
||||
const metadata: RowData["metadata"] = row.getValue("metadata");
|
||||
return !!metadata ? <IOTableCell data={metadata} /> : null;
|
||||
return !!metadata ? (
|
||||
<IOTableCell data={metadata} singleLine={rowHeight === "s"} />
|
||||
) : null;
|
||||
},
|
||||
},
|
||||
{
|
||||
|
||||
@@ -0,0 +1,117 @@
|
||||
import { z } from "zod";
|
||||
|
||||
import { auditLog } from "@/src/features/audit-logs/auditLog";
|
||||
import { throwIfNoAccess } from "@/src/features/rbac/utils/checkAccess";
|
||||
import {
|
||||
createTRPCRouter,
|
||||
protectedProjectProcedure,
|
||||
} from "@/src/server/api/trpc";
|
||||
import { decrypt, encrypt } from "@langfuse/shared/encryption";
|
||||
import { posthogIntegrationFormSchema } from "@/src/features/posthog-integration/types";
|
||||
import { TRPCError } from "@trpc/server";
|
||||
|
||||
export const posthogIntegrationRouter = createTRPCRouter({
|
||||
get: protectedProjectProcedure
|
||||
.input(z.object({ projectId: z.string() }))
|
||||
.query(async ({ input, ctx }) => {
|
||||
throwIfNoAccess({
|
||||
session: ctx.session,
|
||||
projectId: input.projectId,
|
||||
scope: "integrations:CRUD",
|
||||
});
|
||||
try {
|
||||
const dbConfig = await ctx.prisma.posthogIntegration.findFirst({
|
||||
where: {
|
||||
projectId: input.projectId,
|
||||
},
|
||||
});
|
||||
|
||||
if (!dbConfig) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const { encryptedPosthogApiKey, ...config } = dbConfig;
|
||||
|
||||
return {
|
||||
...config,
|
||||
posthogApiKey: decrypt(encryptedPosthogApiKey),
|
||||
};
|
||||
} catch (e) {
|
||||
console.error("posthog integration get", e);
|
||||
throw new TRPCError({
|
||||
code: "INTERNAL_SERVER_ERROR",
|
||||
});
|
||||
}
|
||||
}),
|
||||
|
||||
update: protectedProjectProcedure
|
||||
.input(posthogIntegrationFormSchema.extend({ projectId: z.string() }))
|
||||
.mutation(async ({ input, ctx }) => {
|
||||
try {
|
||||
throwIfNoAccess({
|
||||
session: ctx.session,
|
||||
projectId: input.projectId,
|
||||
scope: "integrations:CRUD",
|
||||
});
|
||||
await auditLog({
|
||||
session: ctx.session,
|
||||
action: "update",
|
||||
resourceType: "posthogIntegration",
|
||||
resourceId: input.projectId,
|
||||
});
|
||||
const { posthogProjectApiKey, ...config } = input;
|
||||
|
||||
const encryptedPosthogApiKey = encrypt(posthogProjectApiKey);
|
||||
|
||||
await ctx.prisma.posthogIntegration.upsert({
|
||||
where: {
|
||||
projectId: input.projectId,
|
||||
},
|
||||
create: {
|
||||
projectId: input.projectId,
|
||||
posthogHostName: config.posthogHostname,
|
||||
encryptedPosthogApiKey,
|
||||
enabled: config.enabled,
|
||||
},
|
||||
update: {
|
||||
encryptedPosthogApiKey,
|
||||
posthogHostName: config.posthogHostname,
|
||||
enabled: config.enabled,
|
||||
},
|
||||
});
|
||||
} catch (e) {
|
||||
console.log("posthog integration update", e);
|
||||
throw new TRPCError({
|
||||
code: "INTERNAL_SERVER_ERROR",
|
||||
});
|
||||
}
|
||||
}),
|
||||
delete: protectedProjectProcedure
|
||||
.input(z.object({ projectId: z.string() }))
|
||||
.mutation(async ({ input, ctx }) => {
|
||||
try {
|
||||
throwIfNoAccess({
|
||||
session: ctx.session,
|
||||
projectId: input.projectId,
|
||||
scope: "integrations:CRUD",
|
||||
});
|
||||
await auditLog({
|
||||
session: ctx.session,
|
||||
action: "delete",
|
||||
resourceType: "posthogIntegration",
|
||||
resourceId: input.projectId,
|
||||
});
|
||||
|
||||
await ctx.prisma.posthogIntegration.delete({
|
||||
where: {
|
||||
projectId: input.projectId,
|
||||
},
|
||||
});
|
||||
} catch (e) {
|
||||
console.log("posthog integration delete", e);
|
||||
throw new TRPCError({
|
||||
code: "INTERNAL_SERVER_ERROR",
|
||||
});
|
||||
}
|
||||
}),
|
||||
});
|
||||
@@ -0,0 +1,9 @@
|
||||
import { z } from "zod";
|
||||
|
||||
export const posthogIntegrationFormSchema = z.object({
|
||||
posthogHostname: z.string().url(),
|
||||
posthogProjectApiKey: z.string().refine((v) => v.startsWith("phc_"), {
|
||||
message: "PostHog Project API Key must start with 'phc_'",
|
||||
}),
|
||||
enabled: z.boolean(),
|
||||
});
|
||||
@@ -50,7 +50,7 @@ export const CreatePromptTRPCSchema = z.union([
|
||||
export type CreatePromptTRPCType = z.infer<typeof CreatePromptTRPCSchema>;
|
||||
|
||||
export const GetPromptSchema = z.object({
|
||||
name: z.string(),
|
||||
name: z.string().transform((v) => decodeURIComponent(v)),
|
||||
version: z.coerce.number().int().nullish(),
|
||||
});
|
||||
|
||||
|
||||
@@ -20,6 +20,7 @@ const scopes = [
|
||||
"project:delete",
|
||||
"project:update",
|
||||
"project:transfer",
|
||||
"integrations:CRUD",
|
||||
|
||||
"datasets:CUD",
|
||||
|
||||
@@ -45,6 +46,7 @@ export const roleAccessRights: Record<MembershipRole, Scope[]> = {
|
||||
"apiKeys:read",
|
||||
"apiKeys:create",
|
||||
"apiKeys:delete",
|
||||
"integrations:CRUD",
|
||||
"objects:publish",
|
||||
"objects:bookmark",
|
||||
"objects:tag",
|
||||
@@ -70,6 +72,7 @@ export const roleAccessRights: Record<MembershipRole, Scope[]> = {
|
||||
"apiKeys:read",
|
||||
"apiKeys:create",
|
||||
"apiKeys:delete",
|
||||
"integrations:CRUD",
|
||||
"objects:publish",
|
||||
"objects:bookmark",
|
||||
"objects:tag",
|
||||
|
||||
@@ -4,7 +4,7 @@ import { useHasAccess } from "@/src/features/rbac/utils/checkAccess";
|
||||
import { type RouterOutput, type RouterInput } from "@/src/utils/types";
|
||||
import TagManager from "@/src/features/tag/components/TagMananger";
|
||||
|
||||
type TagTracePopverProps = {
|
||||
type TagTracePopoverProps = {
|
||||
tags: string[];
|
||||
availableTags: string[];
|
||||
projectId: string;
|
||||
@@ -12,13 +12,13 @@ type TagTracePopverProps = {
|
||||
tracesFilter: RouterInput["traces"]["all"];
|
||||
};
|
||||
|
||||
export function TagTracePopver({
|
||||
export function TagTracePopover({
|
||||
tags,
|
||||
availableTags,
|
||||
projectId,
|
||||
traceId,
|
||||
tracesFilter,
|
||||
}: TagTracePopverProps) {
|
||||
}: TagTracePopoverProps) {
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
const hasAccess = useHasAccess({ projectId, scope: "objects:tag" });
|
||||
|
||||
|
||||
@@ -84,7 +84,8 @@ export default async function handler(
|
||||
if (
|
||||
env.LANGFUSE_NEW_USER_SIGNUP_WEBHOOK &&
|
||||
env.NEXT_PUBLIC_LANGFUSE_CLOUD_REGION &&
|
||||
env.NEXT_PUBLIC_LANGFUSE_CLOUD_REGION !== "STAGING"
|
||||
env.NEXT_PUBLIC_LANGFUSE_CLOUD_REGION !== "STAGING" &&
|
||||
env.NEXT_PUBLIC_LANGFUSE_CLOUD_REGION !== "DEV"
|
||||
) {
|
||||
await fetch(env.LANGFUSE_NEW_USER_SIGNUP_WEBHOOK, {
|
||||
method: "POST",
|
||||
|
||||
@@ -10,7 +10,10 @@ export default async function handler(
|
||||
if (!process.env.NEXT_PUBLIC_POSTHOG_KEY)
|
||||
return res.status(200).json({ message: "No PostHog key provided" });
|
||||
|
||||
if (env.NEXT_PUBLIC_LANGFUSE_CLOUD_REGION === undefined)
|
||||
if (
|
||||
env.NEXT_PUBLIC_LANGFUSE_CLOUD_REGION === undefined ||
|
||||
env.NEXT_PUBLIC_LANGFUSE_CLOUD_REGION === "DEV"
|
||||
)
|
||||
return res.status(200).json({
|
||||
message: "Only runs on Langfuse Cloud, no LANGFUSE_CLOUD_REGION provided",
|
||||
});
|
||||
|
||||
@@ -6,7 +6,7 @@ import { verifyAuthHeaderAndReturnScope } from "@/src/features/public-api/server
|
||||
import { isPrismaException } from "@/src/utils/exceptions";
|
||||
|
||||
const DatasetsGetSchema = z.object({
|
||||
name: z.string(),
|
||||
name: z.string().transform((val) => decodeURIComponent(val)),
|
||||
});
|
||||
|
||||
export default async function handler(
|
||||
|
||||
@@ -6,8 +6,8 @@ import { verifyAuthHeaderAndReturnScope } from "@/src/features/public-api/server
|
||||
import { isPrismaException } from "@/src/utils/exceptions";
|
||||
|
||||
const DatasetRunsGetSchema = z.object({
|
||||
name: z.string(),
|
||||
runName: z.string(),
|
||||
name: z.string().transform((val) => decodeURIComponent(val)),
|
||||
runName: z.string().transform((val) => decodeURIComponent(val)),
|
||||
});
|
||||
|
||||
export default async function handler(
|
||||
|
||||
@@ -427,7 +427,8 @@ export const sendToWorkerIfEnvironmentConfigured = async (
|
||||
if (
|
||||
env.LANGFUSE_WORKER_HOST &&
|
||||
env.LANGFUSE_WORKER_PASSWORD &&
|
||||
env.NEXT_PUBLIC_LANGFUSE_CLOUD_REGION
|
||||
env.NEXT_PUBLIC_LANGFUSE_CLOUD_REGION &&
|
||||
env.NEXT_PUBLIC_LANGFUSE_CLOUD_REGION !== "DEV"
|
||||
) {
|
||||
const traceEvents = batchResults
|
||||
.filter((result) => result.type === eventTypes.TRACE_CREATE) // we only have create, no update.
|
||||
|
||||
@@ -108,6 +108,7 @@ export default async function handler(
|
||||
SELECT
|
||||
DATE_TRUNC('DAY', t.timestamp) "date",
|
||||
count(distinct t.id)::integer count_traces,
|
||||
count(distinct o.id)::integer count_observations,
|
||||
SUM(o.calculated_total_cost)::DOUBLE PRECISION total_cost
|
||||
FROM traces t
|
||||
LEFT JOIN observations_view o ON o.project_id = t.project_id AND t.id = o.trace_id
|
||||
@@ -122,6 +123,7 @@ export default async function handler(
|
||||
SELECT
|
||||
TO_CHAR(COALESCE(ds.date, daily_model_usage.date), 'YYYY-MM-DD') AS "date",
|
||||
COALESCE(count_traces, 0) "countTraces",
|
||||
COALESCE(count_observations, 0) "countObservations",
|
||||
COALESCE(total_cost, 0) "totalCost",
|
||||
COALESCE(daily_usage_json, '[]'::JSON) usage
|
||||
FROM
|
||||
|
||||
@@ -19,7 +19,7 @@ import { TbBrandAzure } from "react-icons/tb";
|
||||
import { signIn } from "next-auth/react";
|
||||
import Head from "next/head";
|
||||
import Link from "next/link";
|
||||
import { useState } from "react";
|
||||
import { useState, useEffect } from "react";
|
||||
import { useForm } from "react-hook-form";
|
||||
import * as z from "zod";
|
||||
import { usePostHog } from "posthog-js/react";
|
||||
@@ -30,6 +30,8 @@ import { PasswordInput } from "@/src/components/ui/password-input";
|
||||
import { Turnstile } from "@marsidev/react-turnstile";
|
||||
import { isAnySsoConfigured } from "@langfuse/ee/sso";
|
||||
import { Shield } from "lucide-react";
|
||||
import { useRouter } from "next/router";
|
||||
import { captureException } from "@sentry/nextjs";
|
||||
|
||||
const credentialAuthForm = z.object({
|
||||
email: z.string().email(),
|
||||
@@ -169,10 +171,35 @@ export function SSOButtons({
|
||||
);
|
||||
}
|
||||
|
||||
const signInErrors = [
|
||||
{
|
||||
code: "OAuthAccountNotLinked",
|
||||
description:
|
||||
"Please sign in with the same provider that you used to create this account.",
|
||||
},
|
||||
];
|
||||
|
||||
export default function SignIn({ authProviders, signUpDisabled }: PageProps) {
|
||||
const router = useRouter();
|
||||
|
||||
// handle NextAuth error codes: https://next-auth.js.org/configuration/pages#sign-in-page
|
||||
const nextAuthError =
|
||||
typeof router.query.error === "string"
|
||||
? decodeURIComponent(router.query.error)
|
||||
: null;
|
||||
const nextAuthErrorDescription = signInErrors.find(
|
||||
(e) => e.code === nextAuthError,
|
||||
)?.description;
|
||||
useEffect(() => {
|
||||
// log unexpected sign in errors to Sentry
|
||||
if (nextAuthError && !nextAuthErrorDescription) {
|
||||
captureException(new Error(`Sign in error: ${nextAuthError}`));
|
||||
}
|
||||
}, [nextAuthError, nextAuthErrorDescription]);
|
||||
|
||||
const [credentialsFormError, setCredentialsFormError] = useState<
|
||||
string | null
|
||||
>(null);
|
||||
>(nextAuthErrorDescription ?? nextAuthError);
|
||||
const [ssoLoading, setSsoLoading] = useState<boolean>(false);
|
||||
|
||||
const posthog = usePostHog();
|
||||
|
||||
@@ -63,9 +63,11 @@ export default function SignIn({ authProviders }: PageProps) {
|
||||
await signIn<"credentials">("credentials", {
|
||||
email: values.email,
|
||||
password: values.password,
|
||||
callbackUrl: env.NEXT_PUBLIC_LANGFUSE_CLOUD_REGION
|
||||
? "/onboarding"
|
||||
: "/?getStarted=1",
|
||||
callbackUrl:
|
||||
env.NEXT_PUBLIC_LANGFUSE_CLOUD_REGION &&
|
||||
env.NEXT_PUBLIC_LANGFUSE_CLOUD_REGION !== "DEV"
|
||||
? "/onboarding"
|
||||
: "/?getStarted=1",
|
||||
turnstileToken,
|
||||
});
|
||||
} catch (err) {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { ChevronRightIcon } from "@heroicons/react/20/solid";
|
||||
import { CommandLineIcon, RocketLaunchIcon } from "@heroicons/react/24/outline";
|
||||
import { SiPython } from "react-icons/si";
|
||||
import { SiOpenai, SiPython } from "react-icons/si";
|
||||
import Header from "@/src/components/layouts/header";
|
||||
import { ApiKeyList } from "@/src/features/public-api/components/ApiKeyList";
|
||||
import { useRouter } from "next/router";
|
||||
@@ -15,10 +15,6 @@ import { env } from "@/src/env.mjs";
|
||||
import { Card } from "@tremor/react";
|
||||
import { Button } from "@/src/components/ui/button";
|
||||
import Link from "next/link";
|
||||
import {
|
||||
sendUserChatMessage,
|
||||
showAgentChatMessage,
|
||||
} from "@/src/features/support-chat/chat";
|
||||
|
||||
export default function SettingsPage() {
|
||||
const router = useRouter();
|
||||
@@ -31,7 +27,7 @@ export default function SettingsPage() {
|
||||
<ApiKeyList projectId={projectId} />
|
||||
<ProjectMembersTable projectId={projectId} />
|
||||
<ProjectUsageChart projectId={projectId} />
|
||||
<Beta />
|
||||
<Integrations projectId={projectId} />
|
||||
<Instructions />
|
||||
<RenameProject projectId={projectId} />
|
||||
<div className="space-y-3">
|
||||
@@ -58,23 +54,36 @@ const instructionItems = [
|
||||
icon: RocketLaunchIcon,
|
||||
},
|
||||
{
|
||||
name: "Langchain integration",
|
||||
name: "OpenAI SDK Integration",
|
||||
description: "Trace your OpenAI API calls with a single line of code",
|
||||
href: "https://langfuse.com/docs/integrations/openai",
|
||||
icon: SiOpenai,
|
||||
},
|
||||
{
|
||||
name: "Langchain Integration",
|
||||
description:
|
||||
"Trace your Langchain llm/chain/agent/... with a single line of code",
|
||||
href: "https://langfuse.com/docs/langchain",
|
||||
href: "https://langfuse.com/docs/integrations/langchain",
|
||||
icon: Bird,
|
||||
},
|
||||
{
|
||||
name: "LlamaIndex Integration",
|
||||
description:
|
||||
"Trace your Llamaindex RAG application by adding the global callback handler",
|
||||
href: "https://langfuse.com/docs/integrations/llama-index",
|
||||
icon: Code,
|
||||
},
|
||||
{
|
||||
name: "Typescript SDK",
|
||||
description: "npm install langfuse",
|
||||
href: "https://langfuse.com/docs/sdk/typescript",
|
||||
icon: CommandLineIcon,
|
||||
icon: Code,
|
||||
},
|
||||
{
|
||||
name: "Python SDK",
|
||||
name: "Python SDK (Decorator)",
|
||||
description: "pip install langfuse",
|
||||
href: "https://langfuse.com/docs/sdk/python",
|
||||
icon: SiPython,
|
||||
icon: Code,
|
||||
},
|
||||
{
|
||||
name: "API Reference (Swagger)",
|
||||
@@ -123,12 +132,12 @@ function Instructions() {
|
||||
);
|
||||
}
|
||||
|
||||
const Beta = () => {
|
||||
const Integrations = (props: { projectId: string }) => {
|
||||
if (env.NEXT_PUBLIC_LANGFUSE_CLOUD_REGION === undefined) return null;
|
||||
|
||||
return (
|
||||
<div>
|
||||
<Header title="Early Access" level="h3" />
|
||||
<Header title="Integrations" level="h3" />
|
||||
<Card className="p-4 lg:w-1/2">
|
||||
{/* eslint-disable-next-line @next/next/no-img-element */}
|
||||
<img
|
||||
@@ -141,16 +150,12 @@ const Beta = () => {
|
||||
Langfuse Events/Metrics available in your Posthog Dashboards.
|
||||
</p>
|
||||
<div className="flex items-center gap-2">
|
||||
<Button
|
||||
variant="secondary"
|
||||
onClick={() => {
|
||||
sendUserChatMessage(
|
||||
"I am interested to join the PostHog Integration Beta",
|
||||
);
|
||||
showAgentChatMessage("We'll be in touch to get you set up!");
|
||||
}}
|
||||
>
|
||||
Get Access
|
||||
<Button variant="secondary" asChild>
|
||||
<Link
|
||||
href={`/project/${props.projectId}/settings/posthog-integration`}
|
||||
>
|
||||
Configure
|
||||
</Link>
|
||||
</Button>
|
||||
<Button asChild variant="ghost">
|
||||
<Link href="https://langfuse.com/docs/analytics/posthog">
|
||||
|
||||
@@ -0,0 +1,222 @@
|
||||
import Header from "@/src/components/layouts/header";
|
||||
import { Button } from "@/src/components/ui/button";
|
||||
import {
|
||||
Form,
|
||||
FormControl,
|
||||
FormDescription,
|
||||
FormField,
|
||||
FormItem,
|
||||
FormLabel,
|
||||
FormMessage,
|
||||
} from "@/src/components/ui/form";
|
||||
import { Input } from "@/src/components/ui/input";
|
||||
import { PasswordInput } from "@/src/components/ui/password-input";
|
||||
import { Switch } from "@/src/components/ui/switch";
|
||||
import { env } from "@/src/env.mjs";
|
||||
import { posthogIntegrationFormSchema } from "@/src/features/posthog-integration/types";
|
||||
import { api } from "@/src/utils/api";
|
||||
import { type RouterOutput } from "@/src/utils/types";
|
||||
import { zodResolver } from "@hookform/resolvers/zod";
|
||||
import { Card } from "@tremor/react";
|
||||
import Link from "next/link";
|
||||
import { useRouter } from "next/router";
|
||||
import posthog from "posthog-js";
|
||||
import { useEffect } from "react";
|
||||
import { useForm } from "react-hook-form";
|
||||
import { type z } from "zod";
|
||||
|
||||
export default function PosthogIntegrationSettings() {
|
||||
const router = useRouter();
|
||||
const projectId = router.query.projectId as string;
|
||||
const state = api.posthogIntegration.get.useQuery({ projectId });
|
||||
|
||||
if (env.NEXT_PUBLIC_LANGFUSE_CLOUD_REGION === undefined) return null;
|
||||
|
||||
return (
|
||||
<div className="md:container">
|
||||
<Header
|
||||
title="PostHog Integration"
|
||||
breadcrumb={[
|
||||
{ name: "Settings", href: `/project/${projectId}/settings` },
|
||||
]}
|
||||
actionButtons={
|
||||
<Button asChild variant="secondary">
|
||||
<Link href="https://langfuse.com/docs/analytics/posthog">
|
||||
Integration Docs
|
||||
</Link>
|
||||
</Button>
|
||||
}
|
||||
status={state.data?.enabled ? "active" : "inactive"}
|
||||
/>
|
||||
<p className="mb-4 text-sm text-gray-700">
|
||||
We have teamed up with{" "}
|
||||
<Link href="https://posthog.com" className="underline">
|
||||
PostHog
|
||||
</Link>{" "}
|
||||
(OSS product analytics) to make Langfuse events/metrics available in
|
||||
your Posthog Dashboards. While in Beta, this integration syncs metrics
|
||||
on a daily schedule to PostHog. When first activated, it will sync all
|
||||
historical data from the beginning of your project.
|
||||
</p>
|
||||
<div className="flex flex-col gap-10"></div>
|
||||
|
||||
{!state.isInitialLoading && (
|
||||
<>
|
||||
<Header level="h3" title="Configuration" />
|
||||
<Card className="p-4">
|
||||
<PostHogIntegrationSettings
|
||||
state={state.data}
|
||||
projectId={projectId}
|
||||
/>
|
||||
</Card>
|
||||
</>
|
||||
)}
|
||||
{state.data?.enabled && (
|
||||
<>
|
||||
<Header level="h3" title="Status" className="mt-8" />
|
||||
<p className="text-sm text-gray-700">
|
||||
Data synced until:{" "}
|
||||
{state.data?.lastSyncAt
|
||||
? new Date(state.data.lastSyncAt).toLocaleString()
|
||||
: "Never (pending)"}
|
||||
</p>
|
||||
<p className="mt-2 text-sm text-gray-700">
|
||||
While in Beta, the sync is scheduled to run once a day.
|
||||
</p>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
const PostHogIntegrationSettings = ({
|
||||
state,
|
||||
projectId,
|
||||
}: {
|
||||
state?: RouterOutput["posthogIntegration"]["get"];
|
||||
projectId: string;
|
||||
}) => {
|
||||
const posthogForm = useForm<z.infer<typeof posthogIntegrationFormSchema>>({
|
||||
resolver: zodResolver(posthogIntegrationFormSchema),
|
||||
defaultValues: {
|
||||
posthogHostname: state?.posthogHostName ?? "",
|
||||
posthogProjectApiKey: state?.posthogApiKey ?? "",
|
||||
enabled: state?.enabled ?? false,
|
||||
},
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
posthogForm.reset({
|
||||
posthogHostname: state?.posthogHostName ?? "",
|
||||
posthogProjectApiKey: state?.posthogApiKey ?? "",
|
||||
enabled: state?.enabled ?? false,
|
||||
});
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [state]);
|
||||
|
||||
const utils = api.useUtils();
|
||||
const mut = api.posthogIntegration.update.useMutation({
|
||||
onSuccess: () => {
|
||||
utils.posthogIntegration.invalidate();
|
||||
},
|
||||
});
|
||||
const mutDelete = api.posthogIntegration.delete.useMutation({
|
||||
onSuccess: () => {
|
||||
utils.posthogIntegration.invalidate();
|
||||
},
|
||||
});
|
||||
|
||||
async function onSubmit(
|
||||
values: z.infer<typeof posthogIntegrationFormSchema>,
|
||||
) {
|
||||
posthog.capture("integrations:posthog_form_submitted");
|
||||
mut.mutate({
|
||||
projectId,
|
||||
...values,
|
||||
});
|
||||
console.log(values);
|
||||
}
|
||||
|
||||
return (
|
||||
<Form {...posthogForm}>
|
||||
<form
|
||||
className="space-y-3"
|
||||
// eslint-disable-next-line @typescript-eslint/no-misused-promises
|
||||
onSubmit={posthogForm.handleSubmit(onSubmit)}
|
||||
>
|
||||
<FormField
|
||||
control={posthogForm.control}
|
||||
name="posthogHostname"
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormLabel>Posthog Hostname</FormLabel>
|
||||
<FormControl>
|
||||
<Input {...field} />
|
||||
</FormControl>
|
||||
<FormDescription>
|
||||
US region: https://us.posthog.com; EU region:
|
||||
https://eu.posthog.com
|
||||
</FormDescription>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
<FormField
|
||||
control={posthogForm.control}
|
||||
name="posthogProjectApiKey"
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormLabel>Posthog Project API Key</FormLabel>
|
||||
<FormControl>
|
||||
<PasswordInput {...field} />
|
||||
</FormControl>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
<FormField
|
||||
control={posthogForm.control}
|
||||
name="enabled"
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormLabel>Enabled</FormLabel>
|
||||
<FormControl>
|
||||
<Switch
|
||||
id="posthog-integration-enabled"
|
||||
checked={field.value}
|
||||
onCheckedChange={() => {
|
||||
field.onChange(!field.value);
|
||||
}}
|
||||
className="ml-4 mt-1"
|
||||
/>
|
||||
</FormControl>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
</form>
|
||||
<div className="mt-8 flex gap-2">
|
||||
<Button
|
||||
loading={mut.isLoading}
|
||||
onClick={posthogForm.handleSubmit(onSubmit)}
|
||||
>
|
||||
Save
|
||||
</Button>
|
||||
<Button
|
||||
variant="ghost"
|
||||
loading={mutDelete.isLoading}
|
||||
onClick={() => {
|
||||
if (
|
||||
confirm(
|
||||
"Are you sure you want to reset the PostHog integration for this project?",
|
||||
)
|
||||
)
|
||||
mutDelete.mutate({ projectId });
|
||||
}}
|
||||
>
|
||||
Reset
|
||||
</Button>
|
||||
</div>
|
||||
</Form>
|
||||
);
|
||||
};
|
||||
@@ -15,6 +15,7 @@ import { sessionRouter } from "@/src/server/api/routers/sessions";
|
||||
import { promptRouter } from "@/src/features/prompts/server/prompt-router";
|
||||
import { modelRouter } from "@/src/server/api/routers/models";
|
||||
import { evalRouter } from "@/src/features/evals/server/router";
|
||||
import { posthogIntegrationRouter } from "@/src/features/posthog-integration/posthog-integration-router";
|
||||
|
||||
/**
|
||||
* This is the primary router for your server.
|
||||
@@ -38,6 +39,7 @@ export const appRouter = createTRPCRouter({
|
||||
prompts: promptRouter,
|
||||
models: modelRouter,
|
||||
evals: evalRouter,
|
||||
posthogIntegration: posthogIntegrationRouter,
|
||||
});
|
||||
|
||||
// export type definition of API
|
||||
|
||||
@@ -19,17 +19,19 @@ export const userRouter = createTRPCRouter({
|
||||
all: protectedProjectProcedure
|
||||
.input(UserAllOptions)
|
||||
.query(async ({ input, ctx }) => {
|
||||
const uniqueUsers = await ctx.prisma.trace.findMany({
|
||||
where: {
|
||||
projectId: input.projectId,
|
||||
},
|
||||
select: {
|
||||
userId: true,
|
||||
},
|
||||
distinct: ["userId"],
|
||||
});
|
||||
const totalUsers = (
|
||||
await ctx.prisma.$queryRaw<
|
||||
Array<{
|
||||
totalCount: number;
|
||||
}>
|
||||
>`
|
||||
SELECT COUNT(DISTINCT t.user_id)::int AS "totalCount"
|
||||
FROM traces t
|
||||
WHERE t.project_id = ${input.projectId}
|
||||
`
|
||||
)[0].totalCount;
|
||||
|
||||
const topUsers = await ctx.prisma.$queryRaw<
|
||||
const users = await ctx.prisma.$queryRaw<
|
||||
Array<{
|
||||
userId: string;
|
||||
totalTraces: number;
|
||||
@@ -52,8 +54,8 @@ export const userRouter = createTRPCRouter({
|
||||
${input.limit} OFFSET ${input.page * input.limit};
|
||||
`;
|
||||
return {
|
||||
totalUsers: uniqueUsers.length,
|
||||
users: topUsers,
|
||||
totalUsers,
|
||||
users,
|
||||
};
|
||||
}),
|
||||
|
||||
|
||||
@@ -324,7 +324,8 @@ export async function getAuthOptions(): Promise<NextAuthOptions> {
|
||||
if (
|
||||
env.LANGFUSE_NEW_USER_SIGNUP_WEBHOOK &&
|
||||
env.NEXT_PUBLIC_LANGFUSE_CLOUD_REGION &&
|
||||
env.NEXT_PUBLIC_LANGFUSE_CLOUD_REGION !== "STAGING"
|
||||
env.NEXT_PUBLIC_LANGFUSE_CLOUD_REGION !== "STAGING" &&
|
||||
env.NEXT_PUBLIC_LANGFUSE_CLOUD_REGION !== "DEV"
|
||||
) {
|
||||
await fetch(env.LANGFUSE_NEW_USER_SIGNUP_WEBHOOK, {
|
||||
method: "POST",
|
||||
|
||||
+3
-3
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "worker",
|
||||
"version": "2.29.2",
|
||||
"version": "2.30.2",
|
||||
"description": "",
|
||||
"license": "MIT",
|
||||
"main": "index.js",
|
||||
@@ -50,7 +50,7 @@
|
||||
"@types/lodash": "^4.14.202",
|
||||
"@types/node": "^20.11.19",
|
||||
"@types/pg": "^8.11.5",
|
||||
"@typescript-eslint/parser": "^7.6.0",
|
||||
"@typescript-eslint/parser": "^7.7.0",
|
||||
"eslint": "^8.57.0",
|
||||
"eslint-config-prettier": "^9.1.0",
|
||||
"eslint-config-standard": "^17.1.0",
|
||||
@@ -62,6 +62,6 @@
|
||||
"tsc-watch": "^6.2.0",
|
||||
"typescript": "^5.4.5",
|
||||
"vite": "^5.2.9",
|
||||
"vitest": "^1.3.1"
|
||||
"vitest": "^1.5.0"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1 +1 @@
|
||||
export const VERSION = "v2.29.2";
|
||||
export const VERSION = "v2.30.2";
|
||||
|
||||
Reference in New Issue
Block a user