Compare commits

..
12 Commits
Author SHA1 Message Date
Marlies Mayerhofer 0e1498a094 chore: release v3.78.2 2025-07-03 13:37:47 +02:00
marliessophieandGitHub 38f020e482 fix(scores): update score validation logic to accept inclusive ranges for score config validation (#7650)
* fix(scores): update score validation logic to accept inclusive ranges for score config validation

* chore: typo
2025-07-03 09:22:33 +00:00
Hassieb PakzadandGitHub 07e1d4e960 fix(otel): trace update events on present metadata (#7648) 2025-07-03 08:36:40 +00:00
Hassieb PakzadandGitHub 602d00a5bb chore: upgrade langfuse-langchain (#7647) 2025-07-03 08:16:41 +00:00
7395bd8d83 fix: loading state of csv import button (#7640)
* Disable import button during processing and show loading state

Co-authored-by: marc <marc@langfuse.com>

* prettier

---------

Co-authored-by: Cursor Agent <cursoragent@cursor.com>
2025-07-03 00:12:27 +00:00
Marc KlingenandGitHub 63d164ad52 chore: improve cursor background agent configuration (#7641)
* fix

* push

* test
2025-07-03 01:41:14 +02:00
Thorsten SpiekerandGitHub f1b915c8b7 fix: remove invalid metrics and dimensions from pivot table widget on view change (#7636)
* fix: remove invalid metrics and dimensions from pivot table widget on view change

* linting
2025-07-02 19:01:26 +02:00
Steffen SchmitzandGitHub e3886b94c1 chore: set static backoff for query clickhouse retries (#7635) 2025-07-02 17:35:43 +02:00
Steffen SchmitzandGitHub 44376c2a9f chore: ignore all pending linting errors and enforce zero warnings (#7634) 2025-07-02 14:32:35 +00:00
marliessophieandGitHub 2e85c9ac15 chore(evals): add QUEUE_ERROR_MESSAGES constants for improved error handling (#7630)
* chore(evals): add QUEUE_ERROR_MESSAGES constants for improved error handling

* chore: push
2025-07-02 14:01:15 +00:00
Steffen SchmitzandGitHub 7cc81deb65 chore: retry socket hang ups for CH queries (#7628) 2025-07-02 13:52:26 +00:00
Steffen SchmitzandGitHub a13ddd41ae exp: write observation and score info to traces_mt (#7590)
* exp: write observation and score info to traces_mt

* chore: update declarations for experimental traces table

* chore: change anyLast pattern to working condition

* chore: revert anyLast setup

* chore: revert

* chore: cleanup queries
2025-07-02 11:53:38 +00:00
48 changed files with 857 additions and 345 deletions
+12 -5
View File
@@ -12,8 +12,15 @@ RUN apt-get update && \
postgresql-client \
redis-tools \
less nano \
sudo \
&& rm -rf /var/lib/apt/lists/*
# ---------- Docker -----------------------------------------------------------
# Install Docker for background agents that need container capabilities
RUN curl -fsSL https://get.docker.com -o get-docker.sh && \
sh get-docker.sh && \
rm get-docker.sh
# ---------- pnpm -------------------------------------------------------------
# Langfuse monorepo relies on pnpm 9.5.0 (see CONTRIBUTING.md)
ENV PNPM_HOME="/pnpm"
@@ -29,11 +36,11 @@ RUN wget -qO- "https://github.com/golang-migrate/migrate/releases/download/v${MI
chmod +x /usr/local/bin/migrate
# ---------- Non-root user -----------------------------------------------------
# Use root for convenience in development containers
WORKDIR /workspace
# Create non-root user
RUN useradd -ms /bin/bash ubuntu
# Create non-root user with sudo privileges and docker group access
RUN useradd -ms /bin/bash ubuntu && \
usermod -aG sudo ubuntu && \
usermod -aG docker ubuntu && \
echo "ubuntu ALL=(ALL) NOPASSWD:ALL" >> /etc/sudoers
# Pre-create pnpm store and set correct ownership to avoid first-run cost & permission issues
RUN pnpm store path > /dev/null && \
+7 -1
View File
@@ -4,5 +4,11 @@
"context": ".",
"dockerfile": "Dockerfile"
},
"start": "pnpm dx-f"
"start": "sudo service docker start",
"terminals": [
{
"name": "dev server",
"command": "pnpm run dx-f"
}
]
}
+2 -1
View File
@@ -5,4 +5,5 @@ alwaysApply: true
---
# General rules
- Linting in this repo only works if the development server is running
- Linting in this repo only works if the development server is running
- Always run the full mono-repo via `pnpm run dx` (use `pnpm dx-f` when you run this in a background agent). Thereby the database will also be seeded.
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "langfuse",
"version": "3.78.1",
"version": "3.78.2",
"author": "engineering@langfuse.com",
"license": "MIT",
"private": true,
+2 -2
View File
@@ -33,7 +33,7 @@
"scripts": {
"build": "tsc",
"dev": "tsc --watch",
"lint": "eslint . --ext .js,.jsx,.ts,.tsx --max-warnings 72",
"lint": "eslint . --ext .js,.jsx,.ts,.tsx --max-warnings 0",
"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",
@@ -84,7 +84,7 @@
"jsonpath-plus": "10.3.0",
"kysely": "^0.27.4",
"langchain": "^0.3.28",
"langfuse-langchain": "3.37.4",
"langfuse-langchain": "3.38.1",
"lodash": "^4.17.21",
"lossless-json": "^4.1.1",
"next-auth": "^4.24.11",
@@ -178,7 +178,7 @@ async function main() {
const seedApiKey = {
id: "seed-api-key",
secret: process.env.SEED_SECRET_KEY ?? "sk-lf-1234567890",
secret: process.env.SEED_SECRET_KEY ?? "sk-lf-1234567890", // eslint-disable-line turbo/no-undeclared-env-vars
public: "pk-lf-1234567890",
note: "seeded key",
};
@@ -241,7 +241,7 @@ async function main() {
const secondKey = {
id: "seed-api-key-2",
secret: process.env.SEED_SECRET_KEY ?? "sk-lf-asdfghjkl",
secret: process.env.SEED_SECRET_KEY ?? "sk-lf-asdfghjkl", // eslint-disable-line turbo/no-undeclared-env-vars
public: "pk-lf-asdfghjkl",
note: "seeded key 2",
};
@@ -274,7 +274,7 @@ async function main() {
await createTraceSessions(project1, project2);
// If openai key is in environment, add it to the projects LLM API keys
const OPENAI_API_KEY = process.env.OPENAI_API_KEY;
const OPENAI_API_KEY = process.env.OPENAI_API_KEY; // eslint-disable-line turbo/no-undeclared-env-vars
if (OPENAI_API_KEY) {
await prisma.llmApiKeys.create({
+1 -1
View File
@@ -88,7 +88,7 @@ declare const globalThis: {
kyselyPrismaGlobal: { $kysely: Kysely<DB> } | undefined;
} & typeof global;
if (process.env.NODE_ENV === "development") {
if (process.env.NODE_ENV === "development") { // eslint-disable-line turbo/no-undeclared-env-vars
globalThis.prismaGlobal ??= createPrismaInstance(); // regular instantiation
globalThis.kyselyPrismaGlobal ??= globalThis.prismaGlobal.$extends(
kyselyExtension({
@@ -3,11 +3,11 @@ import { orderBy } from "../interfaces/orderBy";
import z from "zod/v4";
export enum TableViewPresetTableName {
Traces = "traces",
Observations = "observations",
Scores = "scores",
Sessions = "sessions",
Datasets = "datasets",
Traces = "traces", // eslint-disable-line no-unused-vars
Observations = "observations", // eslint-disable-line no-unused-vars
Scores = "scores", // eslint-disable-line no-unused-vars
Sessions = "sessions", // eslint-disable-line no-unused-vars
Datasets = "datasets", // eslint-disable-line no-unused-vars
}
const TableViewPresetDomainSchema = z.object({
+2 -1
View File
@@ -105,10 +105,11 @@ const EnvSchema = z.object({
.number()
.default(80e6), // 80MB
LANGFUSE_CLICKHOUSE_DELETION_TIMEOUT_MS: z.coerce.number().default(240_000), // 4 minutes
LANGFUSE_CLICKHOUSE_QUERY_MAX_ATTEMPTS: z.coerce.number().default(3), // Maximum attempts for socket hang up errors
LANGFUSE_SKIP_S3_LIST_FOR_OBSERVATIONS_PROJECT_IDS: z.string().optional(),
});
export const env: z.infer<typeof EnvSchema> =
process.env.DOCKER_BUILD === "1"
process.env.DOCKER_BUILD === "1" // eslint-disable-line turbo/no-undeclared-env-vars
? (process.env as any)
: EnvSchema.parse(removeEmptyEnvVariables(process.env));
+1
View File
@@ -7,3 +7,4 @@ export { MethodNotAllowedError } from "./MethodNotAllowedError";
export { ApiError } from "./ApiError";
export { InternalServerError } from "./InternalServerError";
export { LangfuseConflictError } from "./ConflictError";
export { QUEUE_ERROR_MESSAGES } from "./utils/constants";
@@ -0,0 +1,10 @@
export const QUEUE_ERROR_MESSAGES = {
API_KEY_ERROR: "API key for provider",
NO_DEFAULT_MODEL_ERROR: "No default model or custom model found for project",
MAPPED_DATA_ERROR:
"Please ensure the mapped data exists and consider extending the job delay.",
INVALID_JSON_ERROR: "is not valid JSON",
TOO_LOW_MAX_TOKENS_ERROR: "Error: Unterminated string in JSON at position",
OUTPUT_TOKENS_TOO_LONG_ERROR:
"Could not parse response content as the length limit was reached",
};
@@ -8,16 +8,16 @@ import { BatchTableNames } from "../../interfaces/tableNames";
import { TracingSearchType } from "../../interfaces/search";
export enum BatchExportStatus {
QUEUED = "QUEUED",
PROCESSING = "PROCESSING",
COMPLETED = "COMPLETED",
FAILED = "FAILED",
QUEUED = "QUEUED", // eslint-disable-line no-unused-vars
PROCESSING = "PROCESSING", // eslint-disable-line no-unused-vars
COMPLETED = "COMPLETED", // eslint-disable-line no-unused-vars
FAILED = "FAILED", // eslint-disable-line no-unused-vars
}
export enum BatchExportFileFormat {
JSON = "JSON",
CSV = "CSV",
JSONL = "JSONL",
JSON = "JSON", // eslint-disable-line no-unused-vars
CSV = "CSV", // eslint-disable-line no-unused-vars
JSONL = "JSONL", // eslint-disable-line no-unused-vars
}
// Use shared BatchTableNames enum for consistency across batch operations
@@ -42,7 +42,7 @@ function parseJsonDefault(selectedColumn: unknown, jsonSelector: string) {
export function extractValueFromObject(
obj: Record<string, unknown>,
mapping: z.infer<typeof variableMapping>,
parseJson?: (selectedColumn: unknown, jsonSelector: string) => unknown,
parseJson?: (selectedColumn: unknown, jsonSelector: string) => unknown, // eslint-disable-line no-unused-vars
): { value: string; error: Error | null } {
const selectedColumn = obj[mapping.selectedColumnId];
const jsonParser = parseJson || parseJsonDefault;
@@ -37,13 +37,13 @@ const ScorePropsAgainstConfigNumeric = z
dataType: z.literal("NUMERIC"),
})
.superRefine((data, ctx) => {
if (isPresent(data.maxValue) && data.value >= data.maxValue) {
if (isPresent(data.maxValue) && data.value > data.maxValue) {
ctx.addIssue({
code: z.ZodIssueCode.custom,
message: `Value exceeds maximum value of ${data.maxValue} defined in config`,
});
}
if (isPresent(data.minValue) && data.value <= data.minValue) {
if (isPresent(data.minValue) && data.value < data.minValue) {
ctx.addIssue({
code: z.ZodIssueCode.custom,
message: `Value is below minimum value of ${data.minValue} defined in config`,
@@ -150,7 +150,7 @@ const ValidatedScoreConfigSchema = z
*/
export const filterAndValidateDbScoreConfigList = (
scoreConfigs: ScoreConfigDbType[],
onParseError?: (error: z.ZodError) => void,
onParseError?: (error: z.ZodError) => void, // eslint-disable-line no-unused-vars
): ValidatedScoreConfig[] =>
scoreConfigs.reduce((acc, ts) => {
const result = ValidatedScoreConfigSchema.safeParse(ts);
+7 -7
View File
@@ -4,11 +4,11 @@
* to avoid coupling between different batch operation types.
*/
export enum BatchTableNames {
Scores = "scores",
Sessions = "sessions",
Traces = "traces",
Observations = "observations",
DatasetRunItems = "dataset_run_items",
DatasetItems = "dataset_items",
AuditLogs = "audit_logs",
Scores = "scores", // eslint-disable-line no-unused-vars
Sessions = "sessions", // eslint-disable-line no-unused-vars
Traces = "traces", // eslint-disable-line no-unused-vars
Observations = "observations", // eslint-disable-line no-unused-vars
DatasetRunItems = "dataset_run_items", // eslint-disable-line no-unused-vars
DatasetItems = "dataset_items", // eslint-disable-line no-unused-vars
AuditLogs = "audit_logs", // eslint-disable-line no-unused-vars
}
@@ -45,7 +45,7 @@ const getS3StorageServiceClient = (bucketName: string): StorageService => {
return s3StorageServiceClient;
};
export type TokenCountDelegate = (p: {
export type TokenCountDelegate = (p: { // eslint-disable-line no-unused-vars
model: Model;
text: unknown;
}) => number | undefined;
@@ -22,7 +22,7 @@ export type SpanCtx = {
traceContext?: TCarrier;
};
type AsyncCallbackFn<T> = (span: opentelemetry.Span) => Promise<T>;
type AsyncCallbackFn<T> = (span: opentelemetry.Span) => Promise<T>; // eslint-disable-line no-unused-vars
export async function instrumentAsync<T>(
ctx: SpanCtx,
@@ -64,7 +64,7 @@ export async function instrumentAsync<T>(
);
}
type SyncCallbackFn<T> = (span: opentelemetry.Span) => T;
type SyncCallbackFn<T> = (span: opentelemetry.Span) => T; // eslint-disable-line no-unused-vars
export function instrumentSync<T>(
ctx: SpanCtx,
@@ -63,7 +63,7 @@ type FetchLLMCompletionParams = LLMCompletionParams & {
};
export async function fetchLLMCompletion(
params: LLMCompletionParams & {
params: LLMCompletionParams & { // eslint-disable-line no-unused-vars
streaming: true;
},
): Promise<{
@@ -72,7 +72,7 @@ export async function fetchLLMCompletion(
}>;
export async function fetchLLMCompletion(
params: LLMCompletionParams & {
params: LLMCompletionParams & { // eslint-disable-line no-unused-vars
streaming: false;
},
): Promise<{
@@ -81,7 +81,7 @@ export async function fetchLLMCompletion(
}>;
export async function fetchLLMCompletion(
params: LLMCompletionParams & {
params: LLMCompletionParams & { // eslint-disable-line no-unused-vars
streaming: false;
structuredOutputSchema: ZodSchema;
},
@@ -91,7 +91,7 @@ export async function fetchLLMCompletion(
}>;
export async function fetchLLMCompletion(
params: LLMCompletionParams & {
params: LLMCompletionParams & { // eslint-disable-line no-unused-vars
tools: LLMToolDefinition[];
streaming: false;
},
@@ -432,19 +432,19 @@ export class FilterList {
this.filters.push(...filter);
}
find(predicate: (filter: Filter) => boolean) {
find(predicate: (filter: Filter) => boolean) { // eslint-disable-line no-unused-vars
return this.filters.find(predicate);
}
filter(predicate: (filter: Filter) => boolean) {
filter(predicate: (filter: Filter) => boolean) { // eslint-disable-line no-unused-vars
return new FilterList(this.filters.filter(predicate));
}
some(predicate: (filter: Filter) => boolean) {
some(predicate: (filter: Filter) => boolean) { // eslint-disable-line no-unused-vars
return this.filters.some(predicate);
}
forEach(callback: (filter: Filter) => void) {
forEach(callback: (filter: Filter) => void) { // eslint-disable-line no-unused-vars
this.filters.forEach(callback);
}
+3 -2
View File
@@ -70,8 +70,9 @@ export function getQueue(
return ScoreDeleteQueue.getInstance();
case QueueName.DeadLetterRetryQueue:
return DeadLetterRetryQueue.getInstance();
default:
const exhaustiveCheckDefault: never = queueName;
default: {
const exhaustiveCheckDefault: never = queueName; // eslint-disable-line no-case-declarations, no-unused-vars
throw new Error(`Queue ${queueName} not found`);
}
}
}
+3 -3
View File
@@ -167,9 +167,9 @@ const createRedisClient = () => {
declare global {
// eslint-disable-next-line no-var
var redis: undefined | ReturnType<typeof createRedisClient>;
var redis: undefined | ReturnType<typeof createRedisClient>; // eslint-disable-line no-unused-vars
}
export const redis = globalThis.redis ?? createRedisClient();
export const redis = globalThis.redis ?? createRedisClient(); // eslint-disable-line no-undef
if (env.NODE_ENV !== "production") globalThis.redis = redis;
if (env.NODE_ENV !== "production") globalThis.redis = redis; // eslint-disable-line no-undef
@@ -9,6 +9,7 @@ import { randomUUID } from "crypto";
import { getClickhouseEntityType } from "../clickhouse/schemaUtils";
import { NodeClickHouseClientConfigOptions } from "@clickhouse/client/dist/config";
import { context, SpanKind, trace } from "@opentelemetry/api";
import { backOff } from "exponential-backoff";
import {
StorageService,
StorageServiceFactory,
@@ -203,6 +204,18 @@ export async function* queryClickhouseStream<T>(opts: {
}
}
/**
* Determines if an error is retryable (socket hang up, connection reset, etc.)
*/
function isRetryableError(error: unknown): boolean {
if (!error || typeof error !== "object") return false;
const errorMessage = (error as Error).message?.toLowerCase() || "";
// Check for socket hang up and other network-related errors
return errorMessage.includes("socket hang up");
}
export async function queryClickhouse<T>(opts: {
query: string;
params?: Record<string, unknown> | undefined;
@@ -218,40 +231,78 @@ export async function queryClickhouse<T>(opts: {
span.setAttribute("db.query.text", opts.query);
span.setAttribute("db.operation.name", "SELECT");
const res = await clickhouseClient(opts.clickhouseConfigs).query({
query: opts.query,
format: "JSONEachRow",
query_params: opts.params,
clickhouse_settings: {
log_comment: JSON.stringify(opts.tags ?? {}),
},
});
// same logic as for prisma. we want to see queries in development
if (env.NODE_ENV === "development") {
logger.info(`clickhouse:query ${res.query_id} ${opts.query}`);
}
// Retry logic for socket hang up and other network errors
return await backOff(
async () => {
const res = await clickhouseClient(opts.clickhouseConfigs).query({
query: opts.query,
format: "JSONEachRow",
query_params: opts.params,
clickhouse_settings: {
log_comment: JSON.stringify(opts.tags ?? {}),
},
});
span.setAttribute("ch.queryId", res.query_id);
// add summary headers to the span. Helps to tune performance
const summaryHeader = res.response_headers["x-clickhouse-summary"];
if (summaryHeader) {
try {
const summary = Array.isArray(summaryHeader)
? JSON.parse(summaryHeader[0])
: JSON.parse(summaryHeader);
for (const key in summary) {
span.setAttribute(`ch.${key}`, summary[key]);
// same logic as for prisma. we want to see queries in development
if (env.NODE_ENV === "development") {
logger.info(`clickhouse:query ${res.query_id} ${opts.query}`);
}
} catch (error) {
logger.debug(
`Failed to parse clickhouse summary header ${summaryHeader}`,
error,
);
}
}
return await res.json<T>();
span.setAttribute("ch.queryId", res.query_id);
// add summary headers to the span. Helps to tune performance
const summaryHeader = res.response_headers["x-clickhouse-summary"];
if (summaryHeader) {
try {
const summary = Array.isArray(summaryHeader)
? JSON.parse(summaryHeader[0])
: JSON.parse(summaryHeader);
for (const key in summary) {
span.setAttribute(`ch.${key}`, summary[key]);
}
} catch (error) {
logger.debug(
`Failed to parse clickhouse summary header ${summaryHeader}`,
error,
);
}
}
return await res.json<T>();
},
{
numOfAttempts: env.LANGFUSE_CLICKHOUSE_QUERY_MAX_ATTEMPTS,
retry: (error: Error, attemptNumber: number) => {
const shouldRetry = isRetryableError(error);
if (shouldRetry) {
logger.warn(
`ClickHouse query failed with retryable error (attempt ${attemptNumber}/${env.LANGFUSE_CLICKHOUSE_QUERY_MAX_ATTEMPTS}): ${error.message}`,
{
error: error.message,
attemptNumber,
tags: opts.tags,
},
);
span.addEvent("clickhouse-query-retry", {
"retry.attempt": attemptNumber,
"retry.error": error.message,
});
} else {
logger.error(
`ClickHouse query failed with non-retryable error: ${error.message}`,
{
error: error.message,
tags: opts.tags,
},
);
}
return shouldRetry;
},
startingDelay: 100,
timeMultiple: 1,
maxDelay: 100,
},
);
},
);
}
@@ -1,6 +1,6 @@
import z from "zod/v4";
import { prisma } from "../../../db";
import { LangfuseNotFoundError } from "../../../errors";
import { LangfuseNotFoundError, QUEUE_ERROR_MESSAGES } from "../../../errors";
import { LLMApiKeySchema, ZodModelConfig } from "../../llm/types";
type ValidConfig = {
@@ -156,7 +156,7 @@ export class DefaultEvalModelService {
if (!selectedModel) {
return {
valid: false,
error: `No default model or custom model found for project ${projectId}.`,
error: `${QUEUE_ERROR_MESSAGES.NO_DEFAULT_MODEL_ERROR} ${projectId}.`,
};
}
@@ -173,7 +173,7 @@ export class DefaultEvalModelService {
if (!parsedKey.success) {
return {
valid: false,
error: `API key for provider "${selectedModel.provider}" not found in project ${projectId}.`,
error: `${QUEUE_ERROR_MESSAGES.API_KEY_ERROR} "${selectedModel.provider}" not found in project ${projectId}.`,
};
}
@@ -13,7 +13,7 @@ export class InMemoryFilterService {
static evaluateFilter<T>(
data: T,
filter: FilterState,
fieldMapper: (data: T, column: string) => unknown,
fieldMapper: (data: T, column: string) => unknown, // eslint-disable-line no-unused-vars
): boolean {
try {
// If no filters, data matches
@@ -45,7 +45,7 @@ export class InMemoryFilterService {
private static evaluateFilterCondition<T>(
data: T,
condition: FilterCondition,
fieldMapper: (data: T, column: string) => unknown,
fieldMapper: (data: T, column: string) => unknown, // eslint-disable-line no-unused-vars
): boolean {
const { column, type, operator } = condition;
@@ -13,8 +13,8 @@ export type PromptParams = {
);
export enum PromptServiceMetrics {
PromptCacheHit = "prompt_cache_hit",
PromptCacheMiss = "prompt_cache_miss",
PromptCacheHit = "prompt_cache_hit", // eslint-disable-line no-unused-vars
PromptCacheMiss = "prompt_cache_miss", // eslint-disable-line no-unused-vars
}
export type PartialPrompt = Pick<
@@ -28,21 +28,21 @@ type UploadFile = {
};
export interface StorageService {
uploadFile(params: UploadFile): Promise<{ signedUrl: string }>;
uploadFile(params: UploadFile): Promise<{ signedUrl: string }>; // eslint-disable-line no-unused-vars
uploadJson(path: string, body: Record<string, unknown>[]): Promise<void>;
uploadJson(path: string, body: Record<string, unknown>[]): Promise<void>; // eslint-disable-line no-unused-vars
download(path: string): Promise<string>;
download(path: string): Promise<string>; // eslint-disable-line no-unused-vars
listFiles(prefix: string): Promise<{ file: string; createdAt: Date }[]>;
listFiles(prefix: string): Promise<{ file: string; createdAt: Date }[]>; // eslint-disable-line no-unused-vars
getSignedUrl(
fileName: string,
ttlSeconds: number,
asAttachment?: boolean,
fileName: string, // eslint-disable-line no-unused-vars
ttlSeconds: number, // eslint-disable-line no-unused-vars
asAttachment?: boolean, // eslint-disable-line no-unused-vars
): Promise<string>;
getSignedUploadUrl(params: {
getSignedUploadUrl(params: { // eslint-disable-line no-unused-vars
path: string;
ttlSeconds: number;
sha256Hash: string;
@@ -50,7 +50,7 @@ export interface StorageService {
contentLength: number;
}): Promise<string>;
deleteFiles(paths: string[]): Promise<void>;
deleteFiles(paths: string[]): Promise<void>; // eslint-disable-line no-unused-vars
}
export class StorageServiceFactory {
@@ -213,7 +213,7 @@ class AzureBlobStorageService implements StorageService {
}
private async streamToString(
readableStream: NodeJS.ReadableStream,
readableStream: NodeJS.ReadableStream, // eslint-disable-line no-undef
): Promise<string> {
return new Promise((resolve, reject) => {
const chunks: string[] = [];
@@ -24,12 +24,12 @@ export class DatabaseReadStream<EntityType> extends Readable {
constructor(
// the delegate function takes care of querying the database in a paginated manner
private queryDelegate: (
pageSize: number,
offset: number
private queryDelegate: ( // eslint-disable-line no-unused-vars
pageSize: number, // eslint-disable-line no-unused-vars
offset: number // eslint-disable-line no-unused-vars
) => Promise<Array<EntityType>>,
private pageSize: number,
private maxRecords?: number
private pageSize: number, // eslint-disable-line no-unused-vars
private maxRecords?: number // eslint-disable-line no-unused-vars
) {
super({ objectMode: true }); // Set object mode to true to allow pushing objects to the stream rather than strings or buffers
@@ -11,7 +11,7 @@ export function transformStreamToCsv(): Transform {
objectMode: true,
transform(
row: Record<string, any>,
encoding: BufferEncoding,
encoding: BufferEncoding, // eslint-disable-line no-undef
callback: TransformCallback,
): void {
if (isFirstChunk) {
@@ -9,8 +9,8 @@ export function transformStreamToJson(): Transform {
transform(
row: any,
encoding: BufferEncoding,
callback: TransformCallback
encoding: BufferEncoding, // eslint-disable-line no-undef, no-unused-vars
callback: TransformCallback,
): void {
if (isFirstElement) {
this.push("["); // Push the opening bracket for the first element
@@ -7,7 +7,7 @@ export function transformStreamToJsonl(): Transform {
transform(
row: Record<string, any>,
encoding: BufferEncoding,
encoding: BufferEncoding, // eslint-disable-line no-undef, no-unused-vars
callback: TransformCallback,
): void {
this.push(stringify(row) + "\n");
+214 -132
View File
@@ -112,7 +112,7 @@ importers:
version: 7.12.0(eslint@8.57.0)(typescript@5.4.5)
'@vercel/style-guide':
specifier: ^6.0.0
version: 6.0.0(@next/eslint-plugin-next@14.2.15)(eslint@8.57.0)(jest@29.7.0(@types/node@20.14.8))(prettier@3.5.3)(typescript@5.4.5)(vitest@2.1.2(@types/node@20.14.8))
version: 6.0.0(@next/eslint-plugin-next@14.2.15)(eslint@8.57.0)(jest@29.7.0(@types/node@20.14.8)(babel-plugin-macros@3.1.0))(prettier@3.5.3)(typescript@5.4.5)(vitest@2.1.2(@types/node@20.14.8)(jsdom@20.0.3)(msw@2.6.5(@types/node@20.14.8)(typescript@5.4.5))(terser@5.43.1))
eslint-config-next:
specifier: ^14.2.15
version: 14.2.15(eslint@8.57.0)(typescript@5.4.5)
@@ -224,8 +224,8 @@ importers:
specifier: ^0.3.28
version: 0.3.28(@langchain/anthropic@0.3.22(@langchain/core@0.3.58(openai@4.104.0(zod@3.25.62))))(@langchain/aws@0.1.11(@langchain/core@0.3.58(openai@4.104.0(zod@3.25.62))))(@langchain/core@0.3.58(openai@4.104.0(zod@3.25.62)))(@langchain/google-genai@0.2.12(@langchain/core@0.3.58(openai@4.104.0(zod@3.25.62))))(@langchain/google-vertexai@0.2.12(@langchain/core@0.3.58(openai@4.104.0(zod@3.25.62)))(zod@3.25.62))(axios@1.8.2)(cheerio@1.0.0)(handlebars@4.7.8)(openai@4.104.0(zod@3.25.62))
langfuse-langchain:
specifier: 3.37.4
version: 3.37.4(langchain@0.3.28(@langchain/anthropic@0.3.22(@langchain/core@0.3.58(openai@4.104.0(zod@3.25.62))))(@langchain/aws@0.1.11(@langchain/core@0.3.58(openai@4.104.0(zod@3.25.62))))(@langchain/core@0.3.58(openai@4.104.0(zod@3.25.62)))(@langchain/google-genai@0.2.12(@langchain/core@0.3.58(openai@4.104.0(zod@3.25.62))))(@langchain/google-vertexai@0.2.12(@langchain/core@0.3.58(openai@4.104.0(zod@3.25.62)))(zod@3.25.62))(axios@1.8.2)(cheerio@1.0.0)(handlebars@4.7.8)(openai@4.104.0(zod@3.25.62)))
specifier: 3.38.1
version: 3.38.1(langchain@0.3.28(@langchain/anthropic@0.3.22(@langchain/core@0.3.58(openai@4.104.0(zod@3.25.62))))(@langchain/aws@0.1.11(@langchain/core@0.3.58(openai@4.104.0(zod@3.25.62))))(@langchain/core@0.3.58(openai@4.104.0(zod@3.25.62)))(@langchain/google-genai@0.2.12(@langchain/core@0.3.58(openai@4.104.0(zod@3.25.62))))(@langchain/google-vertexai@0.2.12(@langchain/core@0.3.58(openai@4.104.0(zod@3.25.62)))(zod@3.25.62))(axios@1.8.2)(cheerio@1.0.0)(handlebars@4.7.8)(openai@4.104.0(zod@3.25.62)))
lodash:
specifier: ^4.17.21
version: 4.17.21
@@ -328,7 +328,7 @@ importers:
version: 5.4.5
vitest:
specifier: ^2.1.2
version: 2.1.2(@types/node@20.11.29)(jsdom@20.0.3)(msw@2.6.5(@types/node@20.11.29)(typescript@5.4.5))(terser@5.43.0)
version: 2.1.2(@types/node@20.11.29)(jsdom@20.0.3)(msw@2.6.5(@types/node@20.11.29)(typescript@5.4.5))(terser@5.43.1)
web:
dependencies:
@@ -758,7 +758,7 @@ importers:
version: 0.5.7(tailwindcss@3.4.17(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@2.1.2(@types/node@20.10.5))
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@2.1.2(@types/node@20.10.5)(jsdom@20.0.3)(msw@2.6.5(@types/node@20.10.5)(typescript@5.4.5))(terser@5.43.1))
'@testing-library/react':
specifier: ^15.0.7
version: 15.0.7(@types/react@18.2.79)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)
@@ -1038,7 +1038,7 @@ importers:
version: 5.4.5
vitest:
specifier: ^2.1.2
version: 2.1.2(@types/node@20.11.29)(jsdom@20.0.3)(msw@2.6.5(@types/node@20.11.29)(typescript@5.4.5))(terser@5.43.0)
version: 2.1.2(@types/node@20.11.29)(jsdom@20.0.3)(msw@2.6.5(@types/node@20.11.29)(typescript@5.4.5))(terser@5.43.1)
wait-for-expect:
specifier: ^3.0.2
version: 3.0.2
@@ -8936,18 +8936,18 @@ packages:
typeorm:
optional: true
langfuse-core@3.37.4:
resolution: {integrity: sha512-yqXkQ1/8V9TxF8q6oULWPQ4cYZlUs1egN6JfGuhJSWb5x2Ymgt4Qh9of/Kz//CTM7IZgzZqvrlnx0VXhNKDITg==}
langfuse-core@3.38.1:
resolution: {integrity: sha512-qZsHZZ05eHv+EQoPmfrm6bWUflzvlCSAfT8EP1PP1WeHSwEcwT4p7qAs9H0EnWcUtm0dAdyd6U75HGWu3/ETUA==}
engines: {node: '>=18'}
langfuse-langchain@3.37.4:
resolution: {integrity: sha512-yh2+Rg+NqN6k+E29VW9lnLp9qpldBE3Ldhh/qsvInyk0hdhF64I/z0hWPFY4tYNvbzBXDHgApJ+66nSnXBf4Tg==}
langfuse-langchain@3.38.1:
resolution: {integrity: sha512-lkgEwUuGEtkmtxcMhfFn90FRAlGOSGVWczyBFMXck+1YcqnYLIuE/MNg3ufN54yPRVJFfEXj0kesZSLERGQ8eQ==}
engines: {node: '>=18'}
peerDependencies:
langchain: '>=0.0.157 <0.4.0'
langfuse@3.37.4:
resolution: {integrity: sha512-MxnJXkS3xqlydMKWouanghkWZb8OqoDns+NIAZoD+y/WQh1mtx40n8c0dOaT7OahzSfB9ZbWj+j+yqJww/PJYQ==}
langfuse@3.38.1:
resolution: {integrity: sha512-rv0x+NWi4JDN8KUyGcl8JQ7xmM/0UBXEcYmtBR+msLLImsM3I07CPE6imp3N2I4AbByPNKF4IU8Kq/LFS3D34A==}
engines: {node: '>=18'}
langium@3.0.0:
@@ -11310,11 +11310,6 @@ packages:
uglify-js:
optional: true
terser@5.43.0:
resolution: {integrity: sha512-CqNNxKSGKSZCunSvwKLTs8u8sGGlp27sxNZ4quGh0QeNuyHM0JSEM/clM9Mf4zUp6J+tO2gUXhgXT2YMMkwfKQ==}
engines: {node: '>=10'}
hasBin: true
terser@5.43.1:
resolution: {integrity: sha512-+6erLbBm0+LROX2sPXlUYx/ux5PyE9K/a92Wrt6oA+WDAoFTdpHE5tCYCI5PNzq2y8df4rA+QgHLJuR4jNymsg==}
engines: {node: '>=10'}
@@ -14359,12 +14354,26 @@ snapshots:
optionalDependencies:
'@types/node': 20.14.8
'@inquirer/confirm@5.0.2(@types/node@20.10.5)':
dependencies:
'@inquirer/core': 10.1.0(@types/node@20.10.5)
'@inquirer/type': 3.0.1(@types/node@20.10.5)
'@types/node': 20.10.5
optional: true
'@inquirer/confirm@5.0.2(@types/node@20.11.29)':
dependencies:
'@inquirer/core': 10.1.0(@types/node@20.11.29)
'@inquirer/type': 3.0.1(@types/node@20.11.29)
'@types/node': 20.11.29
'@inquirer/confirm@5.0.2(@types/node@20.14.8)':
dependencies:
'@inquirer/core': 10.1.0(@types/node@20.14.8)
'@inquirer/type': 3.0.1(@types/node@20.14.8)
'@types/node': 20.14.8
optional: true
'@inquirer/confirm@5.1.12(@types/node@20.14.8)':
dependencies:
'@inquirer/core': 10.1.13(@types/node@20.14.8)
@@ -14372,6 +14381,21 @@ snapshots:
optionalDependencies:
'@types/node': 20.14.8
'@inquirer/core@10.1.0(@types/node@20.10.5)':
dependencies:
'@inquirer/figures': 1.0.8
'@inquirer/type': 3.0.4(@types/node@20.10.5)
ansi-escapes: 4.3.2
cli-width: 4.1.0
mute-stream: 2.0.0
signal-exit: 4.1.0
strip-ansi: 6.0.1
wrap-ansi: 6.2.0
yoctocolors-cjs: 2.1.2
transitivePeerDependencies:
- '@types/node'
optional: true
'@inquirer/core@10.1.0(@types/node@20.11.29)':
dependencies:
'@inquirer/figures': 1.0.8
@@ -14386,6 +14410,21 @@ snapshots:
transitivePeerDependencies:
- '@types/node'
'@inquirer/core@10.1.0(@types/node@20.14.8)':
dependencies:
'@inquirer/figures': 1.0.8
'@inquirer/type': 3.0.4(@types/node@20.14.8)
ansi-escapes: 4.3.2
cli-width: 4.1.0
mute-stream: 2.0.0
signal-exit: 4.1.0
strip-ansi: 6.0.1
wrap-ansi: 6.2.0
yoctocolors-cjs: 2.1.2
transitivePeerDependencies:
- '@types/node'
optional: true
'@inquirer/core@10.1.13(@types/node@20.14.8)':
dependencies:
'@inquirer/figures': 1.0.12
@@ -14483,14 +14522,34 @@ snapshots:
optionalDependencies:
'@types/node': 20.14.8
'@inquirer/type@3.0.1(@types/node@20.10.5)':
dependencies:
'@types/node': 20.10.5
optional: true
'@inquirer/type@3.0.1(@types/node@20.11.29)':
dependencies:
'@types/node': 20.11.29
'@inquirer/type@3.0.1(@types/node@20.14.8)':
dependencies:
'@types/node': 20.14.8
optional: true
'@inquirer/type@3.0.4(@types/node@20.10.5)':
optionalDependencies:
'@types/node': 20.10.5
optional: true
'@inquirer/type@3.0.4(@types/node@20.11.29)':
optionalDependencies:
'@types/node': 20.11.29
'@inquirer/type@3.0.4(@types/node@20.14.8)':
optionalDependencies:
'@types/node': 20.14.8
optional: true
'@inquirer/type@3.0.7(@types/node@20.14.8)':
optionalDependencies:
'@types/node': 20.14.8
@@ -17685,7 +17744,7 @@ snapshots:
lz-string: 1.5.0
pretty-format: 27.5.1
'@testing-library/jest-dom@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@2.1.2(@types/node@20.10.5))':
'@testing-library/jest-dom@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@2.1.2(@types/node@20.10.5)(jsdom@20.0.3)(msw@2.6.5(@types/node@20.10.5)(typescript@5.4.5))(terser@5.43.1))':
dependencies:
'@adobe/css-tools': 4.4.0
'@babel/runtime': 7.24.7
@@ -17699,7 +17758,7 @@ snapshots:
'@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: 2.1.2(@types/node@20.10.5)
vitest: 2.1.2(@types/node@20.10.5)(jsdom@20.0.3)(msw@2.6.5(@types/node@20.10.5)(typescript@5.4.5))(terser@5.43.1)
'@testing-library/react@15.0.7(@types/react@18.2.79)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)':
dependencies:
@@ -18358,7 +18417,7 @@ snapshots:
'@ungap/structured-clone@1.2.0': {}
'@vercel/style-guide@6.0.0(@next/eslint-plugin-next@14.2.15)(eslint@8.57.0)(jest@29.7.0(@types/node@20.14.8))(prettier@3.5.3)(typescript@5.4.5)(vitest@2.1.2(@types/node@20.14.8))':
'@vercel/style-guide@6.0.0(@next/eslint-plugin-next@14.2.15)(eslint@8.57.0)(jest@29.7.0(@types/node@20.14.8)(babel-plugin-macros@3.1.0))(prettier@3.5.3)(typescript@5.4.5)(vitest@2.1.2(@types/node@20.14.8)(jsdom@20.0.3)(msw@2.6.5(@types/node@20.14.8)(typescript@5.4.5))(terser@5.43.1))':
dependencies:
'@babel/core': 7.24.3
'@babel/eslint-parser': 7.24.1(@babel/core@7.24.3)(eslint@8.57.0)
@@ -18366,19 +18425,19 @@ snapshots:
'@typescript-eslint/eslint-plugin': 7.3.1(@typescript-eslint/parser@7.12.0(eslint@8.57.0)(typescript@5.4.5))(eslint@8.57.0)(typescript@5.4.5)
'@typescript-eslint/parser': 7.12.0(eslint@8.57.0)(typescript@5.4.5)
eslint-config-prettier: 9.1.0(eslint@8.57.0)
eslint-import-resolver-alias: 1.1.2(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))
eslint-import-resolver-alias: 1.1.2(eslint-plugin-import@2.29.1)
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-plugin-eslint-comments: 3.2.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)
eslint-plugin-jest: 27.9.0(@typescript-eslint/eslint-plugin@7.3.1(@typescript-eslint/parser@7.12.0(eslint@8.57.0)(typescript@5.4.5))(eslint@8.57.0)(typescript@5.4.5))(eslint@8.57.0)(jest@29.7.0(@types/node@20.14.8))(typescript@5.4.5)
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(@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-jest: 27.9.0(@typescript-eslint/eslint-plugin@7.3.1(@typescript-eslint/parser@7.12.0(eslint@8.57.0)(typescript@5.4.5))(eslint@8.57.0)(typescript@5.4.5))(eslint@8.57.0)(jest@29.7.0(@types/node@20.14.8)(babel-plugin-macros@3.1.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(@typescript-eslint/eslint-plugin@7.3.1(@typescript-eslint/parser@7.12.0(eslint@8.57.0)(typescript@5.4.5))(eslint@8.57.0)(typescript@5.4.5))(eslint@8.57.0)(jest@29.7.0(@types/node@20.14.8))(typescript@5.4.5))(eslint@8.57.0)
eslint-plugin-playwright: 1.5.4(eslint-plugin-jest@27.9.0(@typescript-eslint/eslint-plugin@7.3.1(@typescript-eslint/parser@7.12.0(eslint@8.57.0)(typescript@5.4.5))(eslint@8.57.0)(typescript@5.4.5))(eslint@8.57.0)(jest@29.7.0(@types/node@20.14.8)(babel-plugin-macros@3.1.0))(typescript@5.4.5))(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)
eslint-plugin-testing-library: 6.2.0(eslint@8.57.0)(typescript@5.4.5)
eslint-plugin-tsdoc: 0.2.17
eslint-plugin-unicorn: 51.0.1(eslint@8.57.0)
eslint-plugin-vitest: 0.3.26(@typescript-eslint/eslint-plugin@7.3.1(@typescript-eslint/parser@7.12.0(eslint@8.57.0)(typescript@5.4.5))(eslint@8.57.0)(typescript@5.4.5))(eslint@8.57.0)(typescript@5.4.5)(vitest@2.1.2(@types/node@20.14.8))
eslint-plugin-vitest: 0.3.26(@typescript-eslint/eslint-plugin@7.3.1(@typescript-eslint/parser@7.12.0(eslint@8.57.0)(typescript@5.4.5))(eslint@8.57.0)(typescript@5.4.5))(eslint@8.57.0)(typescript@5.4.5)(vitest@2.1.2(@types/node@20.14.8)(jsdom@20.0.3)(msw@2.6.5(@types/node@20.14.8)(typescript@5.4.5))(terser@5.43.1))
prettier-plugin-packagejson: 2.4.12(prettier@3.5.3)
optionalDependencies:
'@next/eslint-plugin-next': 14.2.15
@@ -18399,31 +18458,33 @@ snapshots:
chai: 5.1.1
tinyrainbow: 1.2.0
'@vitest/mocker@2.1.2(@vitest/spy@2.1.2)(msw@2.6.5(@types/node@20.11.29)(typescript@5.4.5))(vite@5.2.14(@types/node@20.11.29)(terser@5.43.0))':
'@vitest/mocker@2.1.2(@vitest/spy@2.1.2)(msw@2.6.5(@types/node@20.10.5)(typescript@5.4.5))(vite@5.2.14(@types/node@20.10.5)(terser@5.43.1))':
dependencies:
'@vitest/spy': 2.1.2
estree-walker: 3.0.3
magic-string: 0.30.11
optionalDependencies:
msw: 2.6.5(@types/node@20.10.5)(typescript@5.4.5)
vite: 5.2.14(@types/node@20.10.5)(terser@5.43.1)
optional: true
'@vitest/mocker@2.1.2(@vitest/spy@2.1.2)(msw@2.6.5(@types/node@20.11.29)(typescript@5.4.5))(vite@5.2.14(@types/node@20.11.29)(terser@5.43.1))':
dependencies:
'@vitest/spy': 2.1.2
estree-walker: 3.0.3
magic-string: 0.30.11
optionalDependencies:
msw: 2.6.5(@types/node@20.11.29)(typescript@5.4.5)
vite: 5.2.14(@types/node@20.11.29)(terser@5.43.0)
vite: 5.2.14(@types/node@20.11.29)(terser@5.43.1)
'@vitest/mocker@2.1.2(@vitest/spy@2.1.2)(vite@5.2.14(@types/node@20.10.5))':
'@vitest/mocker@2.1.2(@vitest/spy@2.1.2)(msw@2.6.5(@types/node@20.14.8)(typescript@5.4.5))(vite@5.2.14(@types/node@20.14.8)(terser@5.43.1))':
dependencies:
'@vitest/spy': 2.1.2
estree-walker: 3.0.3
magic-string: 0.30.11
optionalDependencies:
vite: 5.2.14(@types/node@20.10.5)
optional: true
'@vitest/mocker@2.1.2(@vitest/spy@2.1.2)(vite@5.2.14(@types/node@20.14.8))':
dependencies:
'@vitest/spy': 2.1.2
estree-walker: 3.0.3
magic-string: 0.30.11
optionalDependencies:
vite: 5.2.14(@types/node@20.14.8)
msw: 2.6.5(@types/node@20.14.8)(typescript@5.4.5)
vite: 5.2.14(@types/node@20.14.8)(terser@5.43.1)
optional: true
'@vitest/pretty-format@2.1.2':
@@ -19505,7 +19566,7 @@ snapshots:
- supports-color
- ts-node
create-jest@29.7.0(@types/node@20.14.8):
create-jest@29.7.0(@types/node@20.14.8)(babel-plugin-macros@3.1.0):
dependencies:
'@jest/types': 29.6.3
chalk: 4.1.2
@@ -20319,7 +20380,7 @@ snapshots:
eslint: 8.57.0
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-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.12.0(eslint@8.57.0)(typescript@5.4.5))(eslint-import-resolver-typescript@3.6.1)(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(@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-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)
@@ -20336,7 +20397,7 @@ snapshots:
eslint-config-standard@17.1.0(eslint-plugin-import@2.29.1(@typescript-eslint/parser@7.12.0(eslint@8.57.0)(typescript@5.4.5))(eslint@8.57.0))(eslint-plugin-n@16.6.2(eslint@8.57.0))(eslint-plugin-promise@6.4.0(eslint@8.57.0))(eslint@8.57.0):
dependencies:
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@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(@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-n: 16.6.2(eslint@8.57.0)
eslint-plugin-promise: 6.4.0(eslint@8.57.0)
@@ -20346,9 +20407,9 @@ snapshots:
eslint-plugin-turbo: 2.5.4(eslint@8.57.0)(turbo@2.5.4)
turbo: 2.5.4
eslint-import-resolver-alias@1.1.2(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)):
eslint-import-resolver-alias@1.1.2(eslint-plugin-import@2.29.1):
dependencies:
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)
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(@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-import-resolver-node@0.3.9:
dependencies:
@@ -20363,8 +20424,8 @@ snapshots:
debug: 4.3.7(supports-color@5.5.0)
enhanced-resolve: 5.17.1
eslint: 8.57.0
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-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)
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)(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(@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)
fast-glob: 3.3.3
get-tsconfig: 4.8.1
is-core-module: 2.15.1
@@ -20381,7 +20442,7 @@ snapshots:
enhanced-resolve: 5.17.1
eslint: 8.57.0
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)
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(@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)
fast-glob: 3.3.3
get-tsconfig: 4.8.1
is-core-module: 2.15.1
@@ -20392,17 +20453,6 @@ snapshots:
- eslint-import-resolver-webpack
- supports-color
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-import-resolver-node@0.3.9)(eslint-plugin-import@2.29.1)(eslint@8.57.0))(eslint@8.57.0):
dependencies:
debug: 3.2.7
optionalDependencies:
'@typescript-eslint/parser': 7.12.0(eslint@8.57.0)(typescript@5.4.5)
eslint: 8.57.0
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-import-resolver-node@0.3.9)(eslint-plugin-import@2.29.1)(eslint@8.57.0)
transitivePeerDependencies:
- supports-color
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):
dependencies:
debug: 3.2.7
@@ -20414,6 +20464,17 @@ snapshots:
transitivePeerDependencies:
- supports-color
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)(eslint@8.57.0):
dependencies:
debug: 3.2.7
optionalDependencies:
'@typescript-eslint/parser': 7.12.0(eslint@8.57.0)(typescript@5.4.5)
eslint: 8.57.0
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-import-resolver-node@0.3.9)(eslint-plugin-import@2.29.1)(eslint@8.57.0)
transitivePeerDependencies:
- supports-color
eslint-plugin-es-x@7.8.0(eslint@8.57.0):
dependencies:
'@eslint-community/eslint-utils': 4.7.0(eslint@8.57.0)
@@ -20427,7 +20488,7 @@ snapshots:
eslint: 8.57.0
ignore: 5.3.2
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):
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(@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):
dependencies:
array-includes: 3.1.7
array.prototype.findlastindex: 1.2.4
@@ -20454,40 +20515,13 @@ snapshots:
- eslint-import-resolver-webpack
- supports-color
eslint-plugin-import@2.29.1(@typescript-eslint/parser@7.12.0(eslint@8.57.0)(typescript@5.4.5))(eslint@8.57.0):
dependencies:
array-includes: 3.1.7
array.prototype.findlastindex: 1.2.4
array.prototype.flat: 1.3.2
array.prototype.flatmap: 1.3.2
debug: 3.2.7
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.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-import-resolver-node@0.3.9)(eslint-plugin-import@2.29.1)(eslint@8.57.0))(eslint@8.57.0)
hasown: 2.0.2
is-core-module: 2.15.1
is-glob: 4.0.3
minimatch: 3.1.2
object.fromentries: 2.0.8
object.groupby: 1.0.3
object.values: 1.2.0
semver: 6.3.1
tsconfig-paths: 3.15.0
optionalDependencies:
'@typescript-eslint/parser': 7.12.0(eslint@8.57.0)(typescript@5.4.5)
transitivePeerDependencies:
- eslint-import-resolver-typescript
- eslint-import-resolver-webpack
- supports-color
eslint-plugin-jest@27.9.0(@typescript-eslint/eslint-plugin@7.3.1(@typescript-eslint/parser@7.12.0(eslint@8.57.0)(typescript@5.4.5))(eslint@8.57.0)(typescript@5.4.5))(eslint@8.57.0)(jest@29.7.0(@types/node@20.14.8))(typescript@5.4.5):
eslint-plugin-jest@27.9.0(@typescript-eslint/eslint-plugin@7.3.1(@typescript-eslint/parser@7.12.0(eslint@8.57.0)(typescript@5.4.5))(eslint@8.57.0)(typescript@5.4.5))(eslint@8.57.0)(jest@29.7.0(@types/node@20.14.8)(babel-plugin-macros@3.1.0))(typescript@5.4.5):
dependencies:
'@typescript-eslint/utils': 5.62.0(eslint@8.57.0)(typescript@5.4.5)
eslint: 8.57.0
optionalDependencies:
'@typescript-eslint/eslint-plugin': 7.3.1(@typescript-eslint/parser@7.12.0(eslint@8.57.0)(typescript@5.4.5))(eslint@8.57.0)(typescript@5.4.5)
jest: 29.7.0(@types/node@20.14.8)
jest: 29.7.0(@types/node@20.14.8)(babel-plugin-macros@3.1.0)
transitivePeerDependencies:
- supports-color
- typescript
@@ -20529,12 +20563,12 @@ snapshots:
eslint-plugin-only-warn@1.1.0: {}
eslint-plugin-playwright@1.5.4(eslint-plugin-jest@27.9.0(@typescript-eslint/eslint-plugin@7.3.1(@typescript-eslint/parser@7.12.0(eslint@8.57.0)(typescript@5.4.5))(eslint@8.57.0)(typescript@5.4.5))(eslint@8.57.0)(jest@29.7.0(@types/node@20.14.8))(typescript@5.4.5))(eslint@8.57.0):
eslint-plugin-playwright@1.5.4(eslint-plugin-jest@27.9.0(@typescript-eslint/eslint-plugin@7.3.1(@typescript-eslint/parser@7.12.0(eslint@8.57.0)(typescript@5.4.5))(eslint@8.57.0)(typescript@5.4.5))(eslint@8.57.0)(jest@29.7.0(@types/node@20.14.8)(babel-plugin-macros@3.1.0))(typescript@5.4.5))(eslint@8.57.0):
dependencies:
eslint: 8.57.0
globals: 13.24.0
optionalDependencies:
eslint-plugin-jest: 27.9.0(@typescript-eslint/eslint-plugin@7.3.1(@typescript-eslint/parser@7.12.0(eslint@8.57.0)(typescript@5.4.5))(eslint@8.57.0)(typescript@5.4.5))(eslint@8.57.0)(jest@29.7.0(@types/node@20.14.8))(typescript@5.4.5)
eslint-plugin-jest: 27.9.0(@typescript-eslint/eslint-plugin@7.3.1(@typescript-eslint/parser@7.12.0(eslint@8.57.0)(typescript@5.4.5))(eslint@8.57.0)(typescript@5.4.5))(eslint@8.57.0)(jest@29.7.0(@types/node@20.14.8)(babel-plugin-macros@3.1.0))(typescript@5.4.5)
eslint-plugin-prettier@5.1.3(@types/eslint@8.56.12)(eslint-config-prettier@9.1.0(eslint@8.57.0))(eslint@8.57.0)(prettier@3.3.3):
dependencies:
@@ -20617,13 +20651,13 @@ snapshots:
transitivePeerDependencies:
- supports-color
eslint-plugin-vitest@0.3.26(@typescript-eslint/eslint-plugin@7.3.1(@typescript-eslint/parser@7.12.0(eslint@8.57.0)(typescript@5.4.5))(eslint@8.57.0)(typescript@5.4.5))(eslint@8.57.0)(typescript@5.4.5)(vitest@2.1.2(@types/node@20.14.8)):
eslint-plugin-vitest@0.3.26(@typescript-eslint/eslint-plugin@7.3.1(@typescript-eslint/parser@7.12.0(eslint@8.57.0)(typescript@5.4.5))(eslint@8.57.0)(typescript@5.4.5))(eslint@8.57.0)(typescript@5.4.5)(vitest@2.1.2(@types/node@20.14.8)(jsdom@20.0.3)(msw@2.6.5(@types/node@20.14.8)(typescript@5.4.5))(terser@5.43.1)):
dependencies:
'@typescript-eslint/utils': 7.3.1(eslint@8.57.0)(typescript@5.4.5)
eslint: 8.57.0
optionalDependencies:
'@typescript-eslint/eslint-plugin': 7.3.1(@typescript-eslint/parser@7.12.0(eslint@8.57.0)(typescript@5.4.5))(eslint@8.57.0)(typescript@5.4.5)
vitest: 2.1.2(@types/node@20.14.8)
vitest: 2.1.2(@types/node@20.14.8)(jsdom@20.0.3)(msw@2.6.5(@types/node@20.14.8)(typescript@5.4.5))(terser@5.43.1)
transitivePeerDependencies:
- supports-color
- typescript
@@ -21884,13 +21918,13 @@ snapshots:
- supports-color
- ts-node
jest-cli@29.7.0(@types/node@20.14.8):
jest-cli@29.7.0(@types/node@20.14.8)(babel-plugin-macros@3.1.0):
dependencies:
'@jest/core': 29.7.0(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@types/node@20.10.5)(typescript@5.4.5))
'@jest/test-result': 29.7.0
'@jest/types': 29.6.3
chalk: 4.1.2
create-jest: 29.7.0(@types/node@20.14.8)
create-jest: 29.7.0(@types/node@20.14.8)(babel-plugin-macros@3.1.0)
exit: 0.1.2
import-local: 3.1.0
jest-config: 29.7.0(@types/node@20.14.8)(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@types/node@20.10.5)(typescript@5.4.5))
@@ -22191,7 +22225,7 @@ snapshots:
jest-worker@27.5.1:
dependencies:
'@types/node': 20.10.5
'@types/node': 20.14.8
merge-stream: 2.0.0
supports-color: 8.1.1
@@ -22214,12 +22248,12 @@ snapshots:
- supports-color
- ts-node
jest@29.7.0(@types/node@20.14.8):
jest@29.7.0(@types/node@20.14.8)(babel-plugin-macros@3.1.0):
dependencies:
'@jest/core': 29.7.0(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@types/node@20.10.5)(typescript@5.4.5))
'@jest/types': 29.6.3
import-local: 3.1.0
jest-cli: 29.7.0(@types/node@20.14.8)
jest-cli: 29.7.0(@types/node@20.14.8)(babel-plugin-macros@3.1.0)
transitivePeerDependencies:
- '@types/node'
- babel-plugin-macros
@@ -22417,19 +22451,19 @@ snapshots:
- openai
- ws
langfuse-core@3.37.4:
langfuse-core@3.38.1:
dependencies:
mustache: 4.2.0
langfuse-langchain@3.37.4(langchain@0.3.28(@langchain/anthropic@0.3.22(@langchain/core@0.3.58(openai@4.104.0(zod@3.25.62))))(@langchain/aws@0.1.11(@langchain/core@0.3.58(openai@4.104.0(zod@3.25.62))))(@langchain/core@0.3.58(openai@4.104.0(zod@3.25.62)))(@langchain/google-genai@0.2.12(@langchain/core@0.3.58(openai@4.104.0(zod@3.25.62))))(@langchain/google-vertexai@0.2.12(@langchain/core@0.3.58(openai@4.104.0(zod@3.25.62)))(zod@3.25.62))(axios@1.8.2)(cheerio@1.0.0)(handlebars@4.7.8)(openai@4.104.0(zod@3.25.62))):
langfuse-langchain@3.38.1(langchain@0.3.28(@langchain/anthropic@0.3.22(@langchain/core@0.3.58(openai@4.104.0(zod@3.25.62))))(@langchain/aws@0.1.11(@langchain/core@0.3.58(openai@4.104.0(zod@3.25.62))))(@langchain/core@0.3.58(openai@4.104.0(zod@3.25.62)))(@langchain/google-genai@0.2.12(@langchain/core@0.3.58(openai@4.104.0(zod@3.25.62))))(@langchain/google-vertexai@0.2.12(@langchain/core@0.3.58(openai@4.104.0(zod@3.25.62)))(zod@3.25.62))(axios@1.8.2)(cheerio@1.0.0)(handlebars@4.7.8)(openai@4.104.0(zod@3.25.62))):
dependencies:
langchain: 0.3.28(@langchain/anthropic@0.3.22(@langchain/core@0.3.58(openai@4.104.0(zod@3.25.62))))(@langchain/aws@0.1.11(@langchain/core@0.3.58(openai@4.104.0(zod@3.25.62))))(@langchain/core@0.3.58(openai@4.104.0(zod@3.25.62)))(@langchain/google-genai@0.2.12(@langchain/core@0.3.58(openai@4.104.0(zod@3.25.62))))(@langchain/google-vertexai@0.2.12(@langchain/core@0.3.58(openai@4.104.0(zod@3.25.62)))(zod@3.25.62))(axios@1.8.2)(cheerio@1.0.0)(handlebars@4.7.8)(openai@4.104.0(zod@3.25.62))
langfuse: 3.37.4
langfuse-core: 3.37.4
langfuse: 3.38.1
langfuse-core: 3.38.1
langfuse@3.37.4:
langfuse@3.38.1:
dependencies:
langfuse-core: 3.37.4
langfuse-core: 3.38.1
langium@3.0.0:
dependencies:
@@ -23127,6 +23161,32 @@ snapshots:
optionalDependencies:
msgpackr-extract: 3.0.3
msw@2.6.5(@types/node@20.10.5)(typescript@5.4.5):
dependencies:
'@bundled-es-modules/cookie': 2.0.1
'@bundled-es-modules/statuses': 1.0.1
'@bundled-es-modules/tough-cookie': 0.1.6
'@inquirer/confirm': 5.0.2(@types/node@20.10.5)
'@mswjs/interceptors': 0.37.1
'@open-draft/deferred-promise': 2.2.0
'@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.3
path-to-regexp: 6.3.0
strict-event-emitter: 0.5.1
type-fest: 4.27.0
yargs: 17.7.2
optionalDependencies:
typescript: 5.4.5
transitivePeerDependencies:
- '@types/node'
optional: true
msw@2.6.5(@types/node@20.11.29)(typescript@5.4.5):
dependencies:
'@bundled-es-modules/cookie': 2.0.1
@@ -23152,6 +23212,32 @@ snapshots:
transitivePeerDependencies:
- '@types/node'
msw@2.6.5(@types/node@20.14.8)(typescript@5.4.5):
dependencies:
'@bundled-es-modules/cookie': 2.0.1
'@bundled-es-modules/statuses': 1.0.1
'@bundled-es-modules/tough-cookie': 0.1.6
'@inquirer/confirm': 5.0.2(@types/node@20.14.8)
'@mswjs/interceptors': 0.37.1
'@open-draft/deferred-promise': 2.2.0
'@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.3
path-to-regexp: 6.3.0
strict-event-emitter: 0.5.1
type-fest: 4.27.0
yargs: 17.7.2
optionalDependencies:
typescript: 5.4.5
transitivePeerDependencies:
- '@types/node'
optional: true
mustache@4.2.0: {}
mute-stream@2.0.0: {}
@@ -25180,14 +25266,6 @@ snapshots:
terser: 5.43.1
webpack: 5.97.1
terser@5.43.0:
dependencies:
'@jridgewell/source-map': 0.3.6
acorn: 8.15.0
commander: 2.20.3
source-map-support: 0.5.21
optional: true
terser@5.43.1:
dependencies:
'@jridgewell/source-map': 0.3.6
@@ -25681,12 +25759,12 @@ snapshots:
'@egjs/hammerjs': 2.0.17
component-emitter: 1.3.1
vite-node@2.1.2(@types/node@20.10.5):
vite-node@2.1.2(@types/node@20.10.5)(terser@5.43.1):
dependencies:
cac: 6.7.14
debug: 4.3.7(supports-color@5.5.0)
pathe: 1.1.2
vite: 5.2.14(@types/node@20.10.5)
vite: 5.2.14(@types/node@20.10.5)(terser@5.43.1)
transitivePeerDependencies:
- '@types/node'
- less
@@ -25698,12 +25776,12 @@ snapshots:
- terser
optional: true
vite-node@2.1.2(@types/node@20.11.29)(terser@5.43.0):
vite-node@2.1.2(@types/node@20.11.29)(terser@5.43.1):
dependencies:
cac: 6.7.14
debug: 4.3.7(supports-color@5.5.0)
pathe: 1.1.2
vite: 5.2.14(@types/node@20.11.29)(terser@5.43.0)
vite: 5.2.14(@types/node@20.11.29)(terser@5.43.1)
transitivePeerDependencies:
- '@types/node'
- less
@@ -25714,12 +25792,12 @@ snapshots:
- supports-color
- terser
vite-node@2.1.2(@types/node@20.14.8):
vite-node@2.1.2(@types/node@20.14.8)(terser@5.43.1):
dependencies:
cac: 6.7.14
debug: 4.3.7(supports-color@5.5.0)
pathe: 1.1.2
vite: 5.2.14(@types/node@20.14.8)
vite: 5.2.14(@types/node@20.14.8)(terser@5.43.1)
transitivePeerDependencies:
- '@types/node'
- less
@@ -25731,7 +25809,7 @@ snapshots:
- terser
optional: true
vite@5.2.14(@types/node@20.10.5):
vite@5.2.14(@types/node@20.10.5)(terser@5.43.1):
dependencies:
esbuild: 0.20.2
postcss: 8.5.3
@@ -25739,9 +25817,10 @@ snapshots:
optionalDependencies:
'@types/node': 20.10.5
fsevents: 2.3.3
terser: 5.43.1
optional: true
vite@5.2.14(@types/node@20.11.29)(terser@5.43.0):
vite@5.2.14(@types/node@20.11.29)(terser@5.43.1):
dependencies:
esbuild: 0.20.2
postcss: 8.5.3
@@ -25749,9 +25828,9 @@ snapshots:
optionalDependencies:
'@types/node': 20.11.29
fsevents: 2.3.3
terser: 5.43.0
terser: 5.43.1
vite@5.2.14(@types/node@20.14.8):
vite@5.2.14(@types/node@20.14.8)(terser@5.43.1):
dependencies:
esbuild: 0.20.2
postcss: 8.5.3
@@ -25759,12 +25838,13 @@ snapshots:
optionalDependencies:
'@types/node': 20.14.8
fsevents: 2.3.3
terser: 5.43.1
optional: true
vitest@2.1.2(@types/node@20.10.5):
vitest@2.1.2(@types/node@20.10.5)(jsdom@20.0.3)(msw@2.6.5(@types/node@20.10.5)(typescript@5.4.5))(terser@5.43.1):
dependencies:
'@vitest/expect': 2.1.2
'@vitest/mocker': 2.1.2(@vitest/spy@2.1.2)(vite@5.2.14(@types/node@20.10.5))
'@vitest/mocker': 2.1.2(@vitest/spy@2.1.2)(msw@2.6.5(@types/node@20.10.5)(typescript@5.4.5))(vite@5.2.14(@types/node@20.10.5)(terser@5.43.1))
'@vitest/pretty-format': 2.1.2
'@vitest/runner': 2.1.2
'@vitest/snapshot': 2.1.2
@@ -25779,11 +25859,12 @@ snapshots:
tinyexec: 0.3.0
tinypool: 1.0.1
tinyrainbow: 1.2.0
vite: 5.2.14(@types/node@20.10.5)
vite-node: 2.1.2(@types/node@20.10.5)
vite: 5.2.14(@types/node@20.10.5)(terser@5.43.1)
vite-node: 2.1.2(@types/node@20.10.5)(terser@5.43.1)
why-is-node-running: 2.3.0
optionalDependencies:
'@types/node': 20.10.5
jsdom: 20.0.3
transitivePeerDependencies:
- less
- lightningcss
@@ -25795,10 +25876,10 @@ snapshots:
- terser
optional: true
vitest@2.1.2(@types/node@20.11.29)(jsdom@20.0.3)(msw@2.6.5(@types/node@20.11.29)(typescript@5.4.5))(terser@5.43.0):
vitest@2.1.2(@types/node@20.11.29)(jsdom@20.0.3)(msw@2.6.5(@types/node@20.11.29)(typescript@5.4.5))(terser@5.43.1):
dependencies:
'@vitest/expect': 2.1.2
'@vitest/mocker': 2.1.2(@vitest/spy@2.1.2)(msw@2.6.5(@types/node@20.11.29)(typescript@5.4.5))(vite@5.2.14(@types/node@20.11.29)(terser@5.43.0))
'@vitest/mocker': 2.1.2(@vitest/spy@2.1.2)(msw@2.6.5(@types/node@20.11.29)(typescript@5.4.5))(vite@5.2.14(@types/node@20.11.29)(terser@5.43.1))
'@vitest/pretty-format': 2.1.2
'@vitest/runner': 2.1.2
'@vitest/snapshot': 2.1.2
@@ -25813,8 +25894,8 @@ snapshots:
tinyexec: 0.3.0
tinypool: 1.0.1
tinyrainbow: 1.2.0
vite: 5.2.14(@types/node@20.11.29)(terser@5.43.0)
vite-node: 2.1.2(@types/node@20.11.29)(terser@5.43.0)
vite: 5.2.14(@types/node@20.11.29)(terser@5.43.1)
vite-node: 2.1.2(@types/node@20.11.29)(terser@5.43.1)
why-is-node-running: 2.3.0
optionalDependencies:
'@types/node': 20.11.29
@@ -25829,10 +25910,10 @@ snapshots:
- supports-color
- terser
vitest@2.1.2(@types/node@20.14.8):
vitest@2.1.2(@types/node@20.14.8)(jsdom@20.0.3)(msw@2.6.5(@types/node@20.14.8)(typescript@5.4.5))(terser@5.43.1):
dependencies:
'@vitest/expect': 2.1.2
'@vitest/mocker': 2.1.2(@vitest/spy@2.1.2)(vite@5.2.14(@types/node@20.14.8))
'@vitest/mocker': 2.1.2(@vitest/spy@2.1.2)(msw@2.6.5(@types/node@20.14.8)(typescript@5.4.5))(vite@5.2.14(@types/node@20.14.8)(terser@5.43.1))
'@vitest/pretty-format': 2.1.2
'@vitest/runner': 2.1.2
'@vitest/snapshot': 2.1.2
@@ -25847,11 +25928,12 @@ snapshots:
tinyexec: 0.3.0
tinypool: 1.0.1
tinyrainbow: 1.2.0
vite: 5.2.14(@types/node@20.14.8)
vite-node: 2.1.2(@types/node@20.14.8)
vite: 5.2.14(@types/node@20.14.8)(terser@5.43.1)
vite-node: 2.1.2(@types/node@20.14.8)(terser@5.43.1)
why-is-node-running: 2.3.0
optionalDependencies:
'@types/node': 20.14.8
jsdom: 20.0.3
transitivePeerDependencies:
- less
- lightningcss
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "web",
"version": "3.78.1",
"version": "3.78.2",
"private": true,
"license": "MIT",
"engines": {
@@ -244,6 +244,67 @@ describe("/api/public/scores API Endpoint", () => {
"test-key": "test-value-updated",
});
});
it("should post score with score config if in valid range", async () => {
const configId = v4();
const traceId = v4();
const scoreId = v4();
const { projectId: projectId, auth } = await createOrgProjectAndApiKey();
const config = await prisma.scoreConfig.create({
data: {
name: "score-name",
id: configId,
dataType: "NUMERIC",
maxValue: 100,
projectId: projectId,
},
});
const trace = createTrace({
id: traceId,
project_id: projectId,
});
await createTracesCh([trace]);
const score = createTraceScore({
id: scoreId,
project_id: projectId,
trace_id: traceId,
name: "score-name",
value: 100,
source: "API",
comment: "comment",
metadata: { "test-key": "test-value" },
observation_id: null,
environment: "production",
config_id: config.id,
});
await createScoresCh([score]);
const fetchedScore = await makeZodVerifiedAPICall(
GetScoreResponseV1,
"GET",
`/api/public/scores/${scoreId}`,
undefined,
auth,
);
expect(fetchedScore.body?.id).toBe(scoreId);
expect(fetchedScore.body?.traceId).toBe(traceId);
expect(fetchedScore.body?.name).toBe("score-name");
expect(fetchedScore.body?.value).toBe(100);
expect(fetchedScore.body?.configId).toBe(configId);
expect(fetchedScore.body?.observationId).toBeNull();
expect(fetchedScore.body?.comment).toBe("comment");
expect(fetchedScore.body?.source).toBe("API");
expect(fetchedScore.body?.projectId).toBe(projectId);
expect(fetchedScore.body?.environment).toBe("production");
expect(fetchedScore.body?.metadata).toEqual({
"test-key": "test-value",
});
});
});
describe("GET /api/public/scores", () => {
+1 -1
View File
@@ -1 +1 @@
export const VERSION = "v3.78.1";
export const VERSION = "v3.78.2";
@@ -456,13 +456,15 @@ export function PreviewCsvImport({
</Button>
<Button
disabled={
selectedInputColumn.size === 0 &&
selectedExpectedColumn.size === 0 &&
selectedMetadataColumn.size === 0
(selectedInputColumn.size === 0 &&
selectedExpectedColumn.size === 0 &&
selectedMetadataColumn.size === 0) ||
progress.status === "processing"
}
loading={progress.status === "processing"}
onClick={handleImport}
>
Import
{progress.status === "processing" ? "Importing..." : "Import"}
</Button>
{progress.status === "processing" && (
<div className="mt-2">
@@ -619,7 +619,7 @@ export class OtelIngestionProcessor {
}
private hasTraceUpdates(attributes: Record<string, unknown>): boolean {
return [
const hasExactMatchingAttributeName = [
LangfuseOtelSpanAttributes.TRACE_NAME,
LangfuseOtelSpanAttributes.TRACE_INPUT,
LangfuseOtelSpanAttributes.TRACE_OUTPUT,
@@ -637,6 +637,13 @@ export class OtelIngestionProcessor {
`${LangfuseOtelSpanAttributes.TRACE_METADATA}.langfuse_user_id`,
`${LangfuseOtelSpanAttributes.TRACE_METADATA}.langfuse_tags`,
].some((traceAttribute) => Boolean(attributes[traceAttribute]));
const attributeKeys = Object.keys(attributes);
const hasTraceMetadataKey = attributeKeys.some((key) =>
key.startsWith(LangfuseOtelSpanAttributes.TRACE_METADATA),
);
return hasExactMatchingAttributeName || hasTraceMetadataKey;
}
private extractResourceAttributes(
@@ -991,9 +991,42 @@ export function WidgetForm({
value={selectedView}
onValueChange={(value) => {
if (value !== selectedView) {
const newView = value as z.infer<typeof views>;
const newViewDeclaration = viewDeclarations[newView];
// Reset regular chart fields
setSelectedMeasure("count");
setSelectedAggregation("count");
setSelectedDimension("none");
// Handle pivot table metrics - filter out invalid measures for the new view
if (selectedChartType === "PIVOT_TABLE") {
const validMetrics = selectedMetrics.filter(
(metric) =>
metric.measure in newViewDeclaration.measures,
);
// Ensure we have at least one valid metric (count is always available)
if (validMetrics.length === 0) {
validMetrics.push({
id: "count_count",
measure: "count",
aggregation: "count" as z.infer<
typeof metricAggregations
>,
label: "Count Count",
});
}
setSelectedMetrics(validMetrics);
// Handle pivot table dimensions - filter out invalid dimensions for the new view
const validDimensions = pivotDimensions.filter(
(dimension) =>
dimension in newViewDeclaration.dimensions,
);
setPivotDimensions(validDimensions);
}
}
setSelectedView(value as z.infer<typeof views>);
}}
@@ -1109,39 +1142,38 @@ export function WidgetForm({
</Select>
</div>
{currentMeasure &&
currentMeasure !== "count" && (
<div className="flex-1">
<Select
value={currentAggregation}
onValueChange={(value) =>
updatePivotMetric(
index,
currentMeasure,
value as z.infer<
typeof metricAggregations
>,
)
}
>
<SelectTrigger>
<SelectValue placeholder="Select aggregation" />
</SelectTrigger>
<SelectContent>
{aggregationsForIndex.map(
(aggregation) => (
<SelectItem
key={aggregation}
value={aggregation}
>
{startCase(aggregation)}
</SelectItem>
),
)}
</SelectContent>
</Select>
</div>
)}
{currentMeasure && currentMeasure !== "count" && (
<div className="flex-1">
<Select
value={currentAggregation}
onValueChange={(value) =>
updatePivotMetric(
index,
currentMeasure,
value as z.infer<
typeof metricAggregations
>,
)
}
>
<SelectTrigger>
<SelectValue placeholder="Select aggregation" />
</SelectTrigger>
<SelectContent>
{aggregationsForIndex.map(
(aggregation) => (
<SelectItem
key={aggregation}
value={aggregation}
>
{startCase(aggregation)}
</SelectItem>
),
)}
</SelectContent>
</Select>
</div>
)}
</div>
</div>
);
+2 -2
View File
@@ -1,6 +1,6 @@
{
"name": "worker",
"version": "3.78.1",
"version": "3.78.2",
"description": "",
"license": "MIT",
"private": true,
@@ -13,7 +13,7 @@
"start": "dotenv -e ../.env -- node dist/index.js",
"build": "tsc",
"dev": "dotenv -e ../.env -- nodemon src/index.ts",
"lint": "eslint . --ext .js,.jsx,.ts,.tsx --max-warnings 21",
"lint": "eslint . --ext .js,.jsx,.ts,.tsx --max-warnings 0",
"lint:fix": "eslint . --ext .js,.jsx,.ts,.tsx --fix",
"refill-ingestion-events": "dotenv -e ../.env -- tsx src/scripts/s3-ingestion-event-relpay.ts",
"refill-billing-event": "dotenv -e ../.env -- tsx src/scripts/refill-billing-event.ts"
+1 -1
View File
@@ -1 +1 @@
export const VERSION = "v3.78.1";
export const VERSION = "v3.78.2";
+1 -1
View File
@@ -232,6 +232,6 @@ const EnvSchema = z.object({
});
export const env: z.infer<typeof EnvSchema> =
process.env.DOCKER_BUILD === "1"
process.env.DOCKER_BUILD === "1" // eslint-disable-line turbo/no-undeclared-env-vars
? (process.env as any)
: EnvSchema.parse(removeEmptyEnvVariables(process.env));
@@ -49,6 +49,7 @@ import {
TraceDomain,
Observation,
DatasetItem,
QUEUE_ERROR_MESSAGES,
} from "@langfuse/shared";
import { kyselyPrisma, prisma } from "@langfuse/shared/src/db";
import { backOff } from "exponential-backoff";
@@ -877,11 +878,11 @@ export async function extractVariablesFromTracingData({
// user facing errors
if (!observation) {
logger.warn(
`Observation ${mapping.objectName} for trace ${traceId} not found. Please ensure the mapped data exists and consider extending the job delay.`,
`Observation ${mapping.objectName} for trace ${traceId} not found. ${QUEUE_ERROR_MESSAGES.MAPPED_DATA_ERROR}`,
);
// this should only happen for deleted data or data replication lags across clickhouse nodes.
throw new LangfuseNotFoundError(
`Observation ${mapping.objectName} for trace ${traceId} not found. Please ensure the mapped data exists and consider extending the job delay.`,
`Observation ${mapping.objectName} for trace ${traceId} not found. ${QUEUE_ERROR_MESSAGES.MAPPED_DATA_ERROR}`,
);
}
@@ -24,6 +24,7 @@ import {
LangfuseNotFoundError,
type Prisma,
PromptType,
QUEUE_ERROR_MESSAGES,
stringifyValue,
} from "@langfuse/shared";
import { backOff } from "exponential-backoff";
@@ -51,8 +52,8 @@ const replaceVariablesInPrompt = (
const processContent = (content: string) => {
// Extract only Handlebars variables from itemInput (exclude message placeholders)
const filteredContext = Object.fromEntries(
Object.entries(itemInput).filter(([key]) =>
variables.includes(key) && !placeholderNames.includes(key)
Object.entries(itemInput).filter(
([key]) => variables.includes(key) && !placeholderNames.includes(key),
),
);
@@ -85,29 +86,36 @@ const replaceVariablesInPrompt = (
// for stringified arrays (e.g. from dataset processing)
let actualValue = value;
if (typeof value === 'string') {
if (typeof value === "string") {
try {
actualValue = JSON.parse(value);
} catch (_e) {
throw new Error(`Invalid placeholder value for '${placeholderName}': unable to parse JSON`);
throw new Error(
`Invalid placeholder value for '${placeholderName}': unable to parse JSON`,
);
}
}
if (!Array.isArray(actualValue)) {
throw new Error(`Placeholder '${placeholderName}' must be an array of messages`);
throw new Error(
`Placeholder '${placeholderName}' must be an array of messages`,
);
}
const validMessages = actualValue.every(msg =>
typeof msg === 'object' &&
msg !== null &&
'role' in msg &&
'content' in msg
const validMessages = actualValue.every(
(msg) =>
typeof msg === "object" &&
msg !== null &&
"role" in msg &&
"content" in msg,
);
if (!validMessages) {
throw new Error(`Invalid placeholder value for '${placeholderName}': messages must have 'role' and 'content' properties`);
throw new Error(
`Invalid placeholder value for '${placeholderName}': messages must have 'role' and 'content' properties`,
);
}
placeholderValues[placeholderName] = actualValue.map(msg => ({
placeholderValues[placeholderName] = actualValue.map((msg) => ({
...msg,
type: ChatMessageType.PublicAPICreated as const,
}));
@@ -116,7 +124,7 @@ const replaceVariablesInPrompt = (
const compiledMessages = compileChatMessages(
prompt as PromptMessage[],
placeholderValues,
{}
{},
);
return compiledMessages.map((message) => ({
@@ -227,13 +235,13 @@ export const createExperimentJob = async ({
});
if (!apiKey) {
throw new LangfuseNotFoundError(
`API key for provider ${provider} not found`,
`${QUEUE_ERROR_MESSAGES.API_KEY_ERROR} ${provider} not found`,
);
}
const validatedApiKey = LLMApiKeySchema.safeParse(apiKey);
if (!validatedApiKey.success) {
throw new InvalidRequestError(
`API key for provider ${provider} not found.`,
`${QUEUE_ERROR_MESSAGES.API_KEY_ERROR} ${provider} not found.`,
);
}
@@ -257,9 +265,10 @@ export const createExperimentJob = async ({
);
// also extract placeholder names if prompt is a chat prompt
const placeholderNames = prompt?.type === PromptType.Chat && Array.isArray(validatedPrompt.data)
? extractPlaceholderNames(validatedPrompt.data as PromptMessage[])
: [];
const placeholderNames =
prompt?.type === PromptType.Chat && Array.isArray(validatedPrompt.data)
? extractPlaceholderNames(validatedPrompt.data as PromptMessage[])
: [];
const allVariables = [...extractedVariables, ...placeholderNames];
// validate dataset items against prompt configuration
+14 -11
View File
@@ -1,5 +1,10 @@
import { Job } from "bullmq";
import { ApiError, BaseError, LangfuseNotFoundError } from "@langfuse/shared";
import {
ApiError,
BaseError,
LangfuseNotFoundError,
QUEUE_ERROR_MESSAGES,
} from "@langfuse/shared";
import { kyselyPrisma } from "@langfuse/shared/src/db";
import { sql } from "kysely";
import {
@@ -150,22 +155,20 @@ export const evalJobExecutorQueueProcessor = async (
e instanceof LangfuseNotFoundError ||
(e instanceof BaseError &&
e.message.includes(
"Could not parse response content as the length limit was reached",
QUEUE_ERROR_MESSAGES.OUTPUT_TOKENS_TOO_LONG_ERROR,
)) || // output tokens too long
(e instanceof BaseError && e.message.includes("API key for provider")) || // api key not provided
(e instanceof BaseError &&
e.message.includes(
"`No default model or custom model found for project",
)) || // api key not provided
e.message.includes(QUEUE_ERROR_MESSAGES.API_KEY_ERROR)) || // api key not provided
(e instanceof BaseError &&
e.message.includes(QUEUE_ERROR_MESSAGES.NO_DEFAULT_MODEL_ERROR)) || // api key not provided
(e instanceof ApiError && e.httpCode >= 400 && e.httpCode < 500) || // do not error and retry on 4xx errors. They are visible to the user in the UI but do not alert us.
(e instanceof ApiError && e.message.includes("TypeError")) || // Zod parsing the response failed. User should update prompt to consistently return expected output structure.
(e instanceof ApiError &&
e.message.includes("Error: Unterminated string in JSON at position")) || // When evaluator model is configured with too low max_tokens, the structured output response is invalid JSON
(e instanceof ApiError && e.message.includes("is not valid JSON")) || // When evaluator model is not consistently returning valid JSON on structured output calls
e.message.includes(QUEUE_ERROR_MESSAGES.TOO_LOW_MAX_TOKENS_ERROR)) || // When evaluator model is configured with too low max_tokens, the structured output response is invalid JSON
(e instanceof ApiError &&
e.message.includes(QUEUE_ERROR_MESSAGES.INVALID_JSON_ERROR)) || // When evaluator model is not consistently returning valid JSON on structured output calls
(e instanceof BaseError &&
e.message.includes(
"Please ensure the mapped data exists and consider extending the job delay.",
)) // Trace not found.
e.message.includes(QUEUE_ERROR_MESSAGES.MAPPED_DATA_ERROR)) // Trace not found.
) {
return;
}
@@ -26,7 +26,7 @@ export class ClickhouseWriter {
queue: ClickhouseQueue;
isIntervalFlushInProgress: boolean;
intervalId: NodeJS.Timeout | null = null;
intervalId: NodeJS.Timeout | null = null; // eslint-disable-line no-undef
private constructor() {
this.batchSize = env.LANGFUSE_INGESTION_CLICKHOUSE_WRITE_BATCH_SIZE;
@@ -239,11 +239,11 @@ export class ClickhouseWriter {
}
export enum TableName {
Traces = "traces",
TracesMt = "traces_mt",
Scores = "scores",
Observations = "observations",
BlobStorageFileLog = "blob_storage_file_log",
Traces = "traces", // eslint-disable-line no-unused-vars
TracesMt = "traces_mt", // eslint-disable-line no-unused-vars
Scores = "scores", // eslint-disable-line no-unused-vars
Observations = "observations", // eslint-disable-line no-unused-vars
BlobStorageFileLog = "blob_storage_file_log", // eslint-disable-line no-unused-vars
}
type RecordInsertType<T extends TableName> = T extends TableName.Scores
+146 -9
View File
@@ -71,7 +71,11 @@ CREATE TABLE traces_all_amt
`name` SimpleAggregateFunction(anyLast, String),
-- Metadata properties
`metadata` AggregateFunction(argMax, Map(LowCardinality(String), String), DateTime64(3)),
-- Metadata takes the last seen value in this form.
-- If we spread it across multiple rows (keys + values) we might be able to allow updates/overwrites.
-- Indexing speed in this case is unclear though.
`metadata_argmax` AggregateFunction(argMax, Map(LowCardinality(String), String), DateTime64(3)),
`metadata` SimpleAggregateFunction(anyLast, Map(String, String)),
`user_id_argmax` AggregateFunction(argMax, String, DateTime64(3)),
`session_id_argmax` AggregateFunction(argMax, String, DateTime64(3)),
`environment_argmax` AggregateFunction(argMax, String, DateTime64(3)),
@@ -121,14 +125,15 @@ SELECT
anyLast(t0.name) as name,
-- Metadata properties
argMaxState(t0.metadata, t0.event_ts) as metadata,
argMaxState(t0.metadata, if(t0.metadata <> '{}', t0.event_ts, toDateTime64(0,3))) as metadata_argmax,
anyLastMap(t0.metadata) as metadata,
argMaxState(t0.user_id, if(t0.user_id <> '', t0.event_ts, toDateTime64(0, 3))) as user_id_argmax,
argMaxState(t0.session_id, if(t0.session_id <> '', t0.event_ts, toDateTime64(0, 3))) as session_id_argmax,
argMaxState(t0.environment, if(t0.environment <> '', t0.event_ts, toDateTime64(0, 3))) as environment_argmax,
anyLast(t0.user_id) as user_id,
anyLast(t0.session_id) as session_id,
anyLast(t0.environment) as environment,
groupUniqArrayArray(t0.tags) as tags,
groupUniqArrayArray(t0.tags) as tags,
argMaxState(t0.version, if(t0.version <> '', t0.event_ts, toDateTime64(0, 3))) as version,
argMaxState(t0.release, if(t0.release <> '', t0.event_ts, toDateTime64(0, 3))) as release,
@@ -165,7 +170,8 @@ CREATE TABLE traces_7d_amt
`name` SimpleAggregateFunction(anyLast, String),
-- Metadata properties
`metadata` AggregateFunction(argMax, Map(LowCardinality(String), String), DateTime64(3)),
`metadata_argmax` AggregateFunction(argMax, Map(LowCardinality(String), String), DateTime64(3)),
`metadata` SimpleAggregateFunction(anyLast, Map(String, String)),
`user_id_argmax` AggregateFunction(argMax, String, DateTime64(3)),
`session_id_argmax` AggregateFunction(argMax, String, DateTime64(3)),
`environment_argmax` AggregateFunction(argMax, String, DateTime64(3)),
@@ -215,14 +221,15 @@ SELECT
anyLast(t0.name) as name,
-- Metadata properties
argMaxState(t0.metadata, t0.event_ts) as metadata,
argMaxState(t0.metadata, if(t0.metadata <> '{}', t0.event_ts, toDateTime64(0,3))) as metadata_argmax,
anyLastMap(t0.metadata) as metadata,
argMaxState(t0.user_id, if(t0.user_id <> '', t0.event_ts, toDateTime64(0, 3))) as user_id_argmax,
argMaxState(t0.session_id, if(t0.session_id <> '', t0.event_ts, toDateTime64(0, 3))) as session_id_argmax,
argMaxState(t0.environment, if(t0.environment <> '', t0.event_ts, toDateTime64(0, 3))) as environment_argmax,
anyLast(t0.user_id) as user_id,
anyLast(t0.session_id) as session_id,
anyLast(t0.environment) as environment,
groupUniqArrayArray(t0.tags) as tags,
groupUniqArrayArray(t0.tags) as tags,
argMaxState(t0.version, if(t0.version <> '', t0.event_ts, toDateTime64(0, 3))) as version,
argMaxState(t0.release, if(t0.release <> '', t0.event_ts, toDateTime64(0, 3))) as release,
@@ -259,7 +266,8 @@ CREATE TABLE traces_30d_amt
`name` SimpleAggregateFunction(anyLast, String),
-- Metadata properties
`metadata` AggregateFunction(argMax, Map(LowCardinality(String), String), DateTime64(3)),
`metadata_argmax` AggregateFunction(argMax, Map(LowCardinality(String), String), DateTime64(3)),
`metadata` SimpleAggregateFunction(anyLast, Map(String, String)),
`user_id_argmax` AggregateFunction(argMax, String, DateTime64(3)),
`session_id_argmax` AggregateFunction(argMax, String, DateTime64(3)),
`environment_argmax` AggregateFunction(argMax, String, DateTime64(3)),
@@ -309,14 +317,15 @@ SELECT
anyLast(t0.name) as name,
-- Metadata properties
argMaxState(t0.metadata, t0.event_ts) as metadata,
argMaxState(t0.metadata, if(t0.metadata <> '{}', t0.event_ts, toDateTime64(0,3))) as metadata_argmax,
anyLastMap(t0.metadata) as metadata,
argMaxState(t0.user_id, if(t0.user_id <> '', t0.event_ts, toDateTime64(0, 3))) as user_id_argmax,
argMaxState(t0.session_id, if(t0.session_id <> '', t0.event_ts, toDateTime64(0, 3))) as session_id_argmax,
argMaxState(t0.environment, if(t0.environment <> '', t0.event_ts, toDateTime64(0, 3))) as environment_argmax,
anyLast(t0.user_id) as user_id,
anyLast(t0.session_id) as session_id,
anyLast(t0.environment) as environment,
groupUniqArrayArray(t0.tags) as tags,
groupUniqArrayArray(t0.tags) as tags,
argMaxState(t0.version, if(t0.version <> '', t0.event_ts, toDateTime64(0, 3))) as version,
argMaxState(t0.release, if(t0.release <> '', t0.event_ts, toDateTime64(0, 3))) as release,
@@ -342,6 +351,8 @@ FROM traces_mt t0
GROUP BY project_id, id;
```
## Query AggregatingMergeTrees
We can query the properties of the resulting AggregatingMergeTree via (make sure to pick the right timeframe:
```sql
SELECT
@@ -386,3 +397,129 @@ SELECT
FROM traces_all_amt
LIMIT 100;
```
## Find discrepancies
We can identify discrepancies in the original and the new data using
```sql
-- Query to compare traces_all_amt with traces table and identify discrepancies
WITH amt_data AS (
-- First get the finalized values from the AMT table
SELECT project_id,
id,
start_time,
end_time,
finalizeAggregation(name_argmax) AS name_argmax_value,
name AS name_anylast_value,
finalizeAggregation(metadata_argmax) AS metadata_argmax_value,
metadata AS metadata_anylast_value,
finalizeAggregation(user_id_argmax) AS user_id_argmax_value,
user_id AS user_id_anylast_value,
finalizeAggregation(session_id_argmax) AS session_id_argmax_value,
session_id AS session_id_anylast_value,
finalizeAggregation(environment_argmax) AS environment_argmax_value,
environment AS environment_anylast_value,
tags,
finalizeAggregation(version) AS version_value,
finalizeAggregation(release) AS release_value,
finalizeAggregation(bookmarked) AS bookmarked_value,
finalizeAggregation(public) AS public_value,
finalizeAggregation(input_argmax) AS input_argmax_value,
input AS input_anylast_value,
finalizeAggregation(output_argmax) AS output_argmax_value,
output AS output_anylast_value,
created_at,
updated_at
FROM traces_all_amt
)
-- Main query to compare AMT with original traces table
SELECT t.project_id,
t.id,
-- Identify differences between tables
t.timestamp != a.start_time AS timestamp_diff,
t.name != a.name_anylast_value AS name_diff,
t.user_id != a.user_id_anylast_value AS user_id_diff,
t.session_id != a.session_id_anylast_value AS session_id_diff,
t.release != a.release_value AS release_diff,
t.version != a.version_value AS version_diff,
t.public != a.public_value AS public_diff,
t.bookmarked != a.bookmarked_value AS bookmarked_diff,
arraySort(t.tags) != arraySort(a.tags) AS tags_diff,
t.input != a.input_anylast_value AS input_diff,
t.output != a.output_anylast_value AS output_diff,
t.metadata != a.metadata_anylast_value AS metadata_diff,
a.name_argmax_value != a.name_anylast_value AS amt_name_diff,
a.user_id_argmax_value != a.user_id_anylast_value AS amt_user_id_diff,
a.session_id_argmax_value != a.session_id_anylast_value AS amt_session_id_diff,
a.environment_argmax_value != a.environment_anylast_value AS amt_environment_diff,
a.input_argmax_value != a.input_anylast_value AS amt_input_diff,
a.output_argmax_value != a.output_anylast_value AS amt_output_diff,
a.metadata_argmax_value != a.metadata_anylast_value AS amt_metadata_diff,
-- Include original values for comparison
t.timestamp AS traces_timestamp,
a.start_time AS amt_start_time,
t.name AS traces_name,
a.name_anylast_value AS amt_name_anylast,
a.name_argmax_value AS amt_name_argmax,
t.user_id AS traces_user_id,
a.user_id_anylast_value AS amt_user_id_anylast,
a.user_id_argmax_value AS amt_user_id_argmax,
t.session_id AS traces_session_id,
a.session_id_anylast_value AS amt_session_id_anylast,
a.session_id_argmax_value AS amt_session_id_argmax,
t.environment AS traces_environment,
a.environment_anylast_value AS amt_environment_anylast,
a.environment_argmax_value AS amt_environment_argmax,
t.metadata AS traces_metadata,
a.metadata_anylast_value AS amt_metadata_anylast,
a.metadata_argmax_value AS amt_metadata_argmax,
arraySort(t.tags) AS traces_tags,
arraySort(a.tags) AS amt_tags,
t.bookmarked AS traces_bookmarked,
a.bookmarked_value AS amt_bookmarked,
t.public AS traces_public,
a.public_value AS amt_public,
t.release AS traces_release,
a.release_value AS amt_release,
t.version AS traces_version,
a.version_value AS amt_version,
t.input AS traces_input,
a.input_anylast_value AS amt_input_anylast,
a.input_argmax_value AS amt_input_argmax,
t.output AS traces_output,
a.output_anylast_value AS amt_output_anylast,
a.output_argmax_value AS amt_output_argmax,
timestamp_diff +
name_diff +
user_id_diff +
session_id_diff +
release_diff +
version_diff +
public_diff +
bookmarked_diff +
tags_diff +
input_diff +
output_diff +
metadata_diff
AS total_diff
FROM traces t FINAL
LEFT JOIN amt_data a ON t.project_id = a.project_id AND t.id = a.id
where (t.timestamp >= '2025-07-01'
or a.start_time >= '2025-07-01')
and t.project_id in (
'some-project-id'
)
ORDER BY total_diff desc
LIMIT 1000;
```
+111 -11
View File
@@ -93,8 +93,8 @@ export class IngestionService {
constructor(
private redis: Redis | Cluster,
private prisma: PrismaClient,
private clickHouseWriter: ClickhouseWriter,
private clickhouseClient: ClickhouseClientType,
private clickHouseWriter: ClickhouseWriter, // eslint-disable-line no-unused-vars
private clickhouseClient: ClickhouseClientType, // eslint-disable-line no-unused-vars
) {
this.promptService = new PromptService(prisma, redis);
}
@@ -220,6 +220,14 @@ export class IngestionService {
clickhouseScoreRecord?.created_at ?? createdAtTimestamp.getTime();
this.clickHouseWriter.addToQueue(TableName.Scores, finalScoreRecord);
if (
env.LANGFUSE_EXPERIMENT_INSERT_INTO_AGGREGATING_MERGE_TREES === "true" &&
finalScoreRecord.trace_id
) {
const traceMtRecord = this.convertScoreToTraceMt(finalScoreRecord);
this.clickHouseWriter.addToQueue(TableName.TracesMt, traceMtRecord);
}
}
private async processTraceEventList(params: {
@@ -453,6 +461,14 @@ export class IngestionService {
TableName.Observations,
finalObservationRecord,
);
if (
env.LANGFUSE_EXPERIMENT_INSERT_INTO_AGGREGATING_MERGE_TREES === "true" &&
finalObservationRecord.trace_id
) {
const traceMtRecord = this.convertObservationToTraceMt(finalObservationRecord);
this.clickHouseWriter.addToQueue(TableName.TracesMt, traceMtRecord);
}
}
private async mergeScoreRecords(params: {
@@ -543,6 +559,90 @@ export class IngestionService {
};
}
private convertObservationToTraceMt(
observationRecord: ObservationRecordInsertType,
): TraceMtRecordInsertType {
return {
// Identifiers
project_id: observationRecord.project_id,
// Use trace_id as the id in traces_mt. Always set given the conditions around calling the function
id: observationRecord.trace_id || "",
start_time: observationRecord.start_time,
end_time: observationRecord.end_time || null,
name: "",
// Metadata properties
metadata: {},
user_id: '',
session_id: '',
environment: observationRecord.environment,
tags: [],
version: null,
release: null,
// UI properties - nullable to prevent absent values being interpreted as overwrites
bookmarked: null,
public: null,
// Aggregations - include this observation ID
observation_ids: [observationRecord.id],
score_ids: [],
// We can fill the cost details here, but we shouldn't trust them.
// Only used for verification to estimate how big the double-counting is.
cost_details: observationRecord.cost_details || {},
usage_details: observationRecord.usage_details || {},
// Input/Output
input: "",
output: "",
created_at: observationRecord.created_at,
updated_at: observationRecord.updated_at,
event_ts: observationRecord.event_ts,
};
}
private convertScoreToTraceMt(
scoreRecord: ScoreRecordInsertType,
): TraceMtRecordInsertType {
return {
// Identifiers
project_id: scoreRecord.project_id,
// Use trace_id as the id in traces_mt. Always set given the conditions around calling the function
id: scoreRecord.trace_id || "",
start_time: scoreRecord.timestamp,
end_time: null, // scores don't have end_time
name: "",
// Metadata properties
metadata: {},
user_id: '',
session_id: '',
environment: scoreRecord.environment,
tags: [], // scores don't have tags
version: null, // scores don't have version
release: null, // scores don't have release
// UI properties - nullable to prevent absent values being interpreted as overwrites
bookmarked: null,
public: null,
// Aggregations - include this score ID
observation_ids: [],
score_ids: [scoreRecord.id],
cost_details: {},
usage_details: {},
// Input/Output
input: "", // scores don't have input
output: "", // scores don't have output
created_at: scoreRecord.created_at,
updated_at: scoreRecord.updated_at,
event_ts: scoreRecord.event_ts,
};
}
private async mergeObservationRecords(params: {
projectId: string;
observationRecords: ObservationRecordInsertType[];
@@ -713,7 +813,7 @@ export class IngestionService {
> {
const providedUsageDetails = Object.fromEntries(
Object.entries(observationRecord.provided_usage_details).filter(
([k, v]) => v != null && v >= 0,
([k, v]) => v != null && v >= 0, // eslint-disable-line no-unused-vars
),
);
@@ -771,7 +871,7 @@ export class IngestionService {
const { provided_cost_details } = observationRecord;
const providedCostKeys = Object.entries(provided_cost_details ?? {})
.filter(([_, value]) => value != null)
.filter(([_, value]) => value != null) // eslint-disable-line no-unused-vars
.map(([key]) => key);
// If user has provided any cost point, do not calculate any other cost points
@@ -818,7 +918,7 @@ export class IngestionService {
finalTotalCost = finalCostDetails.total;
} else if (finalCostEntries.length > 0) {
finalTotalCost = finalCostEntries.reduce(
(acc, [_, cost]) => acc + cost,
(acc, [_, cost]) => acc + cost, // eslint-disable-line no-unused-vars
0,
);
@@ -882,7 +982,7 @@ export class IngestionService {
return result;
}
private async getClickhouseRecord(params: {
private async getClickhouseRecord(params: { // eslint-disable-line no-unused-vars
projectId: string;
entityId: string;
table: TableName.Traces;
@@ -891,7 +991,7 @@ export class IngestionService {
params: Record<string, unknown>;
};
}): Promise<TraceRecordInsertType | null>;
private async getClickhouseRecord(params: {
private async getClickhouseRecord(params: { // eslint-disable-line no-unused-vars, no-dupe-class-members
projectId: string;
entityId: string;
table: TableName.Scores;
@@ -900,7 +1000,7 @@ export class IngestionService {
params: Record<string, unknown>;
};
}): Promise<ScoreRecordInsertType | null>;
private async getClickhouseRecord(params: {
private async getClickhouseRecord(params: { // eslint-disable-line no-unused-vars, no-dupe-class-members
projectId: string;
entityId: string;
table: TableName.Observations;
@@ -909,7 +1009,7 @@ export class IngestionService {
params: Record<string, unknown>;
};
}): Promise<ObservationRecordInsertType | null>;
private async getClickhouseRecord(params: {
private async getClickhouseRecord(params: { // eslint-disable-line no-dupe-class-members
projectId: string;
entityId: string;
table: TableName;
@@ -1107,7 +1207,7 @@ export class IngestionService {
...("usageDetails" in obs.body
? (Object.fromEntries(
Object.entries(obs.body.usageDetails ?? {}).filter(
([_, val]) => val != null,
([_, val]) => val != null, // eslint-disable-line no-unused-vars
),
) as Record<string, number>)
: {}),
@@ -1128,7 +1228,7 @@ export class IngestionService {
...("costDetails" in obs.body
? (Object.fromEntries(
Object.entries(obs.body.costDetails ?? {}).filter(
([_, val]) => val != null,
([_, val]) => val != null, // eslint-disable-line no-unused-vars
),
) as Record<string, number>)
: {}),
+1 -1
View File
@@ -9,7 +9,7 @@ import { WorkerManager } from "../queues/workerManager";
import { prisma } from "@langfuse/shared/src/db";
import { BackgroundMigrationManager } from "../backgroundMigrations/backgroundMigrationManager";
export const onShutdown: NodeJS.SignalsListener = async (signal) => {
export const onShutdown: NodeJS.SignalsListener = async (signal) => { // eslint-disable-line no-undef
logger.info(`Received ${signal}, closing server...`);
setSigtermReceived();