Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b919562eed | ||
|
|
91c01e7363 | ||
|
|
48ec1bccae | ||
|
|
66aa2fd48e | ||
|
|
f822bf1049 | ||
|
|
96ec24aad8 | ||
|
|
068fbd0cc6 | ||
|
|
7c8b76d573 | ||
|
|
e4e93d64bb | ||
|
|
f5cf8693e9 | ||
|
|
5beef28214 | ||
|
|
d93712c6ab | ||
|
|
b1b0a966be | ||
|
|
b5edadb203 | ||
|
|
3090882590 | ||
|
|
787f5d8d10 | ||
|
|
5fa142eda4 | ||
|
|
d96865b019 | ||
|
|
8d808923a1 | ||
|
|
0f056c1c2d | ||
|
|
094a523795 | ||
|
|
c60e58a568 | ||
|
|
6147e9c518 | ||
|
|
f0408b208e | ||
|
|
55e0bdcac0 | ||
|
|
05e15c9f53 | ||
|
|
ce6249b266 | ||
|
|
83e91e5bf5 | ||
|
|
edf073cc18 | ||
|
|
65343a8e9b | ||
|
|
0978055bdc | ||
|
|
76aaf2a3d0 | ||
|
|
8ed61bf705 | ||
|
|
45436530b4 | ||
|
|
7a3f01fc0a | ||
|
|
468fc4d079 | ||
|
|
945d13dac0 | ||
|
|
3646eaa5a8 | ||
|
|
feb5af11b8 | ||
|
|
3711af5663 | ||
|
|
9ec798f393 | ||
|
|
f71fd43d91 | ||
|
|
0303596420 | ||
|
|
cfaccc46d3 | ||
|
|
907f764adc | ||
|
|
652e9e3fe5 | ||
|
|
48673e43d7 |
@@ -457,13 +457,13 @@ types:
|
||||
docs: Unit used by this model.
|
||||
type: optional<ModelUsageUnit>
|
||||
inputPrice:
|
||||
docs: Price (USD) per input unit
|
||||
docs: Deprecated. See 'prices' instead. Price (USD) per input unit
|
||||
type: optional<double>
|
||||
outputPrice:
|
||||
docs: Price (USD) per output unit
|
||||
docs: Deprecated. See 'prices' instead. Price (USD) per output unit
|
||||
type: optional<double>
|
||||
totalPrice:
|
||||
docs: Price (USD) per total unit. Cannot be set if input or output price is set.
|
||||
docs: Deprecated. See 'prices' instead. Price (USD) per total unit. Cannot be set if input or output price is set.
|
||||
type: optional<double>
|
||||
tokenizerId:
|
||||
docs: Optional. Tokenizer to be applied to observations which match to this model. See docs for more details.
|
||||
@@ -473,6 +473,13 @@ types:
|
||||
type: optional<unknown>
|
||||
isLangfuseManaged:
|
||||
type: boolean
|
||||
prices:
|
||||
docs: Price (USD) by usage type
|
||||
type: map<string, ModelPrice>
|
||||
|
||||
ModelPrice:
|
||||
properties:
|
||||
price: double
|
||||
|
||||
# Utilities
|
||||
ModelUsageUnit:
|
||||
|
||||
@@ -11,7 +11,7 @@ service:
|
||||
method: GET
|
||||
path: /projects
|
||||
response: Projects
|
||||
|
||||
|
||||
create:
|
||||
docs: Create a new project (requires organization-scoped API key)
|
||||
method: POST
|
||||
@@ -28,7 +28,7 @@ service:
|
||||
type: integer
|
||||
docs: Number of days to retain data. Must be 0 or at least 3 days. Requires data-retention entitlement for non-zero values. Optional.
|
||||
response: Project
|
||||
|
||||
|
||||
update:
|
||||
docs: Update a project by ID (requires organization-scoped API key).
|
||||
method: PUT
|
||||
@@ -47,7 +47,7 @@ service:
|
||||
type: integer
|
||||
docs: Number of days to retain data. Must be 0 or at least 3 days. Requires data-retention entitlement for non-zero values. Optional.
|
||||
response: Project
|
||||
|
||||
|
||||
delete:
|
||||
docs: Delete a project by ID (requires organization-scoped API key). Project deletion is processed asynchronously.
|
||||
method: DELETE
|
||||
@@ -57,7 +57,7 @@ service:
|
||||
response:
|
||||
status-code: 202
|
||||
type: ProjectDeletionResponse
|
||||
|
||||
|
||||
# API Key endpoints
|
||||
getApiKeys:
|
||||
docs: Get all API keys for a project (requires organization-scoped API key)
|
||||
@@ -104,14 +104,14 @@ types:
|
||||
type: map<string, unknown>
|
||||
docs: Metadata for the project
|
||||
retentionDays:
|
||||
type: integer
|
||||
type: optional<integer>
|
||||
docs: Number of days to retain data. Null or 0 means no retention. Omitted if no retention is configured.
|
||||
|
||||
ProjectDeletionResponse:
|
||||
properties:
|
||||
success: boolean
|
||||
message: string
|
||||
|
||||
|
||||
ApiKeyList:
|
||||
docs: List of API keys for a project
|
||||
properties:
|
||||
@@ -137,7 +137,7 @@ types:
|
||||
secretKey: string
|
||||
displaySecretKey: string
|
||||
note: optional<string>
|
||||
|
||||
|
||||
ApiKeyDeletionResponse:
|
||||
docs: Response for API key deletion
|
||||
properties:
|
||||
|
||||
+4
-2
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "langfuse",
|
||||
"version": "3.63.0",
|
||||
"version": "3.65.3",
|
||||
"author": "engineering@langfuse.com",
|
||||
"license": "MIT",
|
||||
"private": true,
|
||||
@@ -86,7 +86,9 @@
|
||||
"pnpm": {
|
||||
"overrides": {
|
||||
"nanoid": "^3.3.8",
|
||||
"katex": "^0.16.21"
|
||||
"katex": "^0.16.21",
|
||||
"tar-fs": "^2.1.2",
|
||||
"rollup@^4.0.0": "^4.22.4"
|
||||
},
|
||||
"patchedDependencies": {
|
||||
"next-auth@4.24.11": "patches/next-auth@4.24.11.patch"
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
DROP VIEW IF EXISTS analytics_traces ON CLUSTER default;
|
||||
@@ -0,0 +1,15 @@
|
||||
CREATE VIEW analytics_traces ON CLUSTER default AS
|
||||
SELECT
|
||||
project_id,
|
||||
toStartOfHour(timestamp) AS hour,
|
||||
uniq(id) AS countTraces,
|
||||
max(user_id IS NOT NULL) AS hasUsers,
|
||||
max(session_id IS NOT NULL) AS hasSessions,
|
||||
max(if(environment != 'default', 1, 0)) AS hasEnvironments,
|
||||
max(length(tags) > 0) AS hasTags
|
||||
FROM
|
||||
traces
|
||||
WHERE toStartOfHour(timestamp) <= toStartOfHour(subtractHours(now(), 1))
|
||||
GROUP BY
|
||||
project_id,
|
||||
hour;
|
||||
@@ -0,0 +1 @@
|
||||
DROP VIEW IF EXISTS analytics_observations ON CLUSTER default;
|
||||
@@ -0,0 +1,18 @@
|
||||
CREATE VIEW analytics_observations ON CLUSTER default AS
|
||||
SELECT
|
||||
project_id,
|
||||
type,
|
||||
toStartOfHour(start_time) AS hour,
|
||||
uniq(id) AS countObservations,
|
||||
max(level != 'DEFAULT') AS hasLevel,
|
||||
max(provided_model_name IS NOT NULL) AS hasProvidedModelName,
|
||||
max(length(provided_usage_details) > 0) AS hasProvidedUsageDetails,
|
||||
max(length(provided_cost_details) > 0) AS hasProvidedCostDetails,
|
||||
max(prompt_name IS NOT NULL) AS hasPromptName
|
||||
FROM
|
||||
observations
|
||||
WHERE toStartOfHour(start_time) <= toStartOfHour(subtractHours(now(), 1))
|
||||
GROUP BY
|
||||
project_id,
|
||||
type,
|
||||
hour;
|
||||
@@ -0,0 +1 @@
|
||||
DROP VIEW IF EXISTS analytics_scores ON CLUSTER default;
|
||||
@@ -0,0 +1,21 @@
|
||||
CREATE VIEW analytics_scores ON CLUSTER default AS
|
||||
SELECT
|
||||
project_id,
|
||||
toStartOfHour(timestamp) AS hour,
|
||||
uniq(id) AS countScores,
|
||||
max(source = 'ANNOTATION') AS hasAnnotation,
|
||||
max(source = 'API') AS hasApi,
|
||||
max(source = 'EVAL') AS hasEval,
|
||||
max(observation_id IS NOT NULL) AS hasObservationScore,
|
||||
max(session_id IS NOT NULL) AS hasSessionScore,
|
||||
max(dataset_run_id IS NOT NULL) AS hasDatasetRunScore,
|
||||
max(data_type = 'BOOLEAN') AS hasBoolScore,
|
||||
max(data_type = 'NUMERIC') AS hasNumericScore,
|
||||
max(data_type = 'CATEGORICAL') AS hasCategoricalScore,
|
||||
max(comment IS NOT NULL) AS hasComment
|
||||
FROM
|
||||
scores
|
||||
WHERE toStartOfHour(timestamp) <= toStartOfHour(subtractHours(now(), 1))
|
||||
GROUP BY
|
||||
project_id,
|
||||
hour;
|
||||
@@ -0,0 +1 @@
|
||||
DROP VIEW IF EXISTS analytics_traces;
|
||||
@@ -0,0 +1,15 @@
|
||||
CREATE VIEW analytics_traces AS
|
||||
SELECT
|
||||
project_id,
|
||||
toStartOfHour(timestamp) AS hour,
|
||||
uniq(id) AS countTraces,
|
||||
max(user_id IS NOT NULL) AS hasUsers,
|
||||
max(session_id IS NOT NULL) AS hasSessions,
|
||||
max(if(environment != 'default', 1, 0)) AS hasEnvironments,
|
||||
max(length(tags) > 0) AS hasTags
|
||||
FROM
|
||||
traces
|
||||
WHERE toStartOfHour(timestamp) <= toStartOfHour(subtractHours(now(), 1))
|
||||
GROUP BY
|
||||
project_id,
|
||||
hour;
|
||||
@@ -0,0 +1 @@
|
||||
DROP VIEW IF EXISTS analytics_observations;
|
||||
@@ -0,0 +1,18 @@
|
||||
CREATE VIEW analytics_observations AS
|
||||
SELECT
|
||||
project_id,
|
||||
type,
|
||||
toStartOfHour(start_time) AS hour,
|
||||
uniq(id) AS countObservations,
|
||||
max(level != 'DEFAULT') AS hasLevel,
|
||||
max(provided_model_name IS NOT NULL) AS hasProvidedModelName,
|
||||
max(length(provided_usage_details) > 0) AS hasProvidedUsageDetails,
|
||||
max(length(provided_cost_details) > 0) AS hasProvidedCostDetails,
|
||||
max(prompt_name IS NOT NULL) AS hasPromptName
|
||||
FROM
|
||||
observations
|
||||
WHERE toStartOfHour(start_time) <= toStartOfHour(subtractHours(now(), 1))
|
||||
GROUP BY
|
||||
project_id,
|
||||
type,
|
||||
hour;
|
||||
@@ -0,0 +1 @@
|
||||
DROP VIEW IF EXISTS analytics_scores;
|
||||
@@ -0,0 +1,21 @@
|
||||
CREATE VIEW analytics_scores AS
|
||||
SELECT
|
||||
project_id,
|
||||
toStartOfHour(timestamp) AS hour,
|
||||
uniq(id) AS countScores,
|
||||
max(source = 'ANNOTATION') AS hasAnnotation,
|
||||
max(source = 'API') AS hasApi,
|
||||
max(source = 'EVAL') AS hasEval,
|
||||
max(observation_id IS NOT NULL) AS hasObservationScore,
|
||||
max(session_id IS NOT NULL) AS hasSessionScore,
|
||||
max(dataset_run_id IS NOT NULL) AS hasDatasetRunScore,
|
||||
max(data_type = 'BOOLEAN') AS hasBoolScore,
|
||||
max(data_type = 'NUMERIC') AS hasNumericScore,
|
||||
max(data_type = 'CATEGORICAL') AS hasCategoricalScore,
|
||||
max(comment IS NOT NULL) AS hasComment
|
||||
FROM
|
||||
scores
|
||||
WHERE toStartOfHour(timestamp) <= toStartOfHour(subtractHours(now(), 1))
|
||||
GROUP BY
|
||||
project_id,
|
||||
hour;
|
||||
@@ -60,7 +60,7 @@
|
||||
"@aws-sdk/lib-storage": "^3.675.0",
|
||||
"@aws-sdk/s3-request-presigner": "^3.679.0",
|
||||
"@azure/storage-blob": "^12.26.0",
|
||||
"@clickhouse/client": "^1.4.0",
|
||||
"@clickhouse/client": "^1.11.1",
|
||||
"@google-cloud/storage": "^7.15.2",
|
||||
"@langchain/anthropic": "^0.3.12",
|
||||
"@langchain/aws": "^0.1.3",
|
||||
|
||||
@@ -228,8 +228,8 @@ export type BlobStorageIntegration = {
|
||||
type: BlobStorageIntegrationType;
|
||||
bucket_name: string;
|
||||
prefix: string;
|
||||
access_key_id: string;
|
||||
secret_access_key: string;
|
||||
access_key_id: string | null;
|
||||
secret_access_key: string | null;
|
||||
region: string;
|
||||
endpoint: string | null;
|
||||
force_path_style: boolean;
|
||||
|
||||
+3
@@ -0,0 +1,3 @@
|
||||
-- AlterTable
|
||||
ALTER TABLE "blob_storage_integrations" ALTER COLUMN "access_key_id" DROP NOT NULL,
|
||||
ALTER COLUMN "secret_access_key" DROP NOT NULL;
|
||||
@@ -951,8 +951,8 @@ model BlobStorageIntegration {
|
||||
type BlobStorageIntegrationType @map("type")
|
||||
bucketName String @map("bucket_name")
|
||||
prefix String @map("prefix")
|
||||
accessKeyId String @map("access_key_id")
|
||||
secretAccessKey String @map("secret_access_key")
|
||||
accessKeyId String? @map("access_key_id")
|
||||
secretAccessKey String? @map("secret_access_key")
|
||||
region String @map("region")
|
||||
endpoint String? @map("endpoint")
|
||||
forcePathStyle Boolean @map("force_path_style")
|
||||
|
||||
@@ -50,6 +50,12 @@ const EnvSchema = z.object({
|
||||
.enum(["trace", "debug", "info", "warn", "error", "fatal"])
|
||||
.optional(),
|
||||
LANGFUSE_LOG_FORMAT: z.enum(["text", "json"]).default("text"),
|
||||
LANGFUSE_LOG_PROPAGATED_HEADERS: z
|
||||
.string()
|
||||
.optional()
|
||||
.transform((s) =>
|
||||
s ? s.split(",").map((s) => s.toLowerCase().trim()) : [],
|
||||
),
|
||||
ENABLE_AWS_CLOUDWATCH_METRIC_PUBLISHING: z
|
||||
.enum(["true", "false"])
|
||||
.default("false"),
|
||||
|
||||
@@ -14,7 +14,6 @@ export const planLabels = {
|
||||
"cloud:pro": "Pro",
|
||||
"cloud:team": "Team",
|
||||
"cloud:enterprise": "Enterprise",
|
||||
"self-hosted:pro": "Pro (self-hosted)",
|
||||
"self-hosted:enterprise": "Enterprise (self-hosted)",
|
||||
} as const;
|
||||
|
||||
|
||||
@@ -0,0 +1,44 @@
|
||||
import * as opentelemetry from "@opentelemetry/api";
|
||||
import type { IncomingHttpHeaders } from "http";
|
||||
import { env } from "../env";
|
||||
|
||||
export type LangfuseContextProps = {
|
||||
headers?: IncomingHttpHeaders;
|
||||
userId?: string;
|
||||
projectId?: string;
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns a new context containing baggage entries composed from
|
||||
* the supplied props (headers, userId, projectId). Existing baggage
|
||||
* entries are preserved.
|
||||
*/
|
||||
export const contextWithLangfuseProps = (
|
||||
props: LangfuseContextProps,
|
||||
): opentelemetry.Context => {
|
||||
const ctx = opentelemetry.context.active();
|
||||
let baggage =
|
||||
opentelemetry.propagation.getBaggage(ctx) ??
|
||||
opentelemetry.propagation.createBaggage();
|
||||
|
||||
if (props.headers) {
|
||||
(env.LANGFUSE_LOG_PROPAGATED_HEADERS as string[]).forEach((name) => {
|
||||
const value = props.headers![name];
|
||||
if (!value) return;
|
||||
const strValue = Array.isArray(value) ? JSON.stringify(value) : value;
|
||||
baggage = baggage.setEntry(`langfuse.header.${name}`, {
|
||||
value: strValue,
|
||||
});
|
||||
});
|
||||
}
|
||||
if (props.userId) {
|
||||
baggage = baggage.setEntry("langfuse.user.id", { value: props.userId });
|
||||
}
|
||||
if (props.projectId) {
|
||||
baggage = baggage.setEntry("langfuse.project.id", {
|
||||
value: props.projectId,
|
||||
});
|
||||
}
|
||||
|
||||
return opentelemetry.propagation.setBaggage(ctx, baggage);
|
||||
};
|
||||
@@ -41,13 +41,14 @@ export * from "./redis/dataRetentionProcessingQueue";
|
||||
export * from "./redis/coreDataS3ExportQueue";
|
||||
export * from "./redis/meteringDataPostgresExportQueue";
|
||||
export * from "./redis/experimentCreateQueue";
|
||||
export * from "./redis/dlxRetryQueue";
|
||||
export * from "./redis/dlqRetryQueue";
|
||||
export * from "./auth/types";
|
||||
export * from "./queues";
|
||||
export * from "./orderByToPrisma";
|
||||
export * from "./filterToPrisma";
|
||||
export * from "./instrumentation";
|
||||
export * from "./logger";
|
||||
export * from "./headerPropagation";
|
||||
export * from "./queries";
|
||||
export * from "./repositories";
|
||||
export * from "./redis/evalExecutionQueue";
|
||||
|
||||
@@ -43,6 +43,14 @@ export async function instrumentAsync<T>(
|
||||
},
|
||||
activeContext,
|
||||
async (span) => {
|
||||
const baggage = opentelemetry.propagation.getBaggage(
|
||||
opentelemetry.context.active(),
|
||||
);
|
||||
if (baggage) {
|
||||
baggage
|
||||
.getAllEntries()
|
||||
.forEach(([k, v]) => span.setAttribute(k, v.value));
|
||||
}
|
||||
try {
|
||||
const result = await callback(span);
|
||||
span.end();
|
||||
@@ -77,6 +85,14 @@ export function instrumentSync<T>(
|
||||
},
|
||||
activeContext,
|
||||
(span) => {
|
||||
const baggage = opentelemetry.propagation.getBaggage(
|
||||
opentelemetry.context.active(),
|
||||
);
|
||||
if (baggage) {
|
||||
baggage
|
||||
.getAllEntries()
|
||||
.forEach(([k, v]) => span.setAttribute(k, v.value));
|
||||
}
|
||||
try {
|
||||
const result = callback(span);
|
||||
span.end();
|
||||
@@ -141,30 +157,6 @@ export const traceException = (
|
||||
});
|
||||
};
|
||||
|
||||
export const addUserToSpan = (
|
||||
attributes: {
|
||||
userId?: string;
|
||||
projectId?: string;
|
||||
email?: string;
|
||||
orgId?: string;
|
||||
plan?: string;
|
||||
},
|
||||
span?: opentelemetry.Span,
|
||||
) => {
|
||||
const activeSpan = span ?? getCurrentSpan();
|
||||
|
||||
if (!activeSpan) {
|
||||
return;
|
||||
}
|
||||
|
||||
attributes.userId && activeSpan.setAttribute("user.id", attributes.userId);
|
||||
attributes.email && activeSpan.setAttribute("user.email", attributes.email);
|
||||
attributes.projectId &&
|
||||
activeSpan.setAttribute("project.id", attributes.projectId);
|
||||
attributes.orgId && activeSpan.setAttribute("org.id", attributes.orgId);
|
||||
attributes.plan && activeSpan.setAttribute("org.plan", attributes.plan);
|
||||
};
|
||||
|
||||
export const getTracer = (name: string) => opentelemetry.trace.getTracer(name);
|
||||
|
||||
const cloudWatchClient = new CloudWatchClient();
|
||||
|
||||
@@ -319,7 +319,8 @@ export const anthropicModels = [
|
||||
|
||||
// WARNING: The first entry in the array is chosen as the default model to add LLM API keys
|
||||
export const vertexAIModels = [
|
||||
"gemini-2.5-pro-exp-03-25",
|
||||
"gemini-2.5-pro-preview-05-06",
|
||||
"gemini-2.5-flash-preview-05-20",
|
||||
"gemini-2.0-pro-exp-02-05",
|
||||
"gemini-2.0-flash",
|
||||
"gemini-2.0-flash-001",
|
||||
@@ -332,7 +333,8 @@ export const vertexAIModels = [
|
||||
|
||||
// WARNING: The first entry in the array is chosen as the default model to add LLM API keys. Make sure it supports top_p, max_tokens and temperature.
|
||||
export const googleAIStudioModels = [
|
||||
"gemini-2.5-pro-exp-03-25",
|
||||
"gemini-2.5-pro-preview-05-06",
|
||||
"gemini-2.5-flash-preview-05-20",
|
||||
"gemini-2.0-flash",
|
||||
"gemini-2.0-flash-lite-preview",
|
||||
"gemini-2.0-flash-lite-preview-02-05",
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { env } from "../env";
|
||||
import winston from "winston";
|
||||
import { getCurrentSpan } from "./instrumentation";
|
||||
import { propagation, context } from "@opentelemetry/api";
|
||||
|
||||
const tracingFormat = function () {
|
||||
return winston.format((info) => {
|
||||
@@ -13,6 +14,12 @@ const tracingFormat = function () {
|
||||
info["trace_id"] = traceId;
|
||||
info["span_id"] = spanId;
|
||||
}
|
||||
const baggage = propagation.getBaggage(context.active());
|
||||
if (baggage) {
|
||||
const headerObj: Record<string, string> = {};
|
||||
baggage.getAllEntries().forEach(([k, v]) => (headerObj[k] = v.value));
|
||||
if (Object.keys(headerObj).length) info = { ...headerObj, ...info };
|
||||
}
|
||||
return info;
|
||||
})();
|
||||
};
|
||||
|
||||
@@ -120,6 +120,7 @@ export const CreateEvalQueueEventSchema = DatasetRunItemUpsertEventSchema.and(
|
||||
z.object({
|
||||
timestamp: z.date(),
|
||||
configId: z.string(),
|
||||
exactTimestamp: z.date().optional(),
|
||||
}),
|
||||
),
|
||||
);
|
||||
|
||||
@@ -26,7 +26,7 @@ export class BatchActionQueue {
|
||||
defaultJobOptions: {
|
||||
removeOnComplete: true,
|
||||
removeOnFail: 10_000,
|
||||
attempts: 2,
|
||||
attempts: 10,
|
||||
backoff: {
|
||||
type: "exponential",
|
||||
delay: 5000,
|
||||
|
||||
@@ -20,7 +20,7 @@ import { DataRetentionProcessingQueue } from "./dataRetentionProcessingQueue";
|
||||
import { BatchActionQueue } from "./batchActionQueue";
|
||||
import { CreateEvalQueue } from "./createEvalQueue";
|
||||
import { ScoreDeleteQueue } from "./scoreDelete";
|
||||
import { DeadLetterRetryQueue } from "./dlxRetryQueue";
|
||||
import { DeadLetterRetryQueue } from "./dlqRetryQueue";
|
||||
|
||||
export function getQueue(queueName: QueueName): Queue | null {
|
||||
switch (queueName) {
|
||||
|
||||
@@ -48,12 +48,14 @@ export const checkTraceExists = async ({
|
||||
timestamp,
|
||||
filter,
|
||||
maxTimeStamp,
|
||||
exactTimestamp,
|
||||
}: {
|
||||
projectId: string;
|
||||
traceId: string;
|
||||
timestamp: Date;
|
||||
filter: FilterState;
|
||||
maxTimeStamp: Date | undefined;
|
||||
exactTimestamp?: Date;
|
||||
}): Promise<boolean> => {
|
||||
const { tracesFilter } = getProjectIdDefaultFilter(projectId, {
|
||||
tracesPrefix: "t",
|
||||
@@ -111,6 +113,7 @@ export const checkTraceExists = async ({
|
||||
AND timestamp >= {timestamp: DateTime64(3)} - ${TRACE_TO_OBSERVATIONS_INTERVAL}
|
||||
${maxTimeStamp ? `AND timestamp <= {maxTimeStamp: DateTime64(3)}` : ""}
|
||||
${!maxTimeStamp ? `AND timestamp <= {timestamp: DateTime64(3)} + INTERVAL 2 DAY` : ""}
|
||||
${exactTimestamp ? `AND timestamp = {exactTimestamp: DateTime64(3)}` : ""}
|
||||
GROUP BY t.id, t.project_id
|
||||
`;
|
||||
|
||||
@@ -126,6 +129,9 @@ export const checkTraceExists = async ({
|
||||
...(maxTimeStamp
|
||||
? { maxTimeStamp: convertDateToClickhouseDateTime(maxTimeStamp) }
|
||||
: {}),
|
||||
...(exactTimestamp
|
||||
? { exactTimestamp: convertDateToClickhouseDateTime(exactTimestamp) }
|
||||
: {}),
|
||||
},
|
||||
tags: {
|
||||
feature: "tracing",
|
||||
|
||||
@@ -460,7 +460,7 @@ class S3StorageService implements StorageService {
|
||||
return { signedUrl };
|
||||
} catch (err) {
|
||||
logger.error(`Failed to upload file to ${fileName}`, err);
|
||||
throw new Error("Failed to upload to S3 or generate signed URL");
|
||||
throw new Error(`Failed to upload to S3 or generate signed URL: ${err}`);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -157,7 +157,7 @@ export type TracesTableMetricsClickhouseReturnType = {
|
||||
};
|
||||
|
||||
export type FetchTracesTableProps = {
|
||||
select: "count" | "rows" | "metrics";
|
||||
select: "count" | "rows" | "metrics" | "identifiers";
|
||||
projectId: string;
|
||||
filter: FilterState;
|
||||
searchQuery?: string;
|
||||
@@ -169,84 +169,42 @@ export type FetchTracesTableProps = {
|
||||
tags?: Record<string, string>;
|
||||
};
|
||||
|
||||
export const getTracesTableCount = async (props: {
|
||||
projectId: string;
|
||||
filter: FilterState;
|
||||
searchQuery?: string;
|
||||
searchType: TracingSearchType[];
|
||||
orderBy?: OrderByState;
|
||||
limit?: number;
|
||||
page?: number;
|
||||
}) => {
|
||||
const countRows = await getTracesTableGeneric<{ count: string }>({
|
||||
select: "count",
|
||||
tags: { kind: "count" },
|
||||
...props,
|
||||
});
|
||||
|
||||
const converted = countRows.map((row) => ({
|
||||
count: Number(row.count),
|
||||
}));
|
||||
|
||||
return converted.length > 0 ? converted[0].count : 0;
|
||||
// Define return type mapping for better type safety
|
||||
type SelectReturnTypeMap = {
|
||||
count: { count: string };
|
||||
metrics: TracesTableMetricsClickhouseReturnType;
|
||||
rows: TracesTableReturnType;
|
||||
identifiers: { id: string; projectId: string; timestamp: string };
|
||||
};
|
||||
|
||||
export const getTracesTableMetrics = async (props: {
|
||||
projectId: string;
|
||||
filter: FilterState;
|
||||
searchQuery?: string;
|
||||
orderBy?: OrderByState;
|
||||
limit?: number;
|
||||
page?: number;
|
||||
clickhouseConfigs?: ClickHouseClientConfigOptions | undefined;
|
||||
}): Promise<Array<Omit<TracesMetricsUiReturnType, "scores">>> => {
|
||||
const countRows =
|
||||
await getTracesTableGeneric<TracesTableMetricsClickhouseReturnType>({
|
||||
select: "metrics",
|
||||
tags: { kind: "analytic" },
|
||||
...props,
|
||||
});
|
||||
// Function overloads for type-safe select-specific returns
|
||||
async function getTracesTableGeneric(
|
||||
// eslint-disable-next-line no-unused-vars
|
||||
props: FetchTracesTableProps & { select: "count" },
|
||||
): Promise<Array<SelectReturnTypeMap["count"]>>;
|
||||
|
||||
return countRows.map(convertToUITableMetrics);
|
||||
};
|
||||
async function getTracesTableGeneric(
|
||||
// eslint-disable-next-line no-unused-vars
|
||||
props: FetchTracesTableProps & { select: "metrics" },
|
||||
): Promise<Array<SelectReturnTypeMap["metrics"]>>;
|
||||
|
||||
export const getTracesTable = async (p: {
|
||||
projectId: string;
|
||||
filter: FilterState;
|
||||
searchQuery?: string;
|
||||
searchType?: TracingSearchType[];
|
||||
orderBy?: OrderByState;
|
||||
limit?: number;
|
||||
page?: number;
|
||||
clickhouseConfigs?: ClickHouseClientConfigOptions | undefined;
|
||||
}) => {
|
||||
const {
|
||||
projectId,
|
||||
filter,
|
||||
searchQuery,
|
||||
searchType,
|
||||
orderBy,
|
||||
limit,
|
||||
page,
|
||||
clickhouseConfigs,
|
||||
} = p;
|
||||
const rows = await getTracesTableGeneric<TracesTableReturnType>({
|
||||
select: "rows",
|
||||
tags: { kind: "list" },
|
||||
projectId,
|
||||
filter,
|
||||
searchQuery,
|
||||
searchType,
|
||||
orderBy,
|
||||
limit,
|
||||
page,
|
||||
clickhouseConfigs,
|
||||
});
|
||||
async function getTracesTableGeneric(
|
||||
// eslint-disable-next-line no-unused-vars
|
||||
props: FetchTracesTableProps & { select: "rows" },
|
||||
): Promise<Array<SelectReturnTypeMap["rows"]>>;
|
||||
|
||||
return rows.map(convertToUiTableRows);
|
||||
};
|
||||
async function getTracesTableGeneric(
|
||||
// eslint-disable-next-line no-unused-vars
|
||||
props: FetchTracesTableProps & { select: "identifiers" },
|
||||
): Promise<Array<SelectReturnTypeMap["identifiers"]>>;
|
||||
|
||||
const getTracesTableGeneric = async <T>(props: FetchTracesTableProps) => {
|
||||
// Implementation with union type for internal use
|
||||
async function getTracesTableGeneric(
|
||||
// eslint-disable-next-line no-unused-vars
|
||||
props: FetchTracesTableProps,
|
||||
): Promise<Array<SelectReturnTypeMap[keyof SelectReturnTypeMap]>>;
|
||||
|
||||
async function getTracesTableGeneric(props: FetchTracesTableProps) {
|
||||
const {
|
||||
select,
|
||||
projectId,
|
||||
@@ -297,6 +255,12 @@ const getTracesTableGeneric = async <T>(props: FetchTracesTableProps) => {
|
||||
t.session_id as session_id,
|
||||
t.public as public`;
|
||||
break;
|
||||
case "identifiers":
|
||||
sqlSelect = `
|
||||
t.id as id,
|
||||
t.project_id as projectId,
|
||||
t.timestamp as timestamp`;
|
||||
break;
|
||||
default:
|
||||
throw new Error(`Unknown select type: ${select}`);
|
||||
}
|
||||
@@ -471,7 +435,7 @@ const getTracesTableGeneric = async <T>(props: FetchTracesTableProps) => {
|
||||
)
|
||||
SELECT ${sqlSelect}
|
||||
-- FINAL is used for non default ordering and count.
|
||||
FROM traces t ${["metrics", "rows"].includes(select) && defaultOrder ? "" : "FINAL"}
|
||||
FROM traces t ${["metrics", "rows", "identifiers"].includes(select) && defaultOrder ? "" : "FINAL"}
|
||||
${select === "metrics" || requiresObservationsJoin ? `LEFT JOIN observations_stats os on os.project_id = t.project_id and os.trace_id = t.id` : ""}
|
||||
${select === "metrics" || requiresScoresJoin ? `LEFT JOIN scores_avg s on s.project_id = t.project_id and s.trace_id = t.id` : ""}
|
||||
WHERE t.project_id = {projectId: String}
|
||||
@@ -480,11 +444,13 @@ const getTracesTableGeneric = async <T>(props: FetchTracesTableProps) => {
|
||||
${chOrderBy}
|
||||
-- This is used for metrics and row queries. Count has only one result.
|
||||
-- This is only used for default ordering. Otherwise, we use final.
|
||||
${["metrics", "rows"].includes(select) && defaultOrder ? "LIMIT 1 BY id, project_id" : ""}
|
||||
${["metrics", "rows", "identifiers"].includes(select) && defaultOrder ? "LIMIT 1 BY id, project_id" : ""}
|
||||
${limit !== undefined && page !== undefined ? `LIMIT {limit: Int32} OFFSET {offset: Int32}` : ""}
|
||||
`;
|
||||
|
||||
const res = await queryClickhouse<T>({
|
||||
const res = await queryClickhouse<
|
||||
SelectReturnTypeMap[keyof SelectReturnTypeMap]
|
||||
>({
|
||||
query: query,
|
||||
params: {
|
||||
limit: limit,
|
||||
@@ -506,4 +472,120 @@ const getTracesTableGeneric = async <T>(props: FetchTracesTableProps) => {
|
||||
});
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
export const getTracesTableCount = async (props: {
|
||||
projectId: string;
|
||||
filter: FilterState;
|
||||
searchQuery?: string;
|
||||
searchType: TracingSearchType[];
|
||||
orderBy?: OrderByState;
|
||||
limit?: number;
|
||||
page?: number;
|
||||
}) => {
|
||||
const countRows = await getTracesTableGeneric({
|
||||
select: "count",
|
||||
tags: { kind: "count" },
|
||||
...props,
|
||||
});
|
||||
|
||||
const converted = countRows.map((row) => ({
|
||||
count: Number(row.count),
|
||||
}));
|
||||
|
||||
return converted.length > 0 ? converted[0].count : 0;
|
||||
};
|
||||
|
||||
export const getTracesTableMetrics = async (props: {
|
||||
projectId: string;
|
||||
filter: FilterState;
|
||||
searchQuery?: string;
|
||||
orderBy?: OrderByState;
|
||||
limit?: number;
|
||||
page?: number;
|
||||
clickhouseConfigs?: ClickHouseClientConfigOptions | undefined;
|
||||
}): Promise<Array<Omit<TracesMetricsUiReturnType, "scores">>> => {
|
||||
const countRows = await getTracesTableGeneric({
|
||||
select: "metrics",
|
||||
tags: { kind: "analytic" },
|
||||
...props,
|
||||
});
|
||||
|
||||
return countRows.map(convertToUITableMetrics);
|
||||
};
|
||||
|
||||
export const getTracesTable = async (p: {
|
||||
projectId: string;
|
||||
filter: FilterState;
|
||||
searchQuery?: string;
|
||||
searchType?: TracingSearchType[];
|
||||
orderBy?: OrderByState;
|
||||
limit?: number;
|
||||
page?: number;
|
||||
clickhouseConfigs?: ClickHouseClientConfigOptions | undefined;
|
||||
}) => {
|
||||
const {
|
||||
projectId,
|
||||
filter,
|
||||
searchQuery,
|
||||
searchType,
|
||||
orderBy,
|
||||
limit,
|
||||
page,
|
||||
clickhouseConfigs,
|
||||
} = p;
|
||||
const rows = await getTracesTableGeneric({
|
||||
select: "rows",
|
||||
tags: { kind: "list" },
|
||||
projectId,
|
||||
filter,
|
||||
searchQuery,
|
||||
searchType,
|
||||
orderBy,
|
||||
limit,
|
||||
page,
|
||||
clickhouseConfigs,
|
||||
});
|
||||
|
||||
return rows.map(convertToUiTableRows);
|
||||
};
|
||||
|
||||
export const getTraceIdentifiers = async (props: {
|
||||
projectId: string;
|
||||
filter: FilterState;
|
||||
searchQuery?: string;
|
||||
searchType?: TracingSearchType[];
|
||||
orderBy?: OrderByState;
|
||||
limit?: number;
|
||||
page?: number;
|
||||
clickhouseConfigs?: ClickHouseClientConfigOptions | undefined;
|
||||
}) => {
|
||||
const {
|
||||
projectId,
|
||||
filter,
|
||||
searchQuery,
|
||||
searchType,
|
||||
orderBy,
|
||||
limit,
|
||||
page,
|
||||
clickhouseConfigs,
|
||||
} = props;
|
||||
const identifiers = await getTracesTableGeneric({
|
||||
select: "identifiers",
|
||||
tags: { kind: "list" },
|
||||
projectId,
|
||||
filter,
|
||||
searchQuery,
|
||||
searchType,
|
||||
orderBy,
|
||||
limit,
|
||||
page,
|
||||
clickhouseConfigs,
|
||||
});
|
||||
|
||||
return identifiers.map((row) => ({
|
||||
id: row.id,
|
||||
projectId: row.projectId,
|
||||
timestamp: parseClickhouseUTCDateTimeFormat(row.timestamp),
|
||||
}));
|
||||
};
|
||||
|
||||
@@ -75,6 +75,7 @@ export const tableNames = [
|
||||
"prompts",
|
||||
"users",
|
||||
"job_configurations",
|
||||
"job_executions",
|
||||
"dataset_items",
|
||||
] as const;
|
||||
|
||||
|
||||
@@ -35,4 +35,5 @@ export type TableName =
|
||||
| "dashboard"
|
||||
| "widgets"
|
||||
| "users"
|
||||
| "eval_configs";
|
||||
| "eval_configs"
|
||||
| "job_executions";
|
||||
|
||||
Generated
+150
-88
@@ -7,6 +7,8 @@ settings:
|
||||
overrides:
|
||||
nanoid: ^3.3.8
|
||||
katex: ^0.16.21
|
||||
tar-fs: ^2.1.2
|
||||
rollup@^4.0.0: ^4.22.4
|
||||
|
||||
patchedDependencies:
|
||||
next-auth@4.24.11:
|
||||
@@ -58,7 +60,7 @@ importers:
|
||||
version: 14.2.26(@babel/core@7.24.3)(@opentelemetry/api@1.9.0)(@playwright/test@1.47.2)(babel-plugin-macros@3.1.0)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)
|
||||
next-auth:
|
||||
specifier: ^4.24.11
|
||||
version: 4.24.11(patch_hash=cczsf6i6qe2m2htirvgxjtoclu)(next@14.2.26(@opentelemetry/api@1.9.0)(@playwright/test@1.47.2)(react-dom@18.2.0(react@18.2.0))(react@18.2.0))(nodemailer@6.9.15)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)
|
||||
version: 4.24.11(patch_hash=cczsf6i6qe2m2htirvgxjtoclu)(next@14.2.26(@babel/core@7.24.3)(@opentelemetry/api@1.9.0)(@playwright/test@1.47.2)(babel-plugin-macros@3.1.0)(react-dom@18.2.0(react@18.2.0))(react@18.2.0))(nodemailer@6.9.15)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)
|
||||
zod:
|
||||
specifier: ^3.24.4
|
||||
version: 3.24.4
|
||||
@@ -150,8 +152,8 @@ importers:
|
||||
specifier: ^12.26.0
|
||||
version: 12.26.0
|
||||
'@clickhouse/client':
|
||||
specifier: ^1.4.0
|
||||
version: 1.4.0
|
||||
specifier: ^1.11.1
|
||||
version: 1.11.1
|
||||
'@google-cloud/storage':
|
||||
specifier: ^7.15.2
|
||||
version: 7.15.2
|
||||
@@ -232,7 +234,7 @@ importers:
|
||||
version: 4.0.2
|
||||
next-auth:
|
||||
specifier: ^4.24.11
|
||||
version: 4.24.11(patch_hash=cczsf6i6qe2m2htirvgxjtoclu)(next@14.2.26(@opentelemetry/api@1.9.0)(@playwright/test@1.47.2)(react-dom@18.2.0(react@18.2.0))(react@18.2.0))(nodemailer@6.9.15)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)
|
||||
version: 4.24.11(patch_hash=cczsf6i6qe2m2htirvgxjtoclu)(next@14.2.26(@babel/core@7.24.3)(@opentelemetry/api@1.9.0)(@playwright/test@1.47.2)(babel-plugin-macros@3.1.0)(react-dom@18.2.0(react@18.2.0))(react@18.2.0))(nodemailer@6.9.15)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)
|
||||
nodemailer:
|
||||
specifier: ^6.9.15
|
||||
version: 6.9.15
|
||||
@@ -635,7 +637,7 @@ importers:
|
||||
version: 14.2.26(@babel/core@7.24.3)(@opentelemetry/api@1.9.0)(@playwright/test@1.47.2)(babel-plugin-macros@3.1.0)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)
|
||||
next-auth:
|
||||
specifier: ^4.24.11
|
||||
version: 4.24.11(patch_hash=cczsf6i6qe2m2htirvgxjtoclu)(next@14.2.26(@opentelemetry/api@1.9.0)(@playwright/test@1.47.2)(react-dom@18.2.0(react@18.2.0))(react@18.2.0))(nodemailer@6.9.15)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)
|
||||
version: 4.24.11(patch_hash=cczsf6i6qe2m2htirvgxjtoclu)(next@14.2.26(@babel/core@7.24.3)(@opentelemetry/api@1.9.0)(@playwright/test@1.47.2)(babel-plugin-macros@3.1.0)(react-dom@18.2.0(react@18.2.0))(react@18.2.0))(nodemailer@6.9.15)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)
|
||||
next-query-params:
|
||||
specifier: ^5.0.1
|
||||
version: 5.0.1(next@14.2.26(@babel/core@7.24.3)(@opentelemetry/api@1.9.0)(@playwright/test@1.47.2)(babel-plugin-macros@3.1.0)(react-dom@18.2.0(react@18.2.0))(react@18.2.0))(react@18.2.0)(use-query-params@2.2.1(react-dom@18.2.0(react@18.2.0))(react@18.2.0))
|
||||
@@ -865,9 +867,6 @@ importers:
|
||||
'@appsignal/opentelemetry-instrumentation-bullmq':
|
||||
specifier: ^0.7.3
|
||||
version: 0.7.3(bullmq@5.34.10)
|
||||
'@clickhouse/client':
|
||||
specifier: ^1.4.0
|
||||
version: 1.4.0
|
||||
'@langfuse/shared':
|
||||
specifier: workspace:*
|
||||
version: link:../packages/shared
|
||||
@@ -1736,11 +1735,11 @@ packages:
|
||||
'@chevrotain/utils@11.0.3':
|
||||
resolution: {integrity: sha512-YslZMgtJUyuMbZ+aKvfF3x1f5liK4mWNxghFRv7jqRR9C3R3fAOGTTKvxXDa2Y1s9zSbcpuO0cAxDYsc9SrXoQ==}
|
||||
|
||||
'@clickhouse/client-common@1.4.0':
|
||||
resolution: {integrity: sha512-kglG8YyWnR1K24RckUf5ZdNTN0U0s+a1j/bpCO4ZjzjO87ICgWlXFVD22pZqSACW7/2IIi1IkzbwtxKI2s/MOw==}
|
||||
'@clickhouse/client-common@1.11.1':
|
||||
resolution: {integrity: sha512-bme0le2yhDSAh13d2fxhSW5ZrNoVqZ3LTyac8jK6hNH0qkksXnjYkLS6KQalPU6NMpffxHmpI4+/Gi2MnX0NCA==}
|
||||
|
||||
'@clickhouse/client@1.4.0':
|
||||
resolution: {integrity: sha512-O4mbFPM/wQtFck01ghYI2mnNHv9jSFEiQBsTCH4t6MKeGHNAPkJGaFGv+KycLTv6zjnQNjiUGdXDMVRema5SyA==}
|
||||
'@clickhouse/client@1.11.1':
|
||||
resolution: {integrity: sha512-u9h++h72SmWystijNqfNvMkfA+5+Y1LNfmLL/odCL3VgI3oyAPP9ubSw/Yrt2zRZkLKehMMD1kuOej0QHbSoBA==}
|
||||
engines: {node: '>=16'}
|
||||
|
||||
'@codemirror/autocomplete@6.17.0':
|
||||
@@ -4352,7 +4351,7 @@ packages:
|
||||
resolution: {integrity: sha512-+tNWdlWKbpB3WgBN7ijjYkq9X5uhjmcvyjEght4NmH5fAU++zfQzAJ6wumLS+dNcvwEZhKx2Z+skY8m7v0wGSA==}
|
||||
engines: {node: '>=16.0.0 || 14 >= 14.17'}
|
||||
peerDependencies:
|
||||
rollup: ^2.68.0||^3.0.0||^4.0.0
|
||||
rollup: ^4.22.4
|
||||
peerDependenciesMeta:
|
||||
rollup:
|
||||
optional: true
|
||||
@@ -4361,73 +4360,108 @@ packages:
|
||||
resolution: {integrity: sha512-USm05zrsFxYLPdWWq+K3STlWiT/3ELn3RcV5hJMghpeAIhxfsUIg6mt12CBJBInWMV4VneoV7SfGv8xIwo2qNQ==}
|
||||
engines: {node: '>=14.0.0'}
|
||||
peerDependencies:
|
||||
rollup: ^1.20.0||^2.0.0||^3.0.0||^4.0.0
|
||||
rollup: ^4.22.4
|
||||
peerDependenciesMeta:
|
||||
rollup:
|
||||
optional: true
|
||||
|
||||
'@rollup/rollup-android-arm-eabi@4.13.0':
|
||||
resolution: {integrity: sha512-5ZYPOuaAqEH/W3gYsRkxQATBW3Ii1MfaT4EQstTnLKViLi2gLSQmlmtTpGucNP3sXEpOiI5tdGhjdE111ekyEg==}
|
||||
'@rollup/rollup-android-arm-eabi@4.41.1':
|
||||
resolution: {integrity: sha512-NELNvyEWZ6R9QMkiytB4/L4zSEaBC03KIXEghptLGLZWJ6VPrL63ooZQCOnlx36aQPGhzuOMwDerC1Eb2VmrLw==}
|
||||
cpu: [arm]
|
||||
os: [android]
|
||||
|
||||
'@rollup/rollup-android-arm64@4.13.0':
|
||||
resolution: {integrity: sha512-BSbaCmn8ZadK3UAQdlauSvtaJjhlDEjS5hEVVIN3A4bbl3X+otyf/kOJV08bYiRxfejP3DXFzO2jz3G20107+Q==}
|
||||
'@rollup/rollup-android-arm64@4.41.1':
|
||||
resolution: {integrity: sha512-DXdQe1BJ6TK47ukAoZLehRHhfKnKg9BjnQYUu9gzhI8Mwa1d2fzxA1aw2JixHVl403bwp1+/o/NhhHtxWJBgEA==}
|
||||
cpu: [arm64]
|
||||
os: [android]
|
||||
|
||||
'@rollup/rollup-darwin-arm64@4.13.0':
|
||||
resolution: {integrity: sha512-Ovf2evVaP6sW5Ut0GHyUSOqA6tVKfrTHddtmxGQc1CTQa1Cw3/KMCDEEICZBbyppcwnhMwcDce9ZRxdWRpVd6g==}
|
||||
'@rollup/rollup-darwin-arm64@4.41.1':
|
||||
resolution: {integrity: sha512-5afxvwszzdulsU2w8JKWwY8/sJOLPzf0e1bFuvcW5h9zsEg+RQAojdW0ux2zyYAz7R8HvvzKCjLNJhVq965U7w==}
|
||||
cpu: [arm64]
|
||||
os: [darwin]
|
||||
|
||||
'@rollup/rollup-darwin-x64@4.13.0':
|
||||
resolution: {integrity: sha512-U+Jcxm89UTK592vZ2J9st9ajRv/hrwHdnvyuJpa5A2ngGSVHypigidkQJP+YiGL6JODiUeMzkqQzbCG3At81Gg==}
|
||||
'@rollup/rollup-darwin-x64@4.41.1':
|
||||
resolution: {integrity: sha512-egpJACny8QOdHNNMZKf8xY0Is6gIMz+tuqXlusxquWu3F833DcMwmGM7WlvCO9sB3OsPjdC4U0wHw5FabzCGZg==}
|
||||
cpu: [x64]
|
||||
os: [darwin]
|
||||
|
||||
'@rollup/rollup-linux-arm-gnueabihf@4.13.0':
|
||||
resolution: {integrity: sha512-8wZidaUJUTIR5T4vRS22VkSMOVooG0F4N+JSwQXWSRiC6yfEsFMLTYRFHvby5mFFuExHa/yAp9juSphQQJAijQ==}
|
||||
'@rollup/rollup-freebsd-arm64@4.41.1':
|
||||
resolution: {integrity: sha512-DBVMZH5vbjgRk3r0OzgjS38z+atlupJ7xfKIDJdZZL6sM6wjfDNo64aowcLPKIx7LMQi8vybB56uh1Ftck/Atg==}
|
||||
cpu: [arm64]
|
||||
os: [freebsd]
|
||||
|
||||
'@rollup/rollup-freebsd-x64@4.41.1':
|
||||
resolution: {integrity: sha512-3FkydeohozEskBxNWEIbPfOE0aqQgB6ttTkJ159uWOFn42VLyfAiyD9UK5mhu+ItWzft60DycIN1Xdgiy8o/SA==}
|
||||
cpu: [x64]
|
||||
os: [freebsd]
|
||||
|
||||
'@rollup/rollup-linux-arm-gnueabihf@4.41.1':
|
||||
resolution: {integrity: sha512-wC53ZNDgt0pqx5xCAgNunkTzFE8GTgdZ9EwYGVcg+jEjJdZGtq9xPjDnFgfFozQI/Xm1mh+D9YlYtl+ueswNEg==}
|
||||
cpu: [arm]
|
||||
os: [linux]
|
||||
|
||||
'@rollup/rollup-linux-arm64-gnu@4.13.0':
|
||||
resolution: {integrity: sha512-Iu0Kno1vrD7zHQDxOmvweqLkAzjxEVqNhUIXBsZ8hu8Oak7/5VTPrxOEZXYC1nmrBVJp0ZcL2E7lSuuOVaE3+w==}
|
||||
'@rollup/rollup-linux-arm-musleabihf@4.41.1':
|
||||
resolution: {integrity: sha512-jwKCca1gbZkZLhLRtsrka5N8sFAaxrGz/7wRJ8Wwvq3jug7toO21vWlViihG85ei7uJTpzbXZRcORotE+xyrLA==}
|
||||
cpu: [arm]
|
||||
os: [linux]
|
||||
|
||||
'@rollup/rollup-linux-arm64-gnu@4.41.1':
|
||||
resolution: {integrity: sha512-g0UBcNknsmmNQ8V2d/zD2P7WWfJKU0F1nu0k5pW4rvdb+BIqMm8ToluW/eeRmxCared5dD76lS04uL4UaNgpNA==}
|
||||
cpu: [arm64]
|
||||
os: [linux]
|
||||
|
||||
'@rollup/rollup-linux-arm64-musl@4.13.0':
|
||||
resolution: {integrity: sha512-C31QrW47llgVyrRjIwiOwsHFcaIwmkKi3PCroQY5aVq4H0A5v/vVVAtFsI1nfBngtoRpeREvZOkIhmRwUKkAdw==}
|
||||
'@rollup/rollup-linux-arm64-musl@4.41.1':
|
||||
resolution: {integrity: sha512-XZpeGB5TKEZWzIrj7sXr+BEaSgo/ma/kCgrZgL0oo5qdB1JlTzIYQKel/RmhT6vMAvOdM2teYlAaOGJpJ9lahg==}
|
||||
cpu: [arm64]
|
||||
os: [linux]
|
||||
|
||||
'@rollup/rollup-linux-riscv64-gnu@4.13.0':
|
||||
resolution: {integrity: sha512-Oq90dtMHvthFOPMl7pt7KmxzX7E71AfyIhh+cPhLY9oko97Zf2C9tt/XJD4RgxhaGeAraAXDtqxvKE1y/j35lA==}
|
||||
'@rollup/rollup-linux-loongarch64-gnu@4.41.1':
|
||||
resolution: {integrity: sha512-bkCfDJ4qzWfFRCNt5RVV4DOw6KEgFTUZi2r2RuYhGWC8WhCA8lCAJhDeAmrM/fdiAH54m0mA0Vk2FGRPyzI+tw==}
|
||||
cpu: [loong64]
|
||||
os: [linux]
|
||||
|
||||
'@rollup/rollup-linux-powerpc64le-gnu@4.41.1':
|
||||
resolution: {integrity: sha512-3mr3Xm+gvMX+/8EKogIZSIEF0WUu0HL9di+YWlJpO8CQBnoLAEL/roTCxuLncEdgcfJcvA4UMOf+2dnjl4Ut1A==}
|
||||
cpu: [ppc64]
|
||||
os: [linux]
|
||||
|
||||
'@rollup/rollup-linux-riscv64-gnu@4.41.1':
|
||||
resolution: {integrity: sha512-3rwCIh6MQ1LGrvKJitQjZFuQnT2wxfU+ivhNBzmxXTXPllewOF7JR1s2vMX/tWtUYFgphygxjqMl76q4aMotGw==}
|
||||
cpu: [riscv64]
|
||||
os: [linux]
|
||||
|
||||
'@rollup/rollup-linux-x64-gnu@4.13.0':
|
||||
resolution: {integrity: sha512-yUD/8wMffnTKuiIsl6xU+4IA8UNhQ/f1sAnQebmE/lyQ8abjsVyDkyRkWop0kdMhKMprpNIhPmYlCxgHrPoXoA==}
|
||||
'@rollup/rollup-linux-riscv64-musl@4.41.1':
|
||||
resolution: {integrity: sha512-LdIUOb3gvfmpkgFZuccNa2uYiqtgZAz3PTzjuM5bH3nvuy9ty6RGc/Q0+HDFrHrizJGVpjnTZ1yS5TNNjFlklw==}
|
||||
cpu: [riscv64]
|
||||
os: [linux]
|
||||
|
||||
'@rollup/rollup-linux-s390x-gnu@4.41.1':
|
||||
resolution: {integrity: sha512-oIE6M8WC9ma6xYqjvPhzZYk6NbobIURvP/lEbh7FWplcMO6gn7MM2yHKA1eC/GvYwzNKK/1LYgqzdkZ8YFxR8g==}
|
||||
cpu: [s390x]
|
||||
os: [linux]
|
||||
|
||||
'@rollup/rollup-linux-x64-gnu@4.41.1':
|
||||
resolution: {integrity: sha512-cWBOvayNvA+SyeQMp79BHPK8ws6sHSsYnK5zDcsC3Hsxr1dgTABKjMnMslPq1DvZIp6uO7kIWhiGwaTdR4Og9A==}
|
||||
cpu: [x64]
|
||||
os: [linux]
|
||||
|
||||
'@rollup/rollup-linux-x64-musl@4.13.0':
|
||||
resolution: {integrity: sha512-9RyNqoFNdF0vu/qqX63fKotBh43fJQeYC98hCaf89DYQpv+xu0D8QFSOS0biA7cGuqJFOc1bJ+m2rhhsKcw1hw==}
|
||||
'@rollup/rollup-linux-x64-musl@4.41.1':
|
||||
resolution: {integrity: sha512-y5CbN44M+pUCdGDlZFzGGBSKCA4A/J2ZH4edTYSSxFg7ce1Xt3GtydbVKWLlzL+INfFIZAEg1ZV6hh9+QQf9YQ==}
|
||||
cpu: [x64]
|
||||
os: [linux]
|
||||
|
||||
'@rollup/rollup-win32-arm64-msvc@4.13.0':
|
||||
resolution: {integrity: sha512-46ue8ymtm/5PUU6pCvjlic0z82qWkxv54GTJZgHrQUuZnVH+tvvSP0LsozIDsCBFO4VjJ13N68wqrKSeScUKdA==}
|
||||
'@rollup/rollup-win32-arm64-msvc@4.41.1':
|
||||
resolution: {integrity: sha512-lZkCxIrjlJlMt1dLO/FbpZbzt6J/A8p4DnqzSa4PWqPEUUUnzXLeki/iyPLfV0BmHItlYgHUqJe+3KiyydmiNQ==}
|
||||
cpu: [arm64]
|
||||
os: [win32]
|
||||
|
||||
'@rollup/rollup-win32-ia32-msvc@4.13.0':
|
||||
resolution: {integrity: sha512-P5/MqLdLSlqxbeuJ3YDeX37srC8mCflSyTrUsgbU1c/U9j6l2g2GiIdYaGD9QjdMQPMSgYm7hgg0551wHyIluw==}
|
||||
'@rollup/rollup-win32-ia32-msvc@4.41.1':
|
||||
resolution: {integrity: sha512-+psFT9+pIh2iuGsxFYYa/LhS5MFKmuivRsx9iPJWNSGbh2XVEjk90fmpUEjCnILPEPJnikAU6SFDiEUyOv90Pg==}
|
||||
cpu: [ia32]
|
||||
os: [win32]
|
||||
|
||||
'@rollup/rollup-win32-x64-msvc@4.13.0':
|
||||
resolution: {integrity: sha512-UKXUQNbO3DOhzLRwHSpa0HnhhCgNODvfoPWv2FCXme8N/ANFfhIPMGuOT+QuKd16+B5yxZ0HdpNlqPvTMS1qfw==}
|
||||
'@rollup/rollup-win32-x64-msvc@4.41.1':
|
||||
resolution: {integrity: sha512-Wq2zpapRYLfi4aKxf2Xff0tN+7slj2d4R87WEzqw7ZLsVvO5zwYCIuEGSZYiK41+GlwUo1HiR+GdkLEJnCKTCw==}
|
||||
cpu: [x64]
|
||||
os: [win32]
|
||||
|
||||
@@ -10684,8 +10718,8 @@ packages:
|
||||
engines: {node: '>=14.18.0', npm: '>=8.0.0'}
|
||||
hasBin: true
|
||||
|
||||
rollup@4.13.0:
|
||||
resolution: {integrity: sha512-3YegKemjoQnYKmsBlOHfMLVPPA5xLkQ8MHLLSw/fBrFaVkEayL51DilPpNNLq1exr98F2B1TzrV0FUlN3gWRPg==}
|
||||
rollup@4.41.1:
|
||||
resolution: {integrity: sha512-cPmwD3FnFv8rKMBc1MxWCwVQFxwf1JEmSX3iQXrRVVG15zerAIXRjMFVWnd5Q5QvgKF7Aj+5ykXFhUl+QGnyOw==}
|
||||
engines: {node: '>=18.0.0', npm: '>=8.0.0'}
|
||||
hasBin: true
|
||||
|
||||
@@ -11215,8 +11249,8 @@ packages:
|
||||
resolution: {integrity: sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==}
|
||||
engines: {node: '>=6'}
|
||||
|
||||
tar-fs@2.1.1:
|
||||
resolution: {integrity: sha512-V0r2Y9scmbDRLCNex/+hYzvp/zyYjvFbHPNgVTKfQvVrb6guiE/fxP+XblDNR011utopbkex2nM4dHNV6GDsng==}
|
||||
tar-fs@2.1.3:
|
||||
resolution: {integrity: sha512-090nwYJDmlhwFwEW3QQl+vaNnxsO2yVsd45eTKRBzSzu+hlb1w2K9inVq5b0ngXuLVqQ4ApvsUHHnu/zQNkWAg==}
|
||||
|
||||
tar-stream@2.2.0:
|
||||
resolution: {integrity: sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==}
|
||||
@@ -13703,11 +13737,11 @@ snapshots:
|
||||
|
||||
'@chevrotain/utils@11.0.3': {}
|
||||
|
||||
'@clickhouse/client-common@1.4.0': {}
|
||||
'@clickhouse/client-common@1.11.1': {}
|
||||
|
||||
'@clickhouse/client@1.4.0':
|
||||
'@clickhouse/client@1.11.1':
|
||||
dependencies:
|
||||
'@clickhouse/client-common': 1.4.0
|
||||
'@clickhouse/client-common': 1.11.1
|
||||
|
||||
'@codemirror/autocomplete@6.17.0(@codemirror/language@6.10.8)(@codemirror/state@6.5.1)(@codemirror/view@6.36.2)(@lezer/common@1.2.3)':
|
||||
dependencies:
|
||||
@@ -14703,7 +14737,7 @@ snapshots:
|
||||
'@aws-sdk/client-bedrock-agent-runtime': 3.668.0
|
||||
'@aws-sdk/client-bedrock-runtime': 3.668.0
|
||||
'@aws-sdk/client-kendra': 3.668.0
|
||||
'@aws-sdk/credential-provider-node': 3.675.0(@aws-sdk/client-sso-oidc@3.675.0(@aws-sdk/client-sts@3.675.0))(@aws-sdk/client-sts@3.675.0)
|
||||
'@aws-sdk/credential-provider-node': 3.675.0(@aws-sdk/client-sso-oidc@3.668.0(@aws-sdk/client-sts@3.668.0))(@aws-sdk/client-sts@3.668.0)
|
||||
'@langchain/core': 0.3.18(openai@4.82.0(zod@3.24.4))
|
||||
zod: 3.24.4
|
||||
zod-to-json-schema: 3.23.5(zod@3.24.4)
|
||||
@@ -15081,7 +15115,7 @@ snapshots:
|
||||
'@next-auth/prisma-adapter@1.0.7(@prisma/client@6.3.0(prisma@6.3.0(typescript@5.4.5))(typescript@5.4.5))(next-auth@4.24.11(patch_hash=cczsf6i6qe2m2htirvgxjtoclu)(next@14.2.26(@babel/core@7.24.3)(@opentelemetry/api@1.9.0)(@playwright/test@1.47.2)(babel-plugin-macros@3.1.0)(react-dom@18.2.0(react@18.2.0))(react@18.2.0))(nodemailer@6.9.15)(react-dom@18.2.0(react@18.2.0))(react@18.2.0))':
|
||||
dependencies:
|
||||
'@prisma/client': 6.3.0(prisma@6.3.0(typescript@5.4.5))(typescript@5.4.5)
|
||||
next-auth: 4.24.11(patch_hash=cczsf6i6qe2m2htirvgxjtoclu)(next@14.2.26(@opentelemetry/api@1.9.0)(@playwright/test@1.47.2)(react-dom@18.2.0(react@18.2.0))(react@18.2.0))(nodemailer@6.9.15)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)
|
||||
next-auth: 4.24.11(patch_hash=cczsf6i6qe2m2htirvgxjtoclu)(next@14.2.26(@babel/core@7.24.3)(@opentelemetry/api@1.9.0)(@playwright/test@1.47.2)(babel-plugin-macros@3.1.0)(react-dom@18.2.0(react@18.2.0))(react@18.2.0))(nodemailer@6.9.15)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)
|
||||
|
||||
'@next/env@14.2.26': {}
|
||||
|
||||
@@ -15944,7 +15978,7 @@ snapshots:
|
||||
https-proxy-agent: 5.0.1
|
||||
progress: 2.0.3
|
||||
proxy-from-env: 1.1.0
|
||||
tar-fs: 2.1.1
|
||||
tar-fs: 2.1.3
|
||||
unbzip2-stream: 1.4.3
|
||||
yargs: 17.7.1
|
||||
optionalDependencies:
|
||||
@@ -16764,43 +16798,64 @@ snapshots:
|
||||
optionalDependencies:
|
||||
rollup: 3.29.5
|
||||
|
||||
'@rollup/rollup-android-arm-eabi@4.13.0':
|
||||
'@rollup/rollup-android-arm-eabi@4.41.1':
|
||||
optional: true
|
||||
|
||||
'@rollup/rollup-android-arm64@4.13.0':
|
||||
'@rollup/rollup-android-arm64@4.41.1':
|
||||
optional: true
|
||||
|
||||
'@rollup/rollup-darwin-arm64@4.13.0':
|
||||
'@rollup/rollup-darwin-arm64@4.41.1':
|
||||
optional: true
|
||||
|
||||
'@rollup/rollup-darwin-x64@4.13.0':
|
||||
'@rollup/rollup-darwin-x64@4.41.1':
|
||||
optional: true
|
||||
|
||||
'@rollup/rollup-linux-arm-gnueabihf@4.13.0':
|
||||
'@rollup/rollup-freebsd-arm64@4.41.1':
|
||||
optional: true
|
||||
|
||||
'@rollup/rollup-linux-arm64-gnu@4.13.0':
|
||||
'@rollup/rollup-freebsd-x64@4.41.1':
|
||||
optional: true
|
||||
|
||||
'@rollup/rollup-linux-arm64-musl@4.13.0':
|
||||
'@rollup/rollup-linux-arm-gnueabihf@4.41.1':
|
||||
optional: true
|
||||
|
||||
'@rollup/rollup-linux-riscv64-gnu@4.13.0':
|
||||
'@rollup/rollup-linux-arm-musleabihf@4.41.1':
|
||||
optional: true
|
||||
|
||||
'@rollup/rollup-linux-x64-gnu@4.13.0':
|
||||
'@rollup/rollup-linux-arm64-gnu@4.41.1':
|
||||
optional: true
|
||||
|
||||
'@rollup/rollup-linux-x64-musl@4.13.0':
|
||||
'@rollup/rollup-linux-arm64-musl@4.41.1':
|
||||
optional: true
|
||||
|
||||
'@rollup/rollup-win32-arm64-msvc@4.13.0':
|
||||
'@rollup/rollup-linux-loongarch64-gnu@4.41.1':
|
||||
optional: true
|
||||
|
||||
'@rollup/rollup-win32-ia32-msvc@4.13.0':
|
||||
'@rollup/rollup-linux-powerpc64le-gnu@4.41.1':
|
||||
optional: true
|
||||
|
||||
'@rollup/rollup-win32-x64-msvc@4.13.0':
|
||||
'@rollup/rollup-linux-riscv64-gnu@4.41.1':
|
||||
optional: true
|
||||
|
||||
'@rollup/rollup-linux-riscv64-musl@4.41.1':
|
||||
optional: true
|
||||
|
||||
'@rollup/rollup-linux-s390x-gnu@4.41.1':
|
||||
optional: true
|
||||
|
||||
'@rollup/rollup-linux-x64-gnu@4.41.1':
|
||||
optional: true
|
||||
|
||||
'@rollup/rollup-linux-x64-musl@4.41.1':
|
||||
optional: true
|
||||
|
||||
'@rollup/rollup-win32-arm64-msvc@4.41.1':
|
||||
optional: true
|
||||
|
||||
'@rollup/rollup-win32-ia32-msvc@4.41.1':
|
||||
optional: true
|
||||
|
||||
'@rollup/rollup-win32-x64-msvc@4.41.1':
|
||||
optional: true
|
||||
|
||||
'@rushstack/eslint-patch@1.7.2': {}
|
||||
@@ -18129,7 +18184,7 @@ 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@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-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)
|
||||
@@ -20133,7 +20188,7 @@ snapshots:
|
||||
eslint: 8.57.0
|
||||
eslint-plugin-turbo: 1.13.4(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@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)):
|
||||
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)
|
||||
|
||||
@@ -20224,7 +20279,7 @@ snapshots:
|
||||
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)
|
||||
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)
|
||||
hasown: 2.0.2
|
||||
is-core-module: 2.15.1
|
||||
is-glob: 4.0.3
|
||||
@@ -20251,7 +20306,7 @@ snapshots:
|
||||
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-plugin-import@2.29.1)(eslint@8.57.0))(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)
|
||||
hasown: 2.0.2
|
||||
is-core-module: 2.15.1
|
||||
is-glob: 4.0.3
|
||||
@@ -23120,7 +23175,7 @@ snapshots:
|
||||
dependencies:
|
||||
type-fest: 2.19.0
|
||||
|
||||
next-auth@4.24.11(patch_hash=cczsf6i6qe2m2htirvgxjtoclu)(next@14.2.26(@opentelemetry/api@1.9.0)(@playwright/test@1.47.2)(react-dom@18.2.0(react@18.2.0))(react@18.2.0))(nodemailer@6.9.15)(react-dom@18.2.0(react@18.2.0))(react@18.2.0):
|
||||
next-auth@4.24.11(patch_hash=cczsf6i6qe2m2htirvgxjtoclu)(next@14.2.26(@babel/core@7.24.3)(@opentelemetry/api@1.9.0)(@playwright/test@1.47.2)(babel-plugin-macros@3.1.0)(react-dom@18.2.0(react@18.2.0))(react@18.2.0))(nodemailer@6.9.15)(react-dom@18.2.0(react@18.2.0))(react@18.2.0):
|
||||
dependencies:
|
||||
'@babel/runtime': 7.26.0
|
||||
'@panva/hkdf': 1.1.1
|
||||
@@ -23999,7 +24054,7 @@ snapshots:
|
||||
extract-zip: 2.0.1
|
||||
https-proxy-agent: 5.0.1
|
||||
proxy-from-env: 1.1.0
|
||||
tar-fs: 2.1.1
|
||||
tar-fs: 2.1.3
|
||||
unbzip2-stream: 1.4.3
|
||||
ws: 8.13.0
|
||||
optionalDependencies:
|
||||
@@ -24496,23 +24551,30 @@ snapshots:
|
||||
optionalDependencies:
|
||||
fsevents: 2.3.3
|
||||
|
||||
rollup@4.13.0:
|
||||
rollup@4.41.1:
|
||||
dependencies:
|
||||
'@types/estree': 1.0.5
|
||||
'@types/estree': 1.0.7
|
||||
optionalDependencies:
|
||||
'@rollup/rollup-android-arm-eabi': 4.13.0
|
||||
'@rollup/rollup-android-arm64': 4.13.0
|
||||
'@rollup/rollup-darwin-arm64': 4.13.0
|
||||
'@rollup/rollup-darwin-x64': 4.13.0
|
||||
'@rollup/rollup-linux-arm-gnueabihf': 4.13.0
|
||||
'@rollup/rollup-linux-arm64-gnu': 4.13.0
|
||||
'@rollup/rollup-linux-arm64-musl': 4.13.0
|
||||
'@rollup/rollup-linux-riscv64-gnu': 4.13.0
|
||||
'@rollup/rollup-linux-x64-gnu': 4.13.0
|
||||
'@rollup/rollup-linux-x64-musl': 4.13.0
|
||||
'@rollup/rollup-win32-arm64-msvc': 4.13.0
|
||||
'@rollup/rollup-win32-ia32-msvc': 4.13.0
|
||||
'@rollup/rollup-win32-x64-msvc': 4.13.0
|
||||
'@rollup/rollup-android-arm-eabi': 4.41.1
|
||||
'@rollup/rollup-android-arm64': 4.41.1
|
||||
'@rollup/rollup-darwin-arm64': 4.41.1
|
||||
'@rollup/rollup-darwin-x64': 4.41.1
|
||||
'@rollup/rollup-freebsd-arm64': 4.41.1
|
||||
'@rollup/rollup-freebsd-x64': 4.41.1
|
||||
'@rollup/rollup-linux-arm-gnueabihf': 4.41.1
|
||||
'@rollup/rollup-linux-arm-musleabihf': 4.41.1
|
||||
'@rollup/rollup-linux-arm64-gnu': 4.41.1
|
||||
'@rollup/rollup-linux-arm64-musl': 4.41.1
|
||||
'@rollup/rollup-linux-loongarch64-gnu': 4.41.1
|
||||
'@rollup/rollup-linux-powerpc64le-gnu': 4.41.1
|
||||
'@rollup/rollup-linux-riscv64-gnu': 4.41.1
|
||||
'@rollup/rollup-linux-riscv64-musl': 4.41.1
|
||||
'@rollup/rollup-linux-s390x-gnu': 4.41.1
|
||||
'@rollup/rollup-linux-x64-gnu': 4.41.1
|
||||
'@rollup/rollup-linux-x64-musl': 4.41.1
|
||||
'@rollup/rollup-win32-arm64-msvc': 4.41.1
|
||||
'@rollup/rollup-win32-ia32-msvc': 4.41.1
|
||||
'@rollup/rollup-win32-x64-msvc': 4.41.1
|
||||
fsevents: 2.3.3
|
||||
|
||||
roughjs@4.6.6:
|
||||
@@ -25081,7 +25143,7 @@ snapshots:
|
||||
|
||||
tapable@2.2.1: {}
|
||||
|
||||
tar-fs@2.1.1:
|
||||
tar-fs@2.1.3:
|
||||
dependencies:
|
||||
chownr: 1.1.4
|
||||
mkdirp-classic: 0.5.3
|
||||
@@ -25688,7 +25750,7 @@ snapshots:
|
||||
dependencies:
|
||||
esbuild: 0.20.2
|
||||
postcss: 8.5.3
|
||||
rollup: 4.13.0
|
||||
rollup: 4.41.1
|
||||
optionalDependencies:
|
||||
'@types/node': 20.10.5
|
||||
fsevents: 2.3.3
|
||||
@@ -25698,7 +25760,7 @@ snapshots:
|
||||
dependencies:
|
||||
esbuild: 0.20.2
|
||||
postcss: 8.5.3
|
||||
rollup: 4.13.0
|
||||
rollup: 4.41.1
|
||||
optionalDependencies:
|
||||
'@types/node': 20.11.29
|
||||
fsevents: 2.3.3
|
||||
@@ -25708,7 +25770,7 @@ snapshots:
|
||||
dependencies:
|
||||
esbuild: 0.20.2
|
||||
postcss: 8.5.3
|
||||
rollup: 4.13.0
|
||||
rollup: 4.41.1
|
||||
optionalDependencies:
|
||||
'@types/node': 20.14.8
|
||||
fsevents: 2.3.3
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "web",
|
||||
"version": "3.63.0",
|
||||
"version": "3.65.3",
|
||||
"private": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
|
||||
@@ -5268,19 +5268,19 @@ components:
|
||||
type: number
|
||||
format: double
|
||||
nullable: true
|
||||
description: Price (USD) per input unit
|
||||
description: Deprecated. See 'prices' instead. Price (USD) per input unit
|
||||
outputPrice:
|
||||
type: number
|
||||
format: double
|
||||
nullable: true
|
||||
description: Price (USD) per output unit
|
||||
description: Deprecated. See 'prices' instead. Price (USD) per output unit
|
||||
totalPrice:
|
||||
type: number
|
||||
format: double
|
||||
nullable: true
|
||||
description: >-
|
||||
Price (USD) per total unit. Cannot be set if input or output price
|
||||
is set.
|
||||
Deprecated. See 'prices' instead. Price (USD) per total unit. Cannot
|
||||
be set if input or output price is set.
|
||||
tokenizerId:
|
||||
type: string
|
||||
nullable: true
|
||||
@@ -5294,11 +5294,26 @@ components:
|
||||
JSON. See docs for more details.
|
||||
isLangfuseManaged:
|
||||
type: boolean
|
||||
prices:
|
||||
type: object
|
||||
additionalProperties:
|
||||
$ref: '#/components/schemas/ModelPrice'
|
||||
description: Price (USD) by usage type
|
||||
required:
|
||||
- id
|
||||
- modelName
|
||||
- matchPattern
|
||||
- isLangfuseManaged
|
||||
- prices
|
||||
ModelPrice:
|
||||
title: ModelPrice
|
||||
type: object
|
||||
properties:
|
||||
price:
|
||||
type: number
|
||||
format: double
|
||||
required:
|
||||
- price
|
||||
ModelUsageUnit:
|
||||
title: ModelUsageUnit
|
||||
type: string
|
||||
@@ -6535,6 +6550,7 @@ components:
|
||||
description: Metadata for the project
|
||||
retentionDays:
|
||||
type: integer
|
||||
nullable: true
|
||||
description: >-
|
||||
Number of days to retain data. Null or 0 means no retention. Omitted
|
||||
if no retention is configured.
|
||||
@@ -6542,7 +6558,6 @@ components:
|
||||
- id
|
||||
- name
|
||||
- metadata
|
||||
- retentionDays
|
||||
ProjectDeletionResponse:
|
||||
title: ProjectDeletionResponse
|
||||
type: object
|
||||
|
||||
@@ -38,6 +38,13 @@ describe("/models API Endpoints", () => {
|
||||
unit: "TOKENS",
|
||||
},
|
||||
});
|
||||
await prisma.price.createMany({
|
||||
data: [
|
||||
{ modelId: "model-1", usageType: "input", price: 0.001 },
|
||||
{ modelId: "model-1", usageType: "output", price: 0.002 },
|
||||
{ modelId: "model-1", usageType: "total", price: 0.1 },
|
||||
],
|
||||
});
|
||||
await prisma.model.create({
|
||||
data: {
|
||||
id: "model-2",
|
||||
@@ -51,6 +58,12 @@ describe("/models API Endpoints", () => {
|
||||
unit: "TOKENS",
|
||||
},
|
||||
});
|
||||
await prisma.price.createMany({
|
||||
data: [
|
||||
{ modelId: "model-2", usageType: "input", price: 0.002 },
|
||||
{ modelId: "model-2", usageType: "output", price: 0.004 },
|
||||
],
|
||||
});
|
||||
});
|
||||
afterEach(async () => await pruneDatabase());
|
||||
|
||||
@@ -67,6 +80,11 @@ describe("/models API Endpoints", () => {
|
||||
expect(models.body.data[0]).toMatchObject({
|
||||
isLangfuseManaged: true,
|
||||
modelName: "gpt-3.5-turbo",
|
||||
prices: {
|
||||
input: { price: 0.001 },
|
||||
output: { price: 0.002 },
|
||||
total: { price: 0.1 },
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
@@ -105,6 +123,10 @@ describe("/models API Endpoints", () => {
|
||||
auth,
|
||||
);
|
||||
expect(customModel.body.isLangfuseManaged).toBe(false);
|
||||
expect(customModel.body.prices).toMatchObject({
|
||||
input: { price: 0.002 },
|
||||
output: { price: 0.004 },
|
||||
});
|
||||
|
||||
const models = await makeZodVerifiedAPICall(
|
||||
GetModelsV1Response,
|
||||
@@ -132,6 +154,10 @@ describe("/models API Endpoints", () => {
|
||||
unit: "TOKENS",
|
||||
tokenizerConfig: { tokensPerMessage: 3, tokensPerName: 1 },
|
||||
isLangfuseManaged: false,
|
||||
prices: {
|
||||
input: { price: 0.002 },
|
||||
output: { price: 0.004 },
|
||||
},
|
||||
});
|
||||
|
||||
const prices = await prisma.price.findMany({
|
||||
|
||||
@@ -459,7 +459,7 @@ describe("OTel Resource Span Mapping", () => {
|
||||
expect(traceEvent.body).toMatchObject({
|
||||
id: "95f3b926c7d009925bcb5dbc27311120",
|
||||
timestamp: "2025-05-05T13:42:33.936Z",
|
||||
name: "my-span-with-custom-trace-id",
|
||||
name: undefined,
|
||||
environment: "production",
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import chatCompletionHandler from "@/src/ee/features/playground/server/chatCompletionHandler";
|
||||
import chatCompletionHandler from "@/src/features/playground/server/chatCompletionHandler";
|
||||
|
||||
export const dynamic = "force-dynamic";
|
||||
export const maxDuration = 120;
|
||||
|
||||
@@ -40,6 +40,7 @@ export type ModelParamsContext = {
|
||||
setModelParamEnabled?: (key: keyof UIModelParams, enabled: boolean) => void;
|
||||
formDisabled?: boolean;
|
||||
modelParamsDescription?: string;
|
||||
customHeader?: React.ReactNode;
|
||||
};
|
||||
|
||||
export const ModelParameters: React.FC<ModelParamsContext> = ({
|
||||
@@ -50,6 +51,7 @@ export const ModelParameters: React.FC<ModelParamsContext> = ({
|
||||
setModelParamEnabled,
|
||||
formDisabled = false,
|
||||
modelParamsDescription,
|
||||
customHeader,
|
||||
}) => {
|
||||
const projectId = useProjectIdFromURL();
|
||||
const [modelSettingsOpen, setModelSettingsOpen] = useState(false);
|
||||
@@ -74,9 +76,13 @@ export const ModelParameters: React.FC<ModelParamsContext> = ({
|
||||
if (availableProviders.length === 0) {
|
||||
return (
|
||||
<div className="flex flex-col space-y-4 pr-1">
|
||||
<div className="flex items-center justify-between">
|
||||
<p className="font-semibold">Model</p>
|
||||
</div>
|
||||
{customHeader ? (
|
||||
customHeader
|
||||
) : (
|
||||
<div className="flex items-center justify-between">
|
||||
<p className="font-semibold">Model</p>
|
||||
</div>
|
||||
)}
|
||||
<p className="text-xs">No LLM API key set in project. </p>
|
||||
<CreateLLMApiKeyDialog />
|
||||
</div>
|
||||
@@ -86,7 +92,7 @@ export const ModelParameters: React.FC<ModelParamsContext> = ({
|
||||
return (
|
||||
<div className="flex flex-col space-y-2 pb-1 pr-1 pt-2">
|
||||
<div className="flex items-center justify-between">
|
||||
<p className="font-semibold">Model</p>
|
||||
{customHeader ? customHeader : <p className="font-semibold">Model</p>}
|
||||
<Popover open={modelSettingsOpen} onOpenChange={setModelSettingsOpen}>
|
||||
<PopoverTrigger asChild>
|
||||
<Button
|
||||
|
||||
@@ -52,8 +52,9 @@ export const VersionLabel = ({ className }: { className?: string }) => {
|
||||
const selfHostedPlanLabel = !isLangfuseCloud
|
||||
? plan && isSelfHostedPlan(plan)
|
||||
? // self-host plan
|
||||
// TODO: clean up to use planLabels in packages/shared/src/features/entitlements/plans.ts
|
||||
{
|
||||
short: plan === "self-hosted:pro" ? "Pro" : "EE",
|
||||
short: "EE",
|
||||
long: planLabels[plan],
|
||||
}
|
||||
: // no plan, oss
|
||||
|
||||
@@ -292,7 +292,7 @@ export function DeleteDashboardButton(props: DeleteButtonProps) {
|
||||
);
|
||||
}
|
||||
|
||||
export function DeleteEvaluatorButton(props: DeleteButtonProps) {
|
||||
export function DeleteEvalConfigButton(props: DeleteButtonProps) {
|
||||
const utils = api.useUtils();
|
||||
const {
|
||||
itemId,
|
||||
@@ -300,21 +300,29 @@ export function DeleteEvaluatorButton(props: DeleteButtonProps) {
|
||||
scope = "evalJob:CUD",
|
||||
invalidateFunc = () => void utils.evals.invalidate(),
|
||||
} = props;
|
||||
const evaluatorMutation = api.evals.deleteEvalJob.useMutation();
|
||||
|
||||
const evaluatorMutation = api.evals.deleteEvalJob.useMutation({
|
||||
onSuccess: () => {
|
||||
showSuccessToast({
|
||||
title: "Running evaluator deleted",
|
||||
description: "The running evaluator has been deleted successfully",
|
||||
});
|
||||
void utils.evals.invalidate();
|
||||
},
|
||||
});
|
||||
|
||||
const executeDeleteMutation = async (onSuccess: () => void) => {
|
||||
try {
|
||||
await evaluatorMutation.mutateAsync({
|
||||
evalConfigId: itemId,
|
||||
projectId,
|
||||
});
|
||||
onSuccess();
|
||||
} catch (error) {
|
||||
return Promise.reject(error);
|
||||
}
|
||||
onSuccess();
|
||||
};
|
||||
const hasModelBasedEvaluationEntitlement = useHasEntitlement(
|
||||
"model-based-evaluations",
|
||||
);
|
||||
|
||||
return (
|
||||
<DeleteButton
|
||||
{...props}
|
||||
@@ -322,18 +330,17 @@ export function DeleteEvaluatorButton(props: DeleteButtonProps) {
|
||||
invalidateFunc={invalidateFunc}
|
||||
captureDeleteOpen={(capture, isTableAction) =>
|
||||
capture("eval_config:delete_form_open", {
|
||||
source: isTableAction ? "table-single-row" : "evaluator",
|
||||
source: isTableAction ? "table-single-row" : "eval config detail",
|
||||
})
|
||||
}
|
||||
captureDeleteSuccess={(capture, isTableAction) =>
|
||||
capture("eval_config:delete_evaluator_button_click", {
|
||||
source: isTableAction ? "table-single-row" : "evaluator",
|
||||
source: isTableAction ? "table-single-row" : "eval config detail",
|
||||
})
|
||||
}
|
||||
entityToDeleteName="evaluator"
|
||||
entityToDeleteName="running evaluator"
|
||||
executeDeleteMutation={executeDeleteMutation}
|
||||
isDeleteMutationLoading={evaluatorMutation.isLoading}
|
||||
enabled={hasModelBasedEvaluationEntitlement}
|
||||
/>
|
||||
);
|
||||
}
|
||||
@@ -370,9 +377,7 @@ export function DeleteEvaluationModelButton(
|
||||
}
|
||||
onSuccess();
|
||||
};
|
||||
const hasModelBasedEvaluationEntitlement = useHasEntitlement(
|
||||
"model-based-evaluations",
|
||||
);
|
||||
|
||||
return (
|
||||
<DeleteButton
|
||||
{...props}
|
||||
@@ -394,7 +399,6 @@ export function DeleteEvaluationModelButton(
|
||||
deleteConfirmation="delete"
|
||||
executeDeleteMutation={executeDeleteMutation}
|
||||
isDeleteMutationLoading={isLoading}
|
||||
enabled={hasModelBasedEvaluationEntitlement}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -22,6 +22,19 @@ const partitionScores = <T extends APIScoreV2 | LastUserScore>(
|
||||
return { visibleScores, hiddenScores };
|
||||
};
|
||||
|
||||
const hasMetadata = (score: APIScoreV2 | LastUserScore) => {
|
||||
if (!score.metadata) return false;
|
||||
try {
|
||||
const metadata =
|
||||
typeof score.metadata === "string"
|
||||
? JSON.parse(score.metadata)
|
||||
: score.metadata;
|
||||
return Object.keys(metadata).length > 0;
|
||||
} catch {
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
const ScoreGroupBadge = <T extends APIScoreV2 | LastUserScore>({
|
||||
name,
|
||||
scores,
|
||||
@@ -50,17 +63,17 @@ const ScoreGroupBadge = <T extends APIScoreV2 | LastUserScore>({
|
||||
<HoverCardTrigger className="inline-block">
|
||||
<MessageCircleMoreIcon className="mb-[0.0625rem] !size-3" />
|
||||
</HoverCardTrigger>
|
||||
<HoverCardContent className="overflow-hidden whitespace-normal break-normal">
|
||||
<HoverCardContent className="max-h-[50dvh] overflow-y-auto whitespace-normal break-normal">
|
||||
<p className="whitespace-pre-wrap">{s.comment}</p>
|
||||
</HoverCardContent>
|
||||
</HoverCard>
|
||||
)}
|
||||
{s.metadata && Object.keys(s.metadata).length > 0 && (
|
||||
{hasMetadata(s) && (
|
||||
<HoverCard>
|
||||
<HoverCardTrigger className="inline-block">
|
||||
<BracesIcon className="mb-[0.0625rem] !size-3" />
|
||||
</HoverCardTrigger>
|
||||
<HoverCardContent className="overflow-hidden whitespace-normal break-normal rounded-md border-none p-0">
|
||||
<HoverCardContent className="max-h-[50dvh] overflow-y-auto whitespace-normal break-normal rounded-md border-none p-0">
|
||||
<JSONView codeClassName="!rounded-md" json={s.metadata} />
|
||||
</HoverCardContent>
|
||||
</HoverCard>
|
||||
|
||||
@@ -73,7 +73,6 @@ export const ROUTES: Route[] = [
|
||||
title: "Dashboards",
|
||||
pathname: `/project/[projectId]/dashboards`,
|
||||
icon: LayoutDashboard,
|
||||
entitlements: ["custom-dashboards"],
|
||||
productModule: "dashboards",
|
||||
},
|
||||
{
|
||||
@@ -105,19 +104,16 @@ export const ROUTES: Route[] = [
|
||||
icon: Lightbulb,
|
||||
pathname: `/project/[projectId]/annotation-queues`,
|
||||
productModule: "evaluation",
|
||||
entitlements: ["annotation-queues", "model-based-evaluations"],
|
||||
projectRbacScopes: ["annotationQueues:read", "evalJob:read"],
|
||||
items: [
|
||||
{
|
||||
title: "Human Annotation",
|
||||
pathname: `/project/[projectId]/annotation-queues`,
|
||||
projectRbacScopes: ["annotationQueues:read"],
|
||||
entitlements: ["annotation-queues"],
|
||||
},
|
||||
{
|
||||
title: "LLM-as-a-Judge",
|
||||
pathname: `/project/[projectId]/evals`,
|
||||
entitlements: ["model-based-evaluations"],
|
||||
projectRbacScopes: ["evalJob:read"],
|
||||
},
|
||||
],
|
||||
@@ -140,7 +136,6 @@ export const ROUTES: Route[] = [
|
||||
pathname: "/project/[projectId]/playground",
|
||||
icon: TerminalIcon,
|
||||
productModule: "playground",
|
||||
entitlements: ["playground"],
|
||||
},
|
||||
{
|
||||
title: "Datasets",
|
||||
|
||||
@@ -4,7 +4,7 @@ import {
|
||||
type ValueProposition,
|
||||
} from "@/src/components/ui/splash-screen";
|
||||
import { ClipboardCheck, Users, BarChart4, GitMerge } from "lucide-react";
|
||||
import { CreateOrEditAnnotationQueueButton } from "@/src/ee/features/annotation-queues/components/CreateOrEditAnnotationQueueButton";
|
||||
import { CreateOrEditAnnotationQueueButton } from "@/src/features/annotation-queues/components/CreateOrEditAnnotationQueueButton";
|
||||
|
||||
export function AnnotationQueuesOnboarding({
|
||||
projectId,
|
||||
|
||||
@@ -136,7 +136,7 @@ export function DataTableToolbar<TData, TValue>({
|
||||
<div className={cn("grid h-fit w-full gap-0 px-2", className)}>
|
||||
<div className="my-2 flex flex-wrap items-center gap-2 @container">
|
||||
{searchConfig && (
|
||||
<div className="flex w-full max-w-sm items-stretch">
|
||||
<div className="flex min-w-0 max-w-64 flex-shrink-0 items-stretch">
|
||||
<div
|
||||
className={cn(
|
||||
"flex h-8 flex-1 items-center border border-input bg-background pl-2",
|
||||
@@ -180,13 +180,13 @@ export function DataTableToolbar<TData, TValue>({
|
||||
<Button
|
||||
variant="outline"
|
||||
size="default"
|
||||
className="flex w-auto min-w-[130px] items-center justify-between gap-1 rounded-l-none border-l-0"
|
||||
className="w-30 flex items-center justify-between gap-1 rounded-l-none border-l-0"
|
||||
>
|
||||
<span className="flex items-center gap-1">
|
||||
<span className="flex items-center gap-1 truncate">
|
||||
{searchConfig.tableAllowsFullTextSearch &&
|
||||
(searchConfig.searchType ?? []).includes("content")
|
||||
? "Metadata + Full Text"
|
||||
: "Metadata"}
|
||||
? "Full Text"
|
||||
: "IDs / Names"}
|
||||
<DocPopup
|
||||
description={
|
||||
<>
|
||||
@@ -242,13 +242,13 @@ export function DataTableToolbar<TData, TValue>({
|
||||
}}
|
||||
>
|
||||
<DropdownMenuRadioItem value="metadata">
|
||||
Metadata
|
||||
IDs / Names
|
||||
</DropdownMenuRadioItem>
|
||||
<DropdownMenuRadioItem
|
||||
value="metadata_fulltext"
|
||||
disabled={!searchConfig.tableAllowsFullTextSearch}
|
||||
>
|
||||
Metadata + Full Text
|
||||
Full Text
|
||||
</DropdownMenuRadioItem>
|
||||
</DropdownMenuRadioGroup>
|
||||
</DropdownMenuContent>
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { type EvalsTemplateRow } from "@/src/ee/features/evals/components/eval-templates-table";
|
||||
import { type EvalsTemplateRow } from "@/src/features/evals/components/eval-templates-table";
|
||||
import { type ListEntry } from "@/src/features/navigate-detail-pages/context";
|
||||
import { useRouter } from "next/router";
|
||||
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import { usePeekState } from "@/src/components/table/peek/hooks/usePeekState";
|
||||
import { type EvaluatorDataRow } from "@/src/ee/features/evals/components/evaluator-table";
|
||||
import { type EvaluatorDataRow } from "@/src/features/evals/components/evaluator-table";
|
||||
import { Skeleton } from "@/src/components/ui/skeleton";
|
||||
import TableLink from "@/src/components/table/table-link";
|
||||
import { CardDescription } from "@/src/components/ui/card";
|
||||
import { EvaluatorForm } from "@/src/ee/features/evals/components/evaluator-form";
|
||||
import { EvaluatorForm } from "@/src/features/evals/components/evaluator-form";
|
||||
import { usePeekEvalConfigData } from "@/src/components/table/peek/hooks/usePeekEvalConfigData";
|
||||
import {
|
||||
Tooltip,
|
||||
@@ -13,7 +13,7 @@ import {
|
||||
import { LangfuseIcon } from "@/src/components/LangfuseLogo";
|
||||
import { UserCircle2Icon } from "lucide-react";
|
||||
import { StatusBadge } from "@/src/components/layouts/status-badge";
|
||||
import { DeactivateEvalConfig } from "@/src/ee/features/evals/components/deactivate-config";
|
||||
import { DeactivateEvalConfig } from "@/src/features/evals/components/deactivate-config";
|
||||
|
||||
export const PeekViewEvaluatorConfigDetail = ({
|
||||
projectId,
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import { usePeekState } from "@/src/components/table/peek/hooks/usePeekState";
|
||||
import { Skeleton } from "@/src/components/ui/skeleton";
|
||||
import { usePeekEvalTemplateData } from "@/src/components/table/peek/hooks/usePeekEvalTemplateData";
|
||||
import { EvalTemplateForm } from "@/src/ee/features/evals/components/template-form";
|
||||
import { MaintainerTooltip } from "@/src/ee/features/evals/components/maintainer-tooltip";
|
||||
import { getMaintainer } from "@/src/ee/features/evals/utils/typeHelpers";
|
||||
import { EvalTemplateForm } from "@/src/features/evals/components/template-form";
|
||||
import { MaintainerTooltip } from "@/src/features/evals/components/maintainer-tooltip";
|
||||
import { getMaintainer } from "@/src/features/evals/utils/typeHelpers";
|
||||
|
||||
export const PeekViewEvaluatorTemplateDetail = ({
|
||||
projectId,
|
||||
|
||||
@@ -427,7 +427,6 @@ export default function TracesTable({
|
||||
execute: handleAddToAnnotationQueue,
|
||||
accessCheck: {
|
||||
scope: "annotationQueues:CUD",
|
||||
entitlement: "annotation-queues",
|
||||
},
|
||||
},
|
||||
];
|
||||
@@ -1076,7 +1075,7 @@ export default function TracesTable({
|
||||
}}
|
||||
filterColumnDefinition={transformedFilterOptions}
|
||||
searchConfig={{
|
||||
metadataSearchFields: ["ID", "Name", "Trace Name", "User ID"],
|
||||
metadataSearchFields: ["ID", "Trace Name", "User ID"],
|
||||
updateQuery: setSearchQuery,
|
||||
currentQuery: searchQuery ?? undefined,
|
||||
tableAllowsFullTextSearch: true,
|
||||
|
||||
@@ -9,14 +9,13 @@ import Link from "next/link";
|
||||
import { usdFormatter } from "@/src/utils/numbers";
|
||||
import { withDefault, StringParam, useQueryParam } from "use-query-params";
|
||||
import ScoresTable from "@/src/components/table/use-cases/scores";
|
||||
import { JumpToPlaygroundButton } from "@/src/ee/features/playground/page/components/JumpToPlaygroundButton";
|
||||
import { JumpToPlaygroundButton } from "@/src/features/playground/page/components/JumpToPlaygroundButton";
|
||||
import { AnnotateDrawer } from "@/src/features/scores/components/AnnotateDrawer";
|
||||
import useLocalStorage from "@/src/components/useLocalStorage";
|
||||
import { CommentDrawerButton } from "@/src/features/comments/CommentDrawerButton";
|
||||
import { cn } from "@/src/utils/tailwind";
|
||||
import { NewDatasetItemFromExistingObject } from "@/src/features/datasets/components/NewDatasetItemFromExistingObject";
|
||||
import { CreateNewAnnotationQueueItem } from "@/src/ee/features/annotation-queues/components/CreateNewAnnotationQueueItem";
|
||||
import { useHasEntitlement } from "@/src/features/entitlements/hooks";
|
||||
import { CreateNewAnnotationQueueItem } from "@/src/features/annotation-queues/components/CreateNewAnnotationQueueItem";
|
||||
import { calculateDisplayTotalCost } from "@/src/components/trace/lib/helpers";
|
||||
import { Fragment, useMemo, useState } from "react";
|
||||
import { useIsAuthenticatedAndProjectMember } from "@/src/features/auth/hooks";
|
||||
@@ -65,7 +64,7 @@ export const ObservationPreview = ({
|
||||
const [emptySelectedConfigIds, setEmptySelectedConfigIds] = useLocalStorage<
|
||||
string[]
|
||||
>("emptySelectedConfigIds", []);
|
||||
const hasEntitlement = useHasEntitlement("annotation-queues");
|
||||
|
||||
const isAuthenticatedAndProjectMember =
|
||||
useIsAuthenticatedAndProjectMember(projectId);
|
||||
const router = useRouter();
|
||||
@@ -163,16 +162,15 @@ export const ObservationPreview = ({
|
||||
scores={scores}
|
||||
emptySelectedConfigIds={emptySelectedConfigIds}
|
||||
setEmptySelectedConfigIds={setEmptySelectedConfigIds}
|
||||
hasGroupedButton={hasEntitlement}
|
||||
hasGroupedButton={true}
|
||||
environment={preloadedObservation.environment}
|
||||
/>
|
||||
{hasEntitlement && (
|
||||
<CreateNewAnnotationQueueItem
|
||||
projectId={projectId}
|
||||
objectId={preloadedObservation.id}
|
||||
objectType={AnnotationQueueObjectType.OBSERVATION}
|
||||
/>
|
||||
)}
|
||||
|
||||
<CreateNewAnnotationQueueItem
|
||||
projectId={projectId}
|
||||
objectId={preloadedObservation.id}
|
||||
objectType={AnnotationQueueObjectType.OBSERVATION}
|
||||
/>
|
||||
</div>
|
||||
{observationWithInputAndOutput.data?.type === "GENERATION" && (
|
||||
<JumpToPlaygroundButton
|
||||
|
||||
@@ -16,8 +16,7 @@ import useLocalStorage from "@/src/components/useLocalStorage";
|
||||
import { CommentDrawerButton } from "@/src/features/comments/CommentDrawerButton";
|
||||
import { api } from "@/src/utils/api";
|
||||
import { NewDatasetItemFromExistingObject } from "@/src/features/datasets/components/NewDatasetItemFromExistingObject";
|
||||
import { CreateNewAnnotationQueueItem } from "@/src/ee/features/annotation-queues/components/CreateNewAnnotationQueueItem";
|
||||
import { useHasEntitlement } from "@/src/features/entitlements/hooks";
|
||||
import { CreateNewAnnotationQueueItem } from "@/src/features/annotation-queues/components/CreateNewAnnotationQueueItem";
|
||||
import { useMemo, useState } from "react";
|
||||
import { usdFormatter } from "@/src/utils/numbers";
|
||||
import { calculateDisplayTotalCost } from "@/src/components/trace/lib/helpers";
|
||||
@@ -65,7 +64,6 @@ export const TracePreview = ({
|
||||
const [emptySelectedConfigIds, setEmptySelectedConfigIds] = useLocalStorage<
|
||||
string[]
|
||||
>("emptySelectedConfigIds", []);
|
||||
const hasEntitlement = useHasEntitlement("annotation-queues");
|
||||
const isAuthenticatedAndProjectMember = useIsAuthenticatedAndProjectMember(
|
||||
trace.projectId,
|
||||
);
|
||||
@@ -139,16 +137,14 @@ export const TracePreview = ({
|
||||
scores={scores}
|
||||
emptySelectedConfigIds={emptySelectedConfigIds}
|
||||
setEmptySelectedConfigIds={setEmptySelectedConfigIds}
|
||||
hasGroupedButton={hasEntitlement}
|
||||
hasGroupedButton={true}
|
||||
environment={trace.environment}
|
||||
/>
|
||||
{hasEntitlement && (
|
||||
<CreateNewAnnotationQueueItem
|
||||
projectId={trace.projectId}
|
||||
objectId={trace.id}
|
||||
objectType={AnnotationQueueObjectType.TRACE}
|
||||
/>
|
||||
)}
|
||||
<CreateNewAnnotationQueueItem
|
||||
projectId={trace.projectId}
|
||||
objectId={trace.id}
|
||||
objectType={AnnotationQueueObjectType.TRACE}
|
||||
/>
|
||||
</div>
|
||||
<CommentDrawerButton
|
||||
projectId={trace.projectId}
|
||||
|
||||
@@ -1 +1 @@
|
||||
export const VERSION = "v3.63.0";
|
||||
export const VERSION = "v3.65.3";
|
||||
|
||||
@@ -68,7 +68,7 @@ export const stripeProducts: StripeProduct[] = [
|
||||
: "prod_QhK9qKGH25BTcS", // live
|
||||
mappedPlan: "cloud:team",
|
||||
checkout: {
|
||||
title: "Team",
|
||||
title: "Pro + Teams Add-on",
|
||||
description: "Organizational and security controls for larger teams.",
|
||||
price: "$499 / month",
|
||||
usagePrice: "$8/100k events (100k included)",
|
||||
|
||||
@@ -1,60 +0,0 @@
|
||||
import { InfoIcon, Loader } from "lucide-react";
|
||||
import { type EvalFormType } from "@/src/ee/features/evals/utils/evaluator-form-utils";
|
||||
import { api } from "@/src/utils/api";
|
||||
import { useState } from "react";
|
||||
import {
|
||||
Tooltip,
|
||||
TooltipContent,
|
||||
TooltipTrigger,
|
||||
} from "@/src/components/ui/tooltip";
|
||||
import { compactNumberFormatter } from "@/src/utils/numbers";
|
||||
import { useEvalTargetCount } from "@/src/ee/features/evals/hooks/useEvalTargetCount";
|
||||
|
||||
type ExecutionCountTooltipProps = {
|
||||
projectId: string;
|
||||
item: string;
|
||||
filter: EvalFormType["filter"];
|
||||
};
|
||||
|
||||
export const ExecutionCountTooltip = ({
|
||||
projectId,
|
||||
item,
|
||||
filter,
|
||||
}: ExecutionCountTooltipProps) => {
|
||||
const [isOpen, setIsOpen] = useState(false);
|
||||
|
||||
const globalConfig = api.evals.globalJobConfigs.useQuery({
|
||||
projectId,
|
||||
});
|
||||
|
||||
const { isLoading, totalCount, isTraceTarget } = useEvalTargetCount({
|
||||
projectId,
|
||||
item,
|
||||
filter,
|
||||
enabled: isOpen, // utilize `isOpen` to only query if user hovers over tooltip to avoid unnecessary queries
|
||||
});
|
||||
|
||||
return (
|
||||
<Tooltip open={isOpen} onOpenChange={setIsOpen}>
|
||||
<TooltipTrigger>
|
||||
<InfoIcon className="h-4 w-4" />
|
||||
</TooltipTrigger>
|
||||
<TooltipContent>
|
||||
<div className="text-sm">
|
||||
We execute the evaluation on{" "}
|
||||
{isLoading ? (
|
||||
<Loader className="inline-block h-4 w-4 animate-spin" />
|
||||
) : (
|
||||
compactNumberFormatter(
|
||||
!globalConfig.data ||
|
||||
(totalCount && totalCount < globalConfig.data)
|
||||
? totalCount
|
||||
: globalConfig.data,
|
||||
)
|
||||
)}{" "}
|
||||
{isTraceTarget ? "traces" : "dataset run items"}.
|
||||
</div>
|
||||
</TooltipContent>
|
||||
</Tooltip>
|
||||
);
|
||||
};
|
||||
@@ -1,31 +0,0 @@
|
||||
import { ExternalLink } from "lucide-react";
|
||||
import { CardContent } from "@/src/components/ui/card";
|
||||
import { Card } from "@/src/components/ui/card";
|
||||
import Link from "next/link";
|
||||
|
||||
export function SetupDefaultEvalModelCard({
|
||||
projectId,
|
||||
}: {
|
||||
projectId: string;
|
||||
}) {
|
||||
return (
|
||||
<Card className="mt-2 border-dark-yellow bg-light-yellow">
|
||||
<CardContent className="flex flex-col gap-1">
|
||||
<p className="mt-2 text-sm font-semibold">
|
||||
This evaluator requires a default evaluation model
|
||||
</p>
|
||||
<p className="text-xs text-muted-foreground">
|
||||
Please set up a default evaluation model for your project.
|
||||
</p>
|
||||
<Link
|
||||
href={`/project/${projectId}/evals/default-model`}
|
||||
className="mt-2 flex items-center text-sm text-blue-500 hover:underline"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
Configure default model <ExternalLink className="ml-1" size={14} />
|
||||
</Link>
|
||||
</CardContent>
|
||||
</Card>
|
||||
);
|
||||
}
|
||||
@@ -1,185 +0,0 @@
|
||||
import Page from "@/src/components/layouts/page";
|
||||
import { useRouter } from "next/router";
|
||||
import { useHasProjectAccess } from "@/src/features/rbac/utils/checkProjectAccess";
|
||||
import { useHasEntitlement } from "@/src/features/entitlements/hooks";
|
||||
import { SupportOrUpgradePage } from "@/src/ee/features/billing/components/SupportOrUpgradePage";
|
||||
import { ModelParameters } from "@/src/components/ModelParameters";
|
||||
import { CardContent } from "@/src/components/ui/card";
|
||||
import { Card } from "@/src/components/ui/card";
|
||||
import { useModelParams } from "@/src/ee/features/playground/page/hooks/useModelParams";
|
||||
import { Button } from "@/src/components/ui/button";
|
||||
import { api } from "@/src/utils/api";
|
||||
import { showSuccessToast } from "@/src/features/notifications/showSuccessToast";
|
||||
import { Skeleton } from "@/src/components/ui/skeleton";
|
||||
import { useEvaluationModel } from "@/src/ee/features/evals/hooks/useEvaluationModel";
|
||||
import { DeleteEvaluationModelButton } from "@/src/components/deleteButton";
|
||||
|
||||
export default function DefaultEvaluationModelPage() {
|
||||
const router = useRouter();
|
||||
const projectId = router.query.projectId as string;
|
||||
const utils = api.useUtils();
|
||||
|
||||
const hasEntitlement = useHasEntitlement("model-based-evaluations");
|
||||
const hasWriteAccess = useHasProjectAccess({
|
||||
projectId,
|
||||
scope: "evalDefaultModel:CUD",
|
||||
});
|
||||
|
||||
const hasReadAccess = useHasProjectAccess({
|
||||
projectId,
|
||||
scope: "evalDefaultModel:read",
|
||||
});
|
||||
|
||||
const {
|
||||
modelParams,
|
||||
setModelParams,
|
||||
updateModelParamValue,
|
||||
setModelParamEnabled,
|
||||
availableModels,
|
||||
availableProviders,
|
||||
} = useModelParams();
|
||||
|
||||
const { selectedModel, isDefaultModelLoading } = useEvaluationModel(
|
||||
projectId,
|
||||
setModelParams,
|
||||
);
|
||||
|
||||
const { mutate: upsertDefaultModel } =
|
||||
api.defaultLlmModel.upsertDefaultModel.useMutation({
|
||||
onSuccess: () => {
|
||||
showSuccessToast({
|
||||
title: "Default evaluation model updated",
|
||||
description: "All running evaluators will use the new model.",
|
||||
});
|
||||
|
||||
utils.defaultLlmModel.fetchDefaultModel.invalidate({ projectId });
|
||||
},
|
||||
});
|
||||
|
||||
if (isDefaultModelLoading) {
|
||||
return <Skeleton className="h-[500px] w-full" />;
|
||||
}
|
||||
|
||||
if (!hasReadAccess || !hasEntitlement) {
|
||||
return <SupportOrUpgradePage />;
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<Page
|
||||
withPadding
|
||||
headerProps={{
|
||||
title: "Default Evaluation Model",
|
||||
help: {
|
||||
description:
|
||||
"Configure a default evaluation model for your project.",
|
||||
href: "https://langfuse.com/docs/scores/model-based-evals",
|
||||
},
|
||||
breadcrumb: [
|
||||
{
|
||||
name: "Evaluator Library",
|
||||
href: `/project/${projectId}/evals/templates`,
|
||||
},
|
||||
],
|
||||
}}
|
||||
>
|
||||
{selectedModel ? (
|
||||
<div className="flex flex-col gap-6">
|
||||
<Card className="border-dark-green bg-light-green">
|
||||
<CardContent className="flex flex-col gap-1">
|
||||
<p className="mt-2 text-sm font-semibold">
|
||||
Default evaluation model selected
|
||||
</p>
|
||||
<p className="text-xs text-muted-foreground">
|
||||
This project will use the default evaluation model:
|
||||
<span className="font-medium">
|
||||
{" "}
|
||||
{selectedModel.provider} / {selectedModel.model}
|
||||
</span>
|
||||
</p>
|
||||
</CardContent>
|
||||
</Card>
|
||||
</div>
|
||||
) : (
|
||||
<div className="flex flex-col gap-6">
|
||||
<Card className="border-dark-yellow bg-light-yellow">
|
||||
<CardContent className="flex flex-col gap-1">
|
||||
<p className="mt-2 text-sm font-semibold">
|
||||
No default evaluation model selected
|
||||
</p>
|
||||
<p className="text-xs text-muted-foreground">
|
||||
Select a model to use as the default evaluation model for your
|
||||
project.
|
||||
</p>
|
||||
</CardContent>
|
||||
</Card>
|
||||
</div>
|
||||
)}
|
||||
<Card className="mt-3 flex flex-col gap-6">
|
||||
<CardContent>
|
||||
<ModelParameters
|
||||
{...{
|
||||
modelParams,
|
||||
availableModels,
|
||||
availableProviders,
|
||||
updateModelParamValue,
|
||||
setModelParamEnabled,
|
||||
}}
|
||||
formDisabled={!hasWriteAccess}
|
||||
/>
|
||||
<div className="my-2 text-xs text-muted-foreground">
|
||||
Select a model which supports function calling.
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
{selectedModel ? (
|
||||
<div className="mt-2 flex justify-end gap-2">
|
||||
<DeleteEvaluationModelButton
|
||||
projectId={projectId}
|
||||
scope="evalDefaultModel:CUD"
|
||||
/>
|
||||
<Button
|
||||
disabled={!hasWriteAccess}
|
||||
onClick={() => {
|
||||
upsertDefaultModel({
|
||||
projectId,
|
||||
provider: modelParams.provider.value,
|
||||
adapter: modelParams.adapter.value,
|
||||
model: modelParams.model.value,
|
||||
modelParams: {
|
||||
max_tokens: modelParams.max_tokens.value,
|
||||
temperature: modelParams.temperature.value,
|
||||
top_p: modelParams.top_p.value,
|
||||
},
|
||||
});
|
||||
}}
|
||||
>
|
||||
Update as default
|
||||
</Button>
|
||||
</div>
|
||||
) : (
|
||||
<div className="mt-3 flex justify-end">
|
||||
<Button
|
||||
disabled={!hasWriteAccess || !modelParams.provider.value}
|
||||
onClick={() => {
|
||||
upsertDefaultModel({
|
||||
projectId,
|
||||
provider: modelParams.provider.value,
|
||||
adapter: modelParams.adapter.value,
|
||||
model: modelParams.model.value,
|
||||
modelParams: {
|
||||
max_tokens: modelParams.max_tokens.value,
|
||||
temperature: modelParams.temperature.value,
|
||||
top_p: modelParams.top_p.value,
|
||||
},
|
||||
});
|
||||
}}
|
||||
>
|
||||
Set as default
|
||||
</Button>
|
||||
</div>
|
||||
)}
|
||||
</Page>
|
||||
</>
|
||||
);
|
||||
}
|
||||
+2
-4
@@ -1,9 +1,8 @@
|
||||
import { Tabs, TabsList, TabsTrigger } from "@/src/components/ui/tabs";
|
||||
import useSessionStorage from "@/src/components/useSessionStorage";
|
||||
import { SupportOrUpgradePage } from "@/src/ee/features/billing/components/SupportOrUpgradePage";
|
||||
import { useHasEntitlement } from "@/src/features/entitlements/hooks";
|
||||
import { useHasProjectAccess } from "@/src/features/rbac/utils/checkProjectAccess";
|
||||
import { AnnotationQueueItemPage } from "@/src/ee/features/annotation-queues/components/AnnotationQueueItemPage";
|
||||
import { AnnotationQueueItemPage } from "@/src/features/annotation-queues/components/AnnotationQueueItemPage";
|
||||
import { api } from "@/src/utils/api";
|
||||
import { Goal, Network } from "lucide-react";
|
||||
import Page from "@/src/components/layouts/page";
|
||||
@@ -21,7 +20,6 @@ export const AnnotationQueuesItem = ({
|
||||
projectId,
|
||||
scope: "annotationQueues:read",
|
||||
});
|
||||
const hasEntitlement = useHasEntitlement("annotation-queues");
|
||||
|
||||
const queue = api.annotationQueues.byId.useQuery(
|
||||
{
|
||||
@@ -43,7 +41,7 @@ export const AnnotationQueuesItem = ({
|
||||
"hideTree",
|
||||
);
|
||||
|
||||
if (!hasAccess || !hasEntitlement) return <SupportOrUpgradePage />;
|
||||
if (!hasAccess) return <SupportOrUpgradePage />;
|
||||
|
||||
return (
|
||||
<Page
|
||||
+2
-2
@@ -7,7 +7,7 @@ import useColumnVisibility from "@/src/features/column-visibility/hooks/useColum
|
||||
import { DataTableToolbar } from "@/src/components/table/data-table-toolbar";
|
||||
import { useRowHeightLocalStorage } from "@/src/components/table/data-table-row-height-switch";
|
||||
import useColumnOrder from "@/src/features/column-visibility/hooks/useColumnOrder";
|
||||
import { CreateOrEditAnnotationQueueButton } from "@/src/ee/features/annotation-queues/components/CreateOrEditAnnotationQueueButton";
|
||||
import { CreateOrEditAnnotationQueueButton } from "@/src/features/annotation-queues/components/CreateOrEditAnnotationQueueButton";
|
||||
import { type ScoreDataType } from "@langfuse/shared";
|
||||
import { getScoreDataTypeIcon } from "@/src/features/scores/components/ScoreDetailColumnHelpers";
|
||||
import {
|
||||
@@ -22,7 +22,7 @@ import { cn } from "@/src/utils/tailwind";
|
||||
import TableLink from "@/src/components/table/table-link";
|
||||
import Link from "next/link";
|
||||
import { useHasProjectAccess } from "@/src/features/rbac/utils/checkProjectAccess";
|
||||
import { DeleteAnnotationQueueButton } from "@/src/ee/features/annotation-queues/components/DeleteAnnotationQueueButton";
|
||||
import { DeleteAnnotationQueueButton } from "@/src/features/annotation-queues/components/DeleteAnnotationQueueButton";
|
||||
|
||||
type RowData = {
|
||||
key: {
|
||||
+1
-6
@@ -33,10 +33,7 @@ import { getScoreDataTypeIcon } from "@/src/features/scores/components/ScoreDeta
|
||||
import { MultiSelectKeyValues } from "@/src/features/scores/components/multi-select-key-values";
|
||||
import { useRouter } from "next/router";
|
||||
import { usePostHogClientCapture } from "@/src/features/posthog-analytics/usePostHogClientCapture";
|
||||
import {
|
||||
useHasEntitlement,
|
||||
useEntitlementLimit,
|
||||
} from "@/src/features/entitlements/hooks";
|
||||
import { useEntitlementLimit } from "@/src/features/entitlements/hooks";
|
||||
import { ActionButton } from "@/src/components/ActionButton";
|
||||
import { DropdownMenuItem } from "@/src/components/ui/dropdown-menu";
|
||||
import { useUniqueNameValidation } from "@/src/hooks/useUniqueNameValidation";
|
||||
@@ -57,7 +54,6 @@ export const CreateOrEditAnnotationQueueButton = ({
|
||||
projectId: projectId,
|
||||
scope: "annotationQueues:CUD",
|
||||
});
|
||||
const hasEntitlement = useHasEntitlement("annotation-queues");
|
||||
const queueLimit = useEntitlementLimit("annotation-queue-count");
|
||||
const router = useRouter();
|
||||
const capture = usePostHogClientCapture();
|
||||
@@ -186,7 +182,6 @@ export const CreateOrEditAnnotationQueueButton = ({
|
||||
)
|
||||
}
|
||||
hasAccess={hasAccess}
|
||||
hasEntitlement={hasEntitlement}
|
||||
limitValue={queueCountData.data}
|
||||
limit={queueLimit}
|
||||
size={size}
|
||||
+4
-5
@@ -1,15 +1,14 @@
|
||||
import { api } from "@/src/utils/api";
|
||||
import { useRouter } from "next/router";
|
||||
import { AnnotationQueueItemsTable } from "@/src/ee/features/annotation-queues/components/AnnotationQueueItemsTable";
|
||||
import { AnnotationQueueItemsTable } from "@/src/features/annotation-queues/components/AnnotationQueueItemsTable";
|
||||
import { CardDescription } from "@/src/components/ui/card";
|
||||
import { Button } from "@/src/components/ui/button";
|
||||
import { ClipboardPen, Lock } from "lucide-react";
|
||||
import { Badge } from "@/src/components/ui/badge";
|
||||
import { getScoreDataTypeIcon } from "@/src/features/scores/components/ScoreDetailColumnHelpers";
|
||||
import Link from "next/link";
|
||||
import { CreateOrEditAnnotationQueueButton } from "@/src/ee/features/annotation-queues/components/CreateOrEditAnnotationQueueButton";
|
||||
import { CreateOrEditAnnotationQueueButton } from "@/src/features/annotation-queues/components/CreateOrEditAnnotationQueueButton";
|
||||
import { useHasProjectAccess } from "@/src/features/rbac/utils/checkProjectAccess";
|
||||
import { useHasEntitlement } from "@/src/features/entitlements/hooks";
|
||||
import { SupportOrUpgradePage } from "@/src/ee/features/billing/components/SupportOrUpgradePage";
|
||||
import { Skeleton } from "@/src/components/ui/skeleton";
|
||||
import Page from "@/src/components/layouts/page";
|
||||
@@ -39,8 +38,8 @@ export default function QueueItems() {
|
||||
projectId,
|
||||
scope: "annotationQueues:CUD",
|
||||
});
|
||||
const hasEntitlement = useHasEntitlement("annotation-queues");
|
||||
if (!hasReadAccess || !hasEntitlement) return <SupportOrUpgradePage />;
|
||||
|
||||
if (!hasReadAccess) return <SupportOrUpgradePage />;
|
||||
|
||||
return (
|
||||
<Page
|
||||
+4
-6
@@ -1,12 +1,11 @@
|
||||
import { useRouter } from "next/router";
|
||||
import { AnnotationQueuesTable } from "@/src/ee/features/annotation-queues/components/AnnotationQueuesTable";
|
||||
import { AnnotationQueuesTable } from "@/src/features/annotation-queues/components/AnnotationQueuesTable";
|
||||
import { useHasProjectAccess } from "@/src/features/rbac/utils/checkProjectAccess";
|
||||
import { useHasEntitlement } from "@/src/features/entitlements/hooks";
|
||||
import { SupportOrUpgradePage } from "@/src/ee/features/billing/components/SupportOrUpgradePage";
|
||||
import Page from "@/src/components/layouts/page";
|
||||
import { AnnotationQueuesOnboarding } from "@/src/components/onboarding/AnnotationQueuesOnboarding";
|
||||
import { api } from "@/src/utils/api";
|
||||
import { CreateOrEditAnnotationQueueButton } from "@/src/ee/features/annotation-queues/components/CreateOrEditAnnotationQueueButton";
|
||||
import { CreateOrEditAnnotationQueueButton } from "@/src/features/annotation-queues/components/CreateOrEditAnnotationQueueButton";
|
||||
|
||||
export default function AnnotationQueues() {
|
||||
const router = useRouter();
|
||||
@@ -15,13 +14,12 @@ export default function AnnotationQueues() {
|
||||
projectId: projectId,
|
||||
scope: "annotationQueues:read",
|
||||
});
|
||||
const hasEntitlement = useHasEntitlement("annotation-queues");
|
||||
|
||||
// Check if the user has any annotation queues
|
||||
const { data: hasAnyQueue, isLoading } = api.annotationQueues.hasAny.useQuery(
|
||||
{ projectId },
|
||||
{
|
||||
enabled: !!projectId && hasEntitlement,
|
||||
enabled: !!projectId,
|
||||
trpc: {
|
||||
context: {
|
||||
skipBatch: true,
|
||||
@@ -33,7 +31,7 @@ export default function AnnotationQueues() {
|
||||
|
||||
const showOnboarding = !isLoading && !hasAnyQueue;
|
||||
|
||||
if (!hasAccess || !hasEntitlement) return <SupportOrUpgradePage />;
|
||||
if (!hasAccess) return <SupportOrUpgradePage />;
|
||||
|
||||
return (
|
||||
<Page
|
||||
-37
@@ -1,5 +1,4 @@
|
||||
import { auditLog } from "@/src/features/audit-logs/auditLog";
|
||||
import { throwIfNoEntitlement } from "@/src/features/entitlements/server/hasEntitlement";
|
||||
import { throwIfNoProjectAccess } from "@/src/features/rbac/utils/checkProjectAccess";
|
||||
import { createBatchActionJob } from "@/src/features/table/server/createBatchActionJob";
|
||||
import {
|
||||
@@ -42,12 +41,6 @@ export const queueItemRouter = createTRPCRouter({
|
||||
)
|
||||
.query(async ({ input, ctx }) => {
|
||||
try {
|
||||
throwIfNoEntitlement({
|
||||
entitlement: "annotation-queues",
|
||||
projectId: input.projectId,
|
||||
sessionUser: ctx.session.user,
|
||||
});
|
||||
|
||||
throwIfNoProjectAccess({
|
||||
session: ctx.session,
|
||||
projectId: input.projectId,
|
||||
@@ -114,12 +107,6 @@ export const queueItemRouter = createTRPCRouter({
|
||||
)
|
||||
.query(async ({ input, ctx }) => {
|
||||
try {
|
||||
throwIfNoEntitlement({
|
||||
entitlement: "annotation-queues",
|
||||
projectId: input.projectId,
|
||||
sessionUser: ctx.session.user,
|
||||
});
|
||||
|
||||
throwIfNoProjectAccess({
|
||||
session: ctx.session,
|
||||
projectId: input.projectId,
|
||||
@@ -209,12 +196,6 @@ export const queueItemRouter = createTRPCRouter({
|
||||
)
|
||||
.query(async ({ input, ctx }) => {
|
||||
try {
|
||||
throwIfNoEntitlement({
|
||||
entitlement: "annotation-queues",
|
||||
projectId: input.projectId,
|
||||
sessionUser: ctx.session.user,
|
||||
});
|
||||
|
||||
throwIfNoProjectAccess({
|
||||
session: ctx.session,
|
||||
projectId: input.projectId,
|
||||
@@ -258,12 +239,6 @@ export const queueItemRouter = createTRPCRouter({
|
||||
)
|
||||
.mutation(async ({ input, ctx }) => {
|
||||
try {
|
||||
throwIfNoEntitlement({
|
||||
entitlement: "annotation-queues",
|
||||
projectId: input.projectId,
|
||||
sessionUser: ctx.session.user,
|
||||
});
|
||||
|
||||
throwIfNoProjectAccess({
|
||||
session: ctx.session,
|
||||
projectId: input.projectId,
|
||||
@@ -354,12 +329,6 @@ export const queueItemRouter = createTRPCRouter({
|
||||
)
|
||||
.mutation(async ({ input, ctx }) => {
|
||||
try {
|
||||
throwIfNoEntitlement({
|
||||
entitlement: "annotation-queues",
|
||||
projectId: input.projectId,
|
||||
sessionUser: ctx.session.user,
|
||||
});
|
||||
|
||||
throwIfNoProjectAccess({
|
||||
session: ctx.session,
|
||||
projectId: input.projectId,
|
||||
@@ -417,12 +386,6 @@ export const queueItemRouter = createTRPCRouter({
|
||||
)
|
||||
.mutation(async ({ input, ctx }) => {
|
||||
try {
|
||||
throwIfNoEntitlement({
|
||||
entitlement: "annotation-queues",
|
||||
projectId: input.projectId,
|
||||
sessionUser: ctx.session.user,
|
||||
});
|
||||
|
||||
throwIfNoProjectAccess({
|
||||
session: ctx.session,
|
||||
projectId: input.projectId,
|
||||
-55
@@ -1,5 +1,4 @@
|
||||
import { auditLog } from "@/src/features/audit-logs/auditLog";
|
||||
import { throwIfNoEntitlement } from "@/src/features/entitlements/server/hasEntitlement";
|
||||
import { throwIfNoProjectAccess } from "@/src/features/rbac/utils/checkProjectAccess";
|
||||
import {
|
||||
createTRPCRouter,
|
||||
@@ -26,12 +25,6 @@ export const queueRouter = createTRPCRouter({
|
||||
}),
|
||||
)
|
||||
.query(async ({ input, ctx }) => {
|
||||
throwIfNoEntitlement({
|
||||
entitlement: "annotation-queues",
|
||||
projectId: input.projectId,
|
||||
sessionUser: ctx.session.user,
|
||||
});
|
||||
|
||||
throwIfNoProjectAccess({
|
||||
session: ctx.session,
|
||||
projectId: input.projectId,
|
||||
@@ -57,12 +50,6 @@ export const queueRouter = createTRPCRouter({
|
||||
)
|
||||
.query(async ({ input, ctx }) => {
|
||||
try {
|
||||
throwIfNoEntitlement({
|
||||
entitlement: "annotation-queues",
|
||||
projectId: input.projectId,
|
||||
sessionUser: ctx.session.user,
|
||||
});
|
||||
|
||||
throwIfNoProjectAccess({
|
||||
session: ctx.session,
|
||||
projectId: input.projectId,
|
||||
@@ -147,12 +134,6 @@ export const queueRouter = createTRPCRouter({
|
||||
)
|
||||
.query(async ({ input, ctx }) => {
|
||||
try {
|
||||
throwIfNoEntitlement({
|
||||
entitlement: "annotation-queues",
|
||||
projectId: input.projectId,
|
||||
sessionUser: ctx.session.user,
|
||||
});
|
||||
|
||||
const queueNamesAndIds = await ctx.prisma.annotationQueue.findMany({
|
||||
where: {
|
||||
projectId: input.projectId,
|
||||
@@ -197,12 +178,6 @@ export const queueRouter = createTRPCRouter({
|
||||
.input(z.object({ queueId: z.string(), projectId: z.string() }))
|
||||
.query(async ({ input, ctx }) => {
|
||||
try {
|
||||
throwIfNoEntitlement({
|
||||
entitlement: "annotation-queues",
|
||||
projectId: input.projectId,
|
||||
sessionUser: ctx.session.user,
|
||||
});
|
||||
|
||||
throwIfNoProjectAccess({
|
||||
session: ctx.session,
|
||||
projectId: input.projectId,
|
||||
@@ -247,12 +222,6 @@ export const queueRouter = createTRPCRouter({
|
||||
)
|
||||
.query(async ({ input, ctx }) => {
|
||||
try {
|
||||
throwIfNoEntitlement({
|
||||
entitlement: "annotation-queues",
|
||||
projectId: input.projectId,
|
||||
sessionUser: ctx.session.user,
|
||||
});
|
||||
|
||||
throwIfNoProjectAccess({
|
||||
session: ctx.session,
|
||||
projectId: input.projectId,
|
||||
@@ -316,12 +285,6 @@ export const queueRouter = createTRPCRouter({
|
||||
)
|
||||
.mutation(async ({ input, ctx }) => {
|
||||
try {
|
||||
throwIfNoEntitlement({
|
||||
entitlement: "annotation-queues",
|
||||
projectId: input.projectId,
|
||||
sessionUser: ctx.session.user,
|
||||
});
|
||||
|
||||
throwIfNoProjectAccess({
|
||||
session: ctx.session,
|
||||
projectId: input.projectId,
|
||||
@@ -402,12 +365,6 @@ export const queueRouter = createTRPCRouter({
|
||||
)
|
||||
.mutation(async ({ input, ctx }) => {
|
||||
try {
|
||||
throwIfNoEntitlement({
|
||||
entitlement: "annotation-queues",
|
||||
projectId: input.projectId,
|
||||
sessionUser: ctx.session.user,
|
||||
});
|
||||
|
||||
throwIfNoProjectAccess({
|
||||
session: ctx.session,
|
||||
projectId: input.projectId,
|
||||
@@ -459,12 +416,6 @@ export const queueRouter = createTRPCRouter({
|
||||
.input(z.object({ queueId: z.string(), projectId: z.string() }))
|
||||
.mutation(async ({ input, ctx }) => {
|
||||
try {
|
||||
throwIfNoEntitlement({
|
||||
entitlement: "annotation-queues",
|
||||
projectId: input.projectId,
|
||||
sessionUser: ctx.session.user,
|
||||
});
|
||||
|
||||
throwIfNoProjectAccess({
|
||||
session: ctx.session,
|
||||
projectId: input.projectId,
|
||||
@@ -504,12 +455,6 @@ export const queueRouter = createTRPCRouter({
|
||||
)
|
||||
.mutation(async ({ input, ctx }) => {
|
||||
try {
|
||||
throwIfNoEntitlement({
|
||||
entitlement: "annotation-queues",
|
||||
projectId: input.projectId,
|
||||
sessionUser: ctx.session.user,
|
||||
});
|
||||
|
||||
throwIfNoProjectAccess({
|
||||
session: ctx.session,
|
||||
projectId: input.projectId,
|
||||
@@ -9,19 +9,24 @@ import {
|
||||
import { encrypt } from "@langfuse/shared/encryption";
|
||||
import { blobStorageIntegrationFormSchema } from "@/src/features/blobstorage-integration/types";
|
||||
import { TRPCError } from "@trpc/server";
|
||||
import { throwIfNoEntitlement } from "@/src/features/entitlements/server/hasEntitlement";
|
||||
import { logger } from "@langfuse/shared/src/server";
|
||||
import { type BlobStorageIntegration } from "@langfuse/shared";
|
||||
import {
|
||||
logger,
|
||||
BlobStorageIntegrationProcessingQueue,
|
||||
QueueJobs,
|
||||
StorageServiceFactory,
|
||||
} from "@langfuse/shared/src/server";
|
||||
import { randomUUID } from "crypto";
|
||||
import { decrypt } from "@langfuse/shared/encryption";
|
||||
import {
|
||||
type BlobStorageIntegration,
|
||||
BlobStorageIntegrationType,
|
||||
} from "@langfuse/shared";
|
||||
import { env } from "@/src/env.mjs";
|
||||
|
||||
export const blobStorageIntegrationRouter = createTRPCRouter({
|
||||
get: protectedProjectProcedure
|
||||
.input(z.object({ projectId: z.string() }))
|
||||
.query(async ({ input, ctx }) => {
|
||||
throwIfNoEntitlement({
|
||||
entitlement: "integration-blobstorage",
|
||||
sessionUser: ctx.session.user,
|
||||
projectId: input.projectId,
|
||||
});
|
||||
throwIfNoProjectAccess({
|
||||
session: ctx.session,
|
||||
projectId: input.projectId,
|
||||
@@ -55,11 +60,6 @@ export const blobStorageIntegrationRouter = createTRPCRouter({
|
||||
.input(blobStorageIntegrationFormSchema.extend({ projectId: z.string() }))
|
||||
.mutation(async ({ input, ctx }) => {
|
||||
try {
|
||||
throwIfNoEntitlement({
|
||||
entitlement: "integration-blobstorage",
|
||||
sessionUser: ctx.session.user,
|
||||
projectId: input.projectId,
|
||||
});
|
||||
throwIfNoProjectAccess({
|
||||
session: ctx.session,
|
||||
projectId: input.projectId,
|
||||
@@ -87,6 +87,19 @@ export const blobStorageIntegrationRouter = createTRPCRouter({
|
||||
fileType,
|
||||
} = input;
|
||||
|
||||
const isSelfHosted = !env.NEXT_PUBLIC_LANGFUSE_CLOUD_REGION;
|
||||
const canUseHostCredentials =
|
||||
isSelfHosted && type === BlobStorageIntegrationType.S3;
|
||||
const isUsingHostCredentials =
|
||||
canUseHostCredentials && (!accessKeyId || !secretAccessKey);
|
||||
|
||||
if (!canUseHostCredentials && !accessKeyId) {
|
||||
throw new TRPCError({
|
||||
code: "BAD_REQUEST",
|
||||
message: "Access Key ID and Secret Access Key are required",
|
||||
});
|
||||
}
|
||||
|
||||
const data: Partial<BlobStorageIntegration> = {
|
||||
type,
|
||||
bucketName,
|
||||
@@ -112,7 +125,6 @@ export const blobStorageIntegrationRouter = createTRPCRouter({
|
||||
);
|
||||
|
||||
if (existingConfig) {
|
||||
// Record exists, perform update
|
||||
if (secretAccessKey) {
|
||||
data.secretAccessKey = encrypt(secretAccessKey);
|
||||
}
|
||||
@@ -125,11 +137,11 @@ export const blobStorageIntegrationRouter = createTRPCRouter({
|
||||
});
|
||||
} else {
|
||||
// Record doesn't exist, perform create
|
||||
// For create, secretAccessKey is mandatory
|
||||
if (!secretAccessKey) {
|
||||
if (!isUsingHostCredentials && !secretAccessKey) {
|
||||
throw new TRPCError({
|
||||
code: "BAD_REQUEST",
|
||||
message: "Secret access key is required for new configuration",
|
||||
message:
|
||||
"Secret access key is required for new configuration when not using host credentials",
|
||||
});
|
||||
}
|
||||
|
||||
@@ -138,12 +150,17 @@ export const blobStorageIntegrationRouter = createTRPCRouter({
|
||||
...(data as BlobStorageIntegration),
|
||||
projectId: input.projectId,
|
||||
accessKeyId,
|
||||
secretAccessKey: encrypt(secretAccessKey),
|
||||
secretAccessKey: secretAccessKey
|
||||
? encrypt(secretAccessKey)
|
||||
: undefined,
|
||||
},
|
||||
});
|
||||
}
|
||||
});
|
||||
} catch (e) {
|
||||
if (e instanceof TRPCError) {
|
||||
throw e;
|
||||
}
|
||||
logger.error(`Failed to update blob storage integration`, e);
|
||||
throw new TRPCError({
|
||||
code: "INTERNAL_SERVER_ERROR",
|
||||
@@ -155,11 +172,6 @@ export const blobStorageIntegrationRouter = createTRPCRouter({
|
||||
.input(z.object({ projectId: z.string() }))
|
||||
.mutation(async ({ input, ctx }) => {
|
||||
try {
|
||||
throwIfNoEntitlement({
|
||||
entitlement: "integration-blobstorage",
|
||||
sessionUser: ctx.session.user,
|
||||
projectId: input.projectId,
|
||||
});
|
||||
throwIfNoProjectAccess({
|
||||
session: ctx.session,
|
||||
projectId: input.projectId,
|
||||
@@ -185,4 +197,188 @@ export const blobStorageIntegrationRouter = createTRPCRouter({
|
||||
});
|
||||
}
|
||||
}),
|
||||
|
||||
runNow: protectedProjectProcedure
|
||||
.input(z.object({ projectId: z.string() }))
|
||||
.mutation(async ({ input, ctx }) => {
|
||||
try {
|
||||
throwIfNoProjectAccess({
|
||||
session: ctx.session,
|
||||
projectId: input.projectId,
|
||||
scope: "integrations:CRUD",
|
||||
});
|
||||
|
||||
// Check if integration exists and is enabled
|
||||
const integration = await ctx.prisma.blobStorageIntegration.findUnique({
|
||||
where: {
|
||||
projectId: input.projectId,
|
||||
},
|
||||
});
|
||||
|
||||
if (!integration) {
|
||||
throw new TRPCError({
|
||||
code: "NOT_FOUND",
|
||||
message: "Blob storage integration not found for this project",
|
||||
});
|
||||
}
|
||||
|
||||
if (!integration.enabled) {
|
||||
throw new TRPCError({
|
||||
code: "BAD_REQUEST",
|
||||
message: "Blob storage integration is disabled",
|
||||
});
|
||||
}
|
||||
|
||||
// Get the processing queue
|
||||
const blobStorageIntegrationProcessingQueue =
|
||||
BlobStorageIntegrationProcessingQueue.getInstance();
|
||||
if (!blobStorageIntegrationProcessingQueue) {
|
||||
throw new TRPCError({
|
||||
code: "INTERNAL_SERVER_ERROR",
|
||||
message: "BlobStorageIntegrationProcessingQueue not initialized",
|
||||
});
|
||||
}
|
||||
|
||||
// Create a unique job ID for manual runs to avoid conflicts
|
||||
const jobId = `${input.projectId}-manual-${new Date().toISOString()}`;
|
||||
|
||||
// Enqueue the processing job
|
||||
await blobStorageIntegrationProcessingQueue.add(
|
||||
QueueJobs.BlobStorageIntegrationProcessingJob,
|
||||
{
|
||||
id: randomUUID(),
|
||||
name: QueueJobs.BlobStorageIntegrationProcessingJob,
|
||||
timestamp: new Date(),
|
||||
payload: {
|
||||
projectId: input.projectId,
|
||||
},
|
||||
},
|
||||
{
|
||||
jobId,
|
||||
},
|
||||
);
|
||||
|
||||
logger.info(
|
||||
`Manual blob storage integration job queued for project ${input.projectId}`,
|
||||
);
|
||||
|
||||
return { success: true, jobId };
|
||||
} catch (e) {
|
||||
logger.error(`Failed to trigger blob storage integration run`, e);
|
||||
if (e instanceof TRPCError) {
|
||||
throw e;
|
||||
}
|
||||
throw new TRPCError({
|
||||
code: "INTERNAL_SERVER_ERROR",
|
||||
message: "Failed to trigger blob storage integration run",
|
||||
});
|
||||
}
|
||||
}),
|
||||
|
||||
validate: protectedProjectProcedure
|
||||
.input(z.object({ projectId: z.string() }))
|
||||
.mutation(async ({ input, ctx }) => {
|
||||
try {
|
||||
throwIfNoProjectAccess({
|
||||
session: ctx.session,
|
||||
projectId: input.projectId,
|
||||
scope: "integrations:CRUD",
|
||||
});
|
||||
|
||||
// Get persisted configuration
|
||||
const integration = await ctx.prisma.blobStorageIntegration.findUnique({
|
||||
where: {
|
||||
projectId: input.projectId,
|
||||
},
|
||||
});
|
||||
|
||||
if (!integration) {
|
||||
throw new TRPCError({
|
||||
code: "NOT_FOUND",
|
||||
message:
|
||||
"Blob storage integration not found for this project. Please save your configuration first.",
|
||||
});
|
||||
}
|
||||
|
||||
// Extract configuration from persisted data
|
||||
const {
|
||||
type,
|
||||
bucketName,
|
||||
endpoint,
|
||||
region,
|
||||
accessKeyId,
|
||||
secretAccessKey: encryptedSecretAccessKey,
|
||||
prefix,
|
||||
forcePathStyle,
|
||||
} = integration;
|
||||
|
||||
const secretAccessKey = encryptedSecretAccessKey
|
||||
? decrypt(encryptedSecretAccessKey)
|
||||
: undefined;
|
||||
|
||||
// Create storage service with provided configuration
|
||||
const storageService = StorageServiceFactory.getInstance({
|
||||
accessKeyId: accessKeyId || undefined,
|
||||
secretAccessKey,
|
||||
bucketName,
|
||||
endpoint: endpoint || undefined,
|
||||
region: region || undefined,
|
||||
forcePathStyle: forcePathStyle || false,
|
||||
useAzureBlob: type === BlobStorageIntegrationType.AZURE_BLOB_STORAGE,
|
||||
useGoogleCloudStorage: false, // Not supported in blob storage integration
|
||||
googleCloudCredentials: undefined,
|
||||
awsSse: undefined,
|
||||
awsSseKmsKeyId: undefined,
|
||||
externalEndpoint: undefined,
|
||||
});
|
||||
|
||||
// Create a test file
|
||||
const timestamp = new Date().toISOString().replace(/[:.]/g, "-");
|
||||
const testFileName = `${prefix || ""}langfuse-validation-test-${timestamp}.txt`;
|
||||
const testContent = `Langfuse blob storage validation test
|
||||
Project ID: ${input.projectId}
|
||||
Timestamp: ${new Date().toISOString()}
|
||||
Configuration: ${type} storage
|
||||
This file can be safely deleted.`;
|
||||
|
||||
// Upload the test file
|
||||
const result = await storageService.uploadFile({
|
||||
fileName: testFileName,
|
||||
fileType: "text/plain",
|
||||
data: testContent,
|
||||
expiresInSeconds: 3600, // 1 hour
|
||||
});
|
||||
|
||||
logger.info(
|
||||
`Blob storage validation successful for project ${input.projectId}`,
|
||||
);
|
||||
|
||||
return {
|
||||
success: true,
|
||||
message: "Validation successful! Test file uploaded.",
|
||||
testFileName,
|
||||
signedUrl: result.signedUrl,
|
||||
};
|
||||
} catch (e) {
|
||||
logger.error(
|
||||
`Blob storage validation failed for project ${input.projectId}`,
|
||||
e,
|
||||
);
|
||||
|
||||
// Extract meaningful error message
|
||||
let errorMessage = "Unknown error occurred during validation";
|
||||
if (e instanceof Error) {
|
||||
errorMessage = e.message;
|
||||
}
|
||||
|
||||
if (e instanceof TRPCError) {
|
||||
throw e;
|
||||
}
|
||||
|
||||
throw new TRPCError({
|
||||
code: "BAD_REQUEST",
|
||||
message: `Validation failed: ${errorMessage}`,
|
||||
});
|
||||
}
|
||||
}),
|
||||
});
|
||||
|
||||
@@ -9,11 +9,8 @@ export const blobStorageIntegrationFormSchema = z.object({
|
||||
bucketName: z.string().min(1, { message: "Bucket name is required" }),
|
||||
endpoint: z.string().url().optional().nullable(),
|
||||
region: z.string().default("auto"),
|
||||
accessKeyId: z.string().min(1, { message: "Access key ID is required" }),
|
||||
secretAccessKey: z
|
||||
.string()
|
||||
.min(1, { message: "Secret access key is required" })
|
||||
.nullable(), // Only required on create
|
||||
accessKeyId: z.string().optional(),
|
||||
secretAccessKey: z.string().nullable().optional(),
|
||||
prefix: z
|
||||
.string()
|
||||
.refine((value) => !value || value === "" || value.endsWith("/"), {
|
||||
|
||||
@@ -344,7 +344,7 @@ export const datasetRouter = createTRPCRouter({
|
||||
expectedOutput: true,
|
||||
metadata: true,
|
||||
},
|
||||
orderBy: { createdAt: "desc" },
|
||||
orderBy: [{ createdAt: "desc" }, { id: "desc" }],
|
||||
take: input.limit,
|
||||
skip: input.page * input.limit,
|
||||
});
|
||||
@@ -866,7 +866,8 @@ export const datasetRouter = createTRPCRouter({
|
||||
dri.project_id = ${input.projectId}
|
||||
${filterQuery}
|
||||
ORDER BY
|
||||
di.created_at DESC
|
||||
di.created_at DESC,
|
||||
di.id DESC
|
||||
LIMIT ${input.limit}
|
||||
OFFSET ${input.page * input.limit}
|
||||
`;
|
||||
|
||||
@@ -3,38 +3,25 @@ import { type Plan } from "@langfuse/shared";
|
||||
// Entitlements: Binary feature access
|
||||
const entitlements = [
|
||||
// features
|
||||
"playground",
|
||||
"model-based-evaluations",
|
||||
"rbac-project-roles",
|
||||
"cloud-billing",
|
||||
"cloud-multi-tenant-sso",
|
||||
"integration-posthog",
|
||||
"integration-blobstorage",
|
||||
"annotation-queues",
|
||||
"self-host-ui-customization",
|
||||
"self-host-allowed-organization-creators",
|
||||
"prompt-experiments",
|
||||
"trace-deletion", // Not in use anymore, but necessary to use the TableAction type.
|
||||
"audit-logs",
|
||||
"data-retention",
|
||||
"prompt-protected-labels",
|
||||
"custom-dashboards",
|
||||
"admin-api",
|
||||
] as const;
|
||||
export type Entitlement = (typeof entitlements)[number];
|
||||
|
||||
const cloudAllPlansEntitlements: Entitlement[] = [
|
||||
"playground",
|
||||
"model-based-evaluations",
|
||||
"cloud-billing",
|
||||
"integration-posthog",
|
||||
"annotation-queues",
|
||||
"prompt-experiments",
|
||||
"trace-deletion",
|
||||
"custom-dashboards",
|
||||
];
|
||||
|
||||
const selfHostedAllPlansEntitlements: Entitlement[] = ["trace-deletion", "custom-dashboards"];
|
||||
const selfHostedAllPlansEntitlements: Entitlement[] = ["trace-deletion"];
|
||||
|
||||
// Entitlement Limits: Limits on the number of resources that can be created/used
|
||||
const entitlementLimits = [
|
||||
@@ -96,7 +83,6 @@ export const entitlementAccess: Record<
|
||||
"audit-logs",
|
||||
"data-retention",
|
||||
"cloud-multi-tenant-sso",
|
||||
"integration-blobstorage",
|
||||
"prompt-protected-labels",
|
||||
"admin-api",
|
||||
],
|
||||
@@ -115,7 +101,6 @@ export const entitlementAccess: Record<
|
||||
"audit-logs",
|
||||
"data-retention",
|
||||
"cloud-multi-tenant-sso",
|
||||
"integration-blobstorage",
|
||||
"prompt-protected-labels",
|
||||
"admin-api",
|
||||
],
|
||||
@@ -128,25 +113,7 @@ export const entitlementAccess: Record<
|
||||
},
|
||||
},
|
||||
oss: {
|
||||
entitlements: [...selfHostedAllPlansEntitlements],
|
||||
entitlementLimits: {
|
||||
"annotation-queue-count": 0,
|
||||
"organization-member-count": false,
|
||||
"data-access-days": false,
|
||||
"model-based-evaluations-count-evaluators": false,
|
||||
"prompt-management-count-prompts": false,
|
||||
},
|
||||
},
|
||||
"self-hosted:pro": {
|
||||
entitlements: [
|
||||
...selfHostedAllPlansEntitlements,
|
||||
"annotation-queues",
|
||||
"model-based-evaluations",
|
||||
"playground",
|
||||
"prompt-experiments",
|
||||
"integration-posthog",
|
||||
"integration-blobstorage",
|
||||
],
|
||||
entitlements: selfHostedAllPlansEntitlements,
|
||||
entitlementLimits: {
|
||||
"annotation-queue-count": false,
|
||||
"organization-member-count": false,
|
||||
@@ -158,15 +125,9 @@ export const entitlementAccess: Record<
|
||||
"self-hosted:enterprise": {
|
||||
entitlements: [
|
||||
...selfHostedAllPlansEntitlements,
|
||||
"annotation-queues",
|
||||
"model-based-evaluations",
|
||||
"playground",
|
||||
"prompt-experiments",
|
||||
"rbac-project-roles",
|
||||
"self-host-allowed-organization-creators",
|
||||
"self-host-ui-customization",
|
||||
"integration-posthog",
|
||||
"integration-blobstorage",
|
||||
"audit-logs",
|
||||
"data-retention",
|
||||
"prompt-protected-labels",
|
||||
|
||||
@@ -60,8 +60,5 @@ export function getSelfHostedInstancePlanServerSide(): Plan | null {
|
||||
if (licenseKey.startsWith("langfuse_ee_")) {
|
||||
return "self-hosted:enterprise";
|
||||
}
|
||||
if (licenseKey.startsWith("langfuse_pro_")) {
|
||||
return "self-hosted:pro";
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
import { EvaluatorStatus } from "@/src/ee/features/evals/types";
|
||||
import { EvaluatorStatus } from "@/src/features/evals/types";
|
||||
import { usePostHogClientCapture } from "@/src/features/posthog-analytics/usePostHogClientCapture";
|
||||
import { useHasProjectAccess } from "@/src/features/rbac/utils/checkProjectAccess";
|
||||
import { api, type RouterOutputs } from "@/src/utils/api";
|
||||
+13
@@ -7,6 +7,8 @@ import { type LangfuseColumnDef } from "@/src/components/table/types";
|
||||
import { IOTableCell } from "@/src/components/ui/CodeJsonViewer";
|
||||
import useColumnOrder from "@/src/features/column-visibility/hooks/useColumnOrder";
|
||||
import useColumnVisibility from "@/src/features/column-visibility/hooks/useColumnVisibility";
|
||||
import { useQueryFilterState } from "@/src/features/filters/hooks/useFilterState";
|
||||
import { evalExecutionsFilterCols } from "@/src/server/api/definitions/evalExecutionsTable";
|
||||
import { type RouterOutputs, api } from "@/src/utils/api";
|
||||
import { type Prisma } from "@langfuse/shared";
|
||||
import { createColumnHelper } from "@tanstack/react-table";
|
||||
@@ -38,9 +40,17 @@ export default function EvalLogTable({
|
||||
pageIndex: withDefault(NumberParam, 0),
|
||||
pageSize: withDefault(NumberParam, 50),
|
||||
});
|
||||
|
||||
const [filterState, setFilterState] = useQueryFilterState(
|
||||
[],
|
||||
"job_executions",
|
||||
projectId,
|
||||
);
|
||||
|
||||
const logs = api.evals.getLogs.useQuery({
|
||||
page: paginationState.pageIndex,
|
||||
limit: paginationState.pageSize,
|
||||
filter: filterState,
|
||||
jobConfigurationId,
|
||||
projectId,
|
||||
});
|
||||
@@ -191,6 +201,9 @@ export default function EvalLogTable({
|
||||
setColumnOrder={setColumnOrder}
|
||||
rowHeight={rowHeight}
|
||||
setRowHeight={setRowHeight}
|
||||
filterState={filterState}
|
||||
setFilterState={setFilterState}
|
||||
filterColumnDefinition={evalExecutionsFilterCols}
|
||||
/>
|
||||
<DataTable
|
||||
columns={columns}
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
import * as React from "react";
|
||||
import { EvalTemplateForm } from "@/src/ee/features/evals/components/template-form";
|
||||
import { EvalTemplateForm } from "@/src/features/evals/components/template-form";
|
||||
import { api } from "@/src/utils/api";
|
||||
import { type EvalTemplate } from "@langfuse/shared";
|
||||
import { useRouter } from "next/router";
|
||||
+9
-14
@@ -28,19 +28,16 @@ import {
|
||||
DialogHeader,
|
||||
DialogTitle,
|
||||
} from "@/src/components/ui/dialog";
|
||||
import { EvalTemplateForm } from "@/src/ee/features/evals/components/template-form";
|
||||
import { EvalTemplateForm } from "@/src/features/evals/components/template-form";
|
||||
import { showSuccessToast } from "@/src/features/notifications/showSuccessToast";
|
||||
import { EvalReferencedEvaluators } from "@/src/ee/features/evals/types";
|
||||
import { EvalReferencedEvaluators } from "@/src/features/evals/types";
|
||||
import { showErrorToast } from "@/src/features/notifications/showErrorToast";
|
||||
import { type RouterInput } from "@/src/utils/types";
|
||||
import { useSingleTemplateValidation } from "@/src/ee/features/evals/hooks/useSingleTemplateValidation";
|
||||
import { getMaintainer } from "@/src/ee/features/evals/utils/typeHelpers";
|
||||
import { MaintainerTooltip } from "@/src/ee/features/evals/components/maintainer-tooltip";
|
||||
import { useSingleTemplateValidation } from "@/src/features/evals/hooks/useSingleTemplateValidation";
|
||||
import { getMaintainer } from "@/src/features/evals/utils/typeHelpers";
|
||||
import { MaintainerTooltip } from "@/src/features/evals/components/maintainer-tooltip";
|
||||
import { ActionButton } from "@/src/components/ActionButton";
|
||||
import {
|
||||
useEntitlementLimit,
|
||||
useHasEntitlement,
|
||||
} from "@/src/features/entitlements/hooks";
|
||||
import { useEntitlementLimit } from "@/src/features/entitlements/hooks";
|
||||
import { useHasProjectAccess } from "@/src/features/rbac/utils/checkProjectAccess";
|
||||
|
||||
export type EvalsTemplateRow = {
|
||||
@@ -86,7 +83,6 @@ export default function EvalsTemplateTable({
|
||||
});
|
||||
|
||||
const hasAccess = useHasProjectAccess({ projectId, scope: "evalJob:CUD" });
|
||||
const hasEntitlement = useHasEntitlement("model-based-evaluations");
|
||||
|
||||
const totalCount = templates.data?.totalCount ?? null;
|
||||
|
||||
@@ -120,7 +116,7 @@ export default function EvalsTemplateTable({
|
||||
projectId,
|
||||
},
|
||||
{
|
||||
enabled: !!projectId && hasEntitlement,
|
||||
enabled: !!projectId,
|
||||
trpc: {
|
||||
context: {
|
||||
skipBatch: true,
|
||||
@@ -240,7 +236,6 @@ export default function EvalsTemplateTable({
|
||||
: undefined
|
||||
}
|
||||
hasAccess={hasAccess}
|
||||
hasEntitlement={hasEntitlement}
|
||||
limitValue={countsQuery.data?.configActiveCount ?? 0}
|
||||
limit={evaluatorLimit}
|
||||
onClick={(e) => {
|
||||
@@ -260,7 +255,7 @@ export default function EvalsTemplateTable({
|
||||
variant="outline"
|
||||
size="icon-xs"
|
||||
title="Clone"
|
||||
disabled={!hasAccess || !hasEntitlement}
|
||||
disabled={!hasAccess}
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
if (id) setCloneTemplateId(id);
|
||||
@@ -274,7 +269,7 @@ export default function EvalsTemplateTable({
|
||||
variant="outline"
|
||||
size="icon-xs"
|
||||
title="Edit"
|
||||
disabled={!hasAccess || !hasEntitlement}
|
||||
disabled={!hasAccess}
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
if (id) setEditTemplateId(id);
|
||||
+2
-2
@@ -1,7 +1,7 @@
|
||||
import { ItemBadge } from "@/src/components/ItemBadge";
|
||||
import { Skeleton } from "@/src/components/ui/skeleton";
|
||||
import { useExtractVariables } from "@/src/ee/features/evals/hooks/useExtractVariables";
|
||||
import { type VariableMapping } from "@/src/ee/features/evals/utils/evaluator-form-utils";
|
||||
import { useExtractVariables } from "@/src/features/evals/hooks/useExtractVariables";
|
||||
import { type VariableMapping } from "@/src/features/evals/utils/evaluator-form-utils";
|
||||
import { cn } from "@/src/utils/tailwind";
|
||||
import { type RouterOutput } from "@/src/utils/types";
|
||||
import { type EvalTemplate } from "@langfuse/shared";
|
||||
+2
-2
@@ -1,7 +1,7 @@
|
||||
import * as React from "react";
|
||||
import { api } from "@/src/utils/api";
|
||||
import { useRouter } from "next/router";
|
||||
import EvalLogTable from "@/src/ee/features/evals/components/eval-log";
|
||||
import EvalLogTable from "@/src/features/evals/components/eval-log";
|
||||
import { StatusBadge } from "@/src/components/layouts/status-badge";
|
||||
import { DetailPageNav } from "@/src/features/navigate-detail-pages/DetailPageNav";
|
||||
import Page from "@/src/components/layouts/page";
|
||||
@@ -9,7 +9,7 @@ import { LevelCountsDisplay } from "@/src/components/level-counts-display";
|
||||
import {
|
||||
type JobExecutionState,
|
||||
generateJobExecutionCounts,
|
||||
} from "@/src/ee/features/evals/utils/job-execution-utils";
|
||||
} from "@/src/features/evals/utils/job-execution-utils";
|
||||
|
||||
const JobExecutionCounts = ({
|
||||
jobExecutionsByState,
|
||||
+2
-2
@@ -1,6 +1,6 @@
|
||||
import { type EvalTemplate } from "@langfuse/shared";
|
||||
import { InnerEvaluatorForm } from "@/src/ee/features/evals/components/inner-evaluator-form";
|
||||
import { type PartialConfig } from "@/src/ee/features/evals/types";
|
||||
import { InnerEvaluatorForm } from "@/src/features/evals/components/inner-evaluator-form";
|
||||
import { type PartialConfig } from "@/src/features/evals/types";
|
||||
|
||||
export const EvaluatorForm = (props: {
|
||||
projectId: string;
|
||||
+5
-20
@@ -16,9 +16,9 @@ import {
|
||||
TooltipContent,
|
||||
TooltipTrigger,
|
||||
} from "@/src/components/ui/tooltip";
|
||||
import { useSingleTemplateValidation } from "@/src/ee/features/evals/hooks/useSingleTemplateValidation";
|
||||
import { getMaintainer } from "@/src/ee/features/evals/utils/typeHelpers";
|
||||
import { MaintainerTooltip } from "@/src/ee/features/evals/components/maintainer-tooltip";
|
||||
import { useSingleTemplateValidation } from "@/src/features/evals/hooks/useSingleTemplateValidation";
|
||||
import { getMaintainer } from "@/src/features/evals/utils/typeHelpers";
|
||||
import { MaintainerTooltip } from "@/src/features/evals/components/maintainer-tooltip";
|
||||
import Link from "next/link";
|
||||
|
||||
interface EvaluatorSelectorProps {
|
||||
@@ -130,7 +130,7 @@ export function EvaluatorSelector({
|
||||
{isInvalid && (
|
||||
<Tooltip>
|
||||
<TooltipTrigger asChild>
|
||||
<AlertCircle className="ml-1 h-4 w-4 text-destructive" />
|
||||
<AlertCircle className="ml-1 h-4 w-4 text-yellow-500" />
|
||||
</TooltipTrigger>
|
||||
<TooltipContent>
|
||||
Requires project-level evaluation model
|
||||
@@ -202,7 +202,7 @@ export function EvaluatorSelector({
|
||||
{isInvalid && (
|
||||
<Tooltip>
|
||||
<TooltipTrigger asChild>
|
||||
<AlertCircle className="ml-1 h-4 w-4 text-destructive" />
|
||||
<AlertCircle className="ml-1 h-4 w-4 text-yellow-500" />
|
||||
</TooltipTrigger>
|
||||
<TooltipContent>
|
||||
Requires project-level evaluation model
|
||||
@@ -239,24 +239,9 @@ export function EvaluatorSelector({
|
||||
);
|
||||
})}
|
||||
</InputCommandGroup>
|
||||
{filteredTemplates.langfuse.length > 0 && <InputCommandSeparator />}
|
||||
</>
|
||||
)}
|
||||
|
||||
<InputCommandGroup forceMount>
|
||||
<InputCommandItem
|
||||
onSelect={() => {
|
||||
window.open(
|
||||
`/project/${projectId}/evals/default-model`,
|
||||
"_blank",
|
||||
);
|
||||
}}
|
||||
>
|
||||
Manage default evaluation model
|
||||
<ExternalLink className="ml-auto h-4 w-4" />
|
||||
</InputCommandItem>
|
||||
</InputCommandGroup>
|
||||
|
||||
{onCreateNew && (
|
||||
<>
|
||||
<InputCommandSeparator alwaysRender />
|
||||
+6
-6
@@ -19,7 +19,7 @@ import {
|
||||
StringParam,
|
||||
} from "use-query-params";
|
||||
import { z } from "zod";
|
||||
import { generateJobExecutionCounts } from "@/src/ee/features/evals/utils/job-execution-utils";
|
||||
import { generateJobExecutionCounts } from "@/src/features/evals/utils/job-execution-utils";
|
||||
import { useOrderByState } from "@/src/features/orderBy/hooks/useOrderByState";
|
||||
import TableIdOrName from "@/src/components/table/table-id";
|
||||
import { MoreVertical, Loader2, ExternalLinkIcon, Edit } from "lucide-react";
|
||||
@@ -36,12 +36,12 @@ import {
|
||||
import { Button } from "@/src/components/ui/button";
|
||||
import { showSuccessToast } from "@/src/features/notifications/showSuccessToast";
|
||||
import { Dialog, DialogContent, DialogTitle } from "@/src/components/ui/dialog";
|
||||
import { EvaluatorForm } from "@/src/ee/features/evals/components/evaluator-form";
|
||||
import { EvaluatorForm } from "@/src/features/evals/components/evaluator-form";
|
||||
import { useRouter } from "next/router";
|
||||
import { DeleteEvaluatorButton } from "@/src/components/deleteButton";
|
||||
import { DeleteEvalConfigButton } from "@/src/components/deleteButton";
|
||||
import { evalConfigFilterColumns } from "@/src/server/api/definitions/evalConfigsTable";
|
||||
import { RAGAS_TEMPLATE_PREFIX } from "@/src/ee/features/evals/types";
|
||||
import { MaintainerTooltip } from "@/src/ee/features/evals/components/maintainer-tooltip";
|
||||
import { RAGAS_TEMPLATE_PREFIX } from "@/src/features/evals/types";
|
||||
import { MaintainerTooltip } from "@/src/features/evals/components/maintainer-tooltip";
|
||||
import { useHasProjectAccess } from "@/src/features/rbac/utils/checkProjectAccess";
|
||||
|
||||
export type EvaluatorDataRow = {
|
||||
@@ -292,7 +292,7 @@ export default function EvaluatorTable({ projectId }: { projectId: string }) {
|
||||
Edit
|
||||
</DropdownMenuItem>
|
||||
<DropdownMenuItem asChild>
|
||||
<DeleteEvaluatorButton
|
||||
<DeleteEvalConfigButton
|
||||
aria-label="delete"
|
||||
itemId={id}
|
||||
projectId={projectId}
|
||||
@@ -0,0 +1,45 @@
|
||||
import { type EvalFormType } from "@/src/features/evals/utils/evaluator-form-utils";
|
||||
import { api } from "@/src/utils/api";
|
||||
import { compactNumberFormatter } from "@/src/utils/numbers";
|
||||
import { useEvalTargetCount } from "@/src/features/evals/hooks/useEvalTargetCount";
|
||||
|
||||
type ExecutionCountTooltipProps = {
|
||||
projectId: string;
|
||||
item: string;
|
||||
filter: EvalFormType["filter"];
|
||||
};
|
||||
|
||||
export const ExecutionCountTooltip = ({
|
||||
projectId,
|
||||
item,
|
||||
filter,
|
||||
}: ExecutionCountTooltipProps) => {
|
||||
const globalConfig = api.evals.globalJobConfigs.useQuery({
|
||||
projectId,
|
||||
});
|
||||
|
||||
const { isLoading, totalCount, isTraceTarget } = useEvalTargetCount({
|
||||
projectId,
|
||||
item,
|
||||
filter,
|
||||
enabled: true,
|
||||
});
|
||||
|
||||
return (
|
||||
<>
|
||||
<span className="text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70">
|
||||
(
|
||||
{isLoading ? (
|
||||
<span className="inline-block font-mono">...</span>
|
||||
) : (
|
||||
compactNumberFormatter(
|
||||
!globalConfig.data || (totalCount && totalCount < globalConfig.data)
|
||||
? totalCount
|
||||
: globalConfig.data,
|
||||
)
|
||||
)}
|
||||
{isTraceTarget ? " traces" : " dataset run items"})
|
||||
</span>
|
||||
</>
|
||||
);
|
||||
};
|
||||
+6
-6
@@ -47,24 +47,24 @@ import {
|
||||
isTraceOrDatasetObject,
|
||||
isTraceTarget,
|
||||
type LangfuseObject,
|
||||
} from "@/src/ee/features/evals/utils/evaluator-form-utils";
|
||||
import { ExecutionCountTooltip } from "@/src/ee/features/evals/components/execution-count-tooltip";
|
||||
} from "@/src/features/evals/utils/evaluator-form-utils";
|
||||
import { ExecutionCountTooltip } from "@/src/features/evals/components/execution-count-tooltip";
|
||||
import {
|
||||
TimeScopeDescription,
|
||||
VariableMappingDescription,
|
||||
} from "@/src/ee/features/evals/components/eval-form-descriptions";
|
||||
} from "@/src/features/evals/components/eval-form-descriptions";
|
||||
import { Suspense, lazy } from "react";
|
||||
import {
|
||||
getDateFromOption,
|
||||
type TableDateRange,
|
||||
} from "@/src/utils/date-range-utils";
|
||||
import { useEvalConfigMappingData } from "@/src/ee/features/evals/hooks/useEvalConfigMappingData";
|
||||
import { type PartialConfig } from "@/src/ee/features/evals/types";
|
||||
import { useEvalConfigMappingData } from "@/src/features/evals/hooks/useEvalConfigMappingData";
|
||||
import { type PartialConfig } from "@/src/features/evals/types";
|
||||
import { Switch } from "@/src/components/ui/switch";
|
||||
import {
|
||||
EvaluationPromptPreview,
|
||||
getVariableColor,
|
||||
} from "@/src/ee/features/evals/components/evaluation-prompt-preview";
|
||||
} from "@/src/features/evals/components/evaluation-prompt-preview";
|
||||
import { DetailPageNav } from "@/src/features/navigate-detail-pages/DetailPageNav";
|
||||
import { Skeleton } from "@/src/components/ui/skeleton";
|
||||
|
||||
+1
-1
@@ -4,7 +4,7 @@ import {
|
||||
TooltipTrigger,
|
||||
TooltipContent,
|
||||
} from "@/src/components/ui/tooltip";
|
||||
import { RagasLogoIcon } from "@/src/ee/features/evals/components/ragas-logo";
|
||||
import { RagasLogoIcon } from "@/src/features/evals/components/ragas-logo";
|
||||
import { UserCircle2Icon } from "lucide-react";
|
||||
|
||||
function MaintainerIcon({ maintainer }: { maintainer: string }) {
|
||||
@@ -0,0 +1,90 @@
|
||||
import { Button } from "@/src/components/ui/button";
|
||||
import { useHasProjectAccess } from "@/src/features/rbac/utils/checkProjectAccess";
|
||||
import { api } from "@/src/utils/api";
|
||||
import { cn } from "@/src/utils/tailwind";
|
||||
import { Check, Pencil, TriangleAlert } from "lucide-react";
|
||||
import Link from "next/link";
|
||||
|
||||
export function ManageDefaultEvalModel({
|
||||
projectId,
|
||||
setUpMessage,
|
||||
variant = "default",
|
||||
showEditButton = true,
|
||||
className,
|
||||
}: {
|
||||
projectId: string;
|
||||
setUpMessage?: string;
|
||||
variant?: "default" | "color-coded";
|
||||
showEditButton?: boolean;
|
||||
className?: string;
|
||||
}) {
|
||||
const hasDefaultModelReadAccess = useHasProjectAccess({
|
||||
projectId,
|
||||
scope: "evalDefaultModel:read",
|
||||
});
|
||||
const hasDefaultModelWriteAccess = useHasProjectAccess({
|
||||
projectId,
|
||||
scope: "evalDefaultModel:CUD",
|
||||
});
|
||||
|
||||
const { data: defaultModel } = api.defaultLlmModel.fetchDefaultModel.useQuery(
|
||||
{ projectId },
|
||||
{ enabled: hasDefaultModelReadAccess },
|
||||
);
|
||||
|
||||
return (
|
||||
<div className="flex items-center">
|
||||
{!showEditButton &&
|
||||
(defaultModel ? (
|
||||
<Check className="mr-2 h-4 w-4 text-dark-green" />
|
||||
) : (
|
||||
<TriangleAlert className="mr-2 h-4 w-4 text-dark-yellow" />
|
||||
))}
|
||||
{defaultModel ? (
|
||||
<span
|
||||
className={cn(
|
||||
"text-sm font-medium",
|
||||
variant === "color-coded" && "text-dark-green",
|
||||
className,
|
||||
)}
|
||||
>
|
||||
{"Current default model: "}
|
||||
{defaultModel.provider} / {defaultModel.model}
|
||||
</span>
|
||||
) : (
|
||||
<span
|
||||
className={cn(
|
||||
"text-sm font-medium",
|
||||
variant === "color-coded" && "text-dark-yellow",
|
||||
className,
|
||||
)}
|
||||
>
|
||||
{setUpMessage ?? "No default model set"}
|
||||
</span>
|
||||
)}
|
||||
{showEditButton && (
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="icon-sm"
|
||||
type="button"
|
||||
asChild
|
||||
disabled={!hasDefaultModelWriteAccess}
|
||||
>
|
||||
<Link
|
||||
href={`/project/${projectId}/evals/default-model`}
|
||||
target="_blank"
|
||||
>
|
||||
<Pencil
|
||||
className={cn(
|
||||
"h-3 w-3",
|
||||
variant === "color-coded" &&
|
||||
!defaultModel &&
|
||||
"text-dark-yellow",
|
||||
)}
|
||||
/>
|
||||
</Link>
|
||||
</Button>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user