@percepta/kaizen 0.9.0 → 0.9.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/dashboard/pages/api/langfuse-action.js +9 -2
- package/dist/dashboard/pages/api/langfuse-action.js.map +1 -1
- package/dist/dashboard/pages/api/langfuse-dataset-item.js +5 -2
- package/dist/dashboard/pages/api/langfuse-dataset-item.js.map +1 -1
- package/dist/dashboard/pages/api/langfuse-dataset-mutation.js +1 -1
- package/dist/dashboard/pages/api/langfuse-trace-memberships.js +1 -1
- package/dist/dashboard/src/lib/dataset-item-labeling.js +20 -0
- package/dist/dashboard/src/lib/dataset-item-labeling.js.map +1 -0
- package/dist/package.js +1 -1
- package/dist/studio/client/assets/index-D5r9lQ7l.js +9 -0
- package/dist/studio/client/assets/index-Dc4zGLjQ.css +1 -0
- package/dist/studio/client/index.html +2 -2
- package/package.json +1 -1
- package/dist/studio/client/assets/index-Bwj0gucs.css +0 -1
- package/dist/studio/client/assets/index-DKAiSaYs.js +0 -9
|
@@ -1,7 +1,8 @@
|
|
|
1
|
+
import { isRecord } from "../../src/lib/langfuse-helpers.js";
|
|
2
|
+
import { markDatasetItemLabeled } from "../../src/lib/dataset-item-labeling.js";
|
|
1
3
|
import { resolveLangfuseCreds } from "../../src/lib/langfuse-creds.js";
|
|
2
4
|
import { DEMO_CONNECTION, isDemoMode } from "../../src/lib/langfuse-demo.js";
|
|
3
5
|
import { formatLangfuseHttpError, formatLangfuseRequestError } from "../../src/lib/langfuse-errors.js";
|
|
4
|
-
import { isRecord } from "../../src/lib/langfuse-helpers.js";
|
|
5
6
|
import { datasetItemsCache } from "./langfuse-dataset.js";
|
|
6
7
|
import { membershipsCache } from "./langfuse-trace-memberships.js";
|
|
7
8
|
//#region dashboard/pages/api/langfuse-action.ts
|
|
@@ -77,12 +78,18 @@ async function dispatch(creds, request) {
|
|
|
77
78
|
id: request.itemId,
|
|
78
79
|
input: request.input === void 0 ? existing?.input : request.input,
|
|
79
80
|
expectedOutput: request.expectedOutput === void 0 ? existing?.expectedOutput : request.expectedOutput,
|
|
80
|
-
metadata:
|
|
81
|
+
metadata: getUpdatedDatasetItemMetadata(existing, request),
|
|
81
82
|
sourceTraceId: request.sourceTraceId === void 0 ? existing?.sourceTraceId : request.sourceTraceId,
|
|
82
83
|
status: request.status === void 0 ? existing?.status : request.status
|
|
83
84
|
}
|
|
84
85
|
});
|
|
85
86
|
}
|
|
87
|
+
function getUpdatedDatasetItemMetadata(existing, request) {
|
|
88
|
+
const existingMetadata = existing?.metadata;
|
|
89
|
+
const metadata = request.metadata === void 0 ? isRecord(existingMetadata) || existingMetadata === null ? existingMetadata : void 0 : request.metadata;
|
|
90
|
+
if (request.expectedOutput === void 0) return metadata;
|
|
91
|
+
return markDatasetItemLabeled(metadata);
|
|
92
|
+
}
|
|
86
93
|
async function langfuseGet(creds, resource, path) {
|
|
87
94
|
const res = await fetch(`${creds.host.replace(/\/$/, "")}${path}`, {
|
|
88
95
|
method: "GET",
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"langfuse-action.js","names":[],"sources":["../../../../dashboard/pages/api/langfuse-action.ts"],"sourcesContent":["import { resolveLangfuseCreds } from \"../../src/lib/langfuse-creds\";\nimport { DEMO_CONNECTION, isDemoMode } from \"../../src/lib/langfuse-demo\";\nimport {\n formatLangfuseHttpError,\n formatLangfuseRequestError,\n} from \"../../src/lib/langfuse-errors\";\nimport { isRecord } from \"../../src/lib/langfuse-helpers\";\nimport type {\n StudioRequest,\n StudioResponse,\n} from \"../../src/studio/server/compat\";\nimport { datasetItemsCache } from \"./langfuse-dataset\";\nimport { membershipsCache } from \"./langfuse-trace-memberships\";\n\ninterface LangfuseCreds {\n host: string;\n auth: string;\n}\n\ntype MutationRequest =\n | {\n action: \"create-score\";\n traceId: string;\n name: string;\n value: number | string | boolean;\n comment?: string;\n metadata?: Record<string, unknown>;\n }\n | {\n action: \"update-dataset-item\";\n datasetName: string;\n itemId: string;\n input?: unknown;\n expectedOutput?: unknown;\n metadata?: Record<string, unknown> | null;\n sourceTraceId?: string | null;\n status?: string | null;\n }\n | {\n action: \"create-dataset-run-item\";\n datasetItemId: string;\n traceId: string;\n runName: string;\n runDescription?: string;\n metadata?: Record<string, unknown>;\n };\n\nexport default async function handler(req: StudioRequest, res: StudioResponse) {\n if (req.method !== \"POST\") {\n res.status(405).json({ error: \"POST only\" });\n return;\n }\n\n const parsed = parseMutationRequest(req.body);\n if (!parsed.ok) {\n res.status(400).json({ error: parsed.error });\n return;\n }\n\n const creds = resolveLangfuseCreds();\n if (!creds.host || !creds.auth) {\n if (isDemoMode()) {\n res.status(200).json({\n data: null,\n success: true,\n connection: DEMO_CONNECTION,\n });\n return;\n }\n res.status(400).json({\n error: creds.connection.message,\n connection: creds.connection,\n });\n return;\n }\n\n const lfCreds: LangfuseCreds = { host: creds.host, auth: creds.auth };\n\n try {\n const data = await dispatch(lfCreds, parsed.value);\n if (\n parsed.value.action === \"update-dataset-item\" ||\n parsed.value.action === \"create-dataset-run-item\"\n ) {\n datasetItemsCache.clear();\n membershipsCache.clear();\n }\n res.status(200).json({ data });\n } catch (err) {\n const status = err instanceof LangfuseApiError ? err.status : 502;\n const error =\n err instanceof LangfuseApiError\n ? err.message\n : formatLangfuseRequestError(err);\n res.status(status).json({ error });\n }\n}\n\nasync function dispatch(\n creds: LangfuseCreds,\n request: MutationRequest,\n): Promise<unknown> {\n if (request.action === \"create-score\") {\n return langfuseJson(creds, \"the score\", {\n path: \"/api/public/scores\",\n body: {\n traceId: request.traceId,\n name: request.name,\n value: request.value,\n comment: request.comment,\n metadata: request.metadata,\n },\n });\n }\n\n if (request.action === \"create-dataset-run-item\") {\n return langfuseJson(creds, \"the dataset run item\", {\n path: \"/api/public/dataset-run-items\",\n body: {\n datasetItemId: request.datasetItemId,\n traceId: request.traceId,\n runName: request.runName,\n runDescription: request.runDescription,\n metadata: request.metadata,\n },\n });\n }\n\n // Skip the upstream GET when the client supplies all fields.\n const hasAllFields =\n request.input !== undefined &&\n request.expectedOutput !== undefined &&\n request.metadata !== undefined &&\n request.sourceTraceId !== undefined &&\n request.status !== undefined;\n\n const existing = hasAllFields\n ? null\n : await langfuseGet<Record<string, unknown>>(\n creds,\n \"the dataset item\",\n `/api/public/dataset-items/${encodeURIComponent(request.itemId)}`,\n );\n return langfuseJson(creds, \"the dataset item\", {\n path: \"/api/public/dataset-items\",\n body: {\n datasetName: request.datasetName,\n id: request.itemId,\n input: request.input === undefined ? existing?.input : request.input,\n expectedOutput:\n request.expectedOutput === undefined\n ? existing?.expectedOutput\n : request.expectedOutput,\n metadata:\n request.metadata === undefined ? existing?.metadata : request.metadata,\n sourceTraceId:\n request.sourceTraceId === undefined\n ? existing?.sourceTraceId\n : request.sourceTraceId,\n status: request.status === undefined ? existing?.status : request.status,\n },\n });\n}\n\nasync function langfuseGet<T>(\n creds: LangfuseCreds,\n resource: string,\n path: string,\n): Promise<T> {\n const res = await fetch(`${creds.host.replace(/\\/$/, \"\")}${path}`, {\n method: \"GET\",\n headers: { Authorization: `Basic ${creds.auth}` },\n });\n if (!res.ok) {\n throw new LangfuseApiError(\n res.status,\n await formatLangfuseHttpError(res, resource),\n );\n }\n return res.json() as Promise<T>;\n}\n\nasync function langfuseJson(\n creds: LangfuseCreds,\n resource: string,\n request: { path: string; body: unknown },\n): Promise<unknown> {\n const res = await fetch(`${creds.host.replace(/\\/$/, \"\")}${request.path}`, {\n method: \"POST\",\n headers: {\n Authorization: `Basic ${creds.auth}`,\n \"Content-Type\": \"application/json\",\n },\n body: JSON.stringify(request.body),\n });\n if (!res.ok) {\n throw new LangfuseApiError(\n res.status,\n await formatLangfuseHttpError(res, resource),\n );\n }\n return res.json();\n}\n\nfunction parseMutationRequest(\n value: unknown,\n): { ok: true; value: MutationRequest } | { ok: false; error: string } {\n if (!isRecord(value)) {\n return { ok: false, error: \"Request body must be an object.\" };\n }\n\n const action = stringField(value, \"action\");\n\n if (action === \"create-score\") {\n const traceId = stringField(value, \"traceId\");\n const name = stringField(value, \"name\");\n if (!traceId) return { ok: false, error: \"traceId is required.\" };\n if (!name) return { ok: false, error: \"name is required.\" };\n if (!isScoreValue(value.value)) {\n return {\n ok: false,\n error: \"value must be a number, string, or boolean.\",\n };\n }\n return {\n ok: true,\n value: {\n action,\n traceId,\n name,\n value: value.value,\n comment: stringField(value, \"comment\") ?? undefined,\n metadata: recordField(value, \"metadata\") ?? undefined,\n },\n };\n }\n\n if (action === \"update-dataset-item\") {\n const datasetName = stringField(value, \"datasetName\");\n const itemId = stringField(value, \"itemId\");\n if (!datasetName) return { ok: false, error: \"datasetName is required.\" };\n if (!itemId) return { ok: false, error: \"itemId is required.\" };\n return {\n ok: true,\n value: {\n action,\n datasetName,\n itemId,\n input: value.input,\n expectedOutput: value.expectedOutput,\n metadata: recordOrNullField(value, \"metadata\"),\n sourceTraceId: stringOrNullField(value, \"sourceTraceId\"),\n status: stringOrNullField(value, \"status\"),\n },\n };\n }\n\n if (action === \"create-dataset-run-item\") {\n const datasetItemId = stringField(value, \"datasetItemId\");\n const traceId = stringField(value, \"traceId\");\n const runName = stringField(value, \"runName\");\n if (!datasetItemId)\n return { ok: false, error: \"datasetItemId is required.\" };\n if (!traceId) return { ok: false, error: \"traceId is required.\" };\n if (!runName) return { ok: false, error: \"runName is required.\" };\n return {\n ok: true,\n value: {\n action,\n datasetItemId,\n traceId,\n runName,\n runDescription: stringField(value, \"runDescription\") ?? undefined,\n metadata: recordField(value, \"metadata\") ?? undefined,\n },\n };\n }\n\n return {\n ok: false,\n error:\n \"action must be create-score, update-dataset-item, or create-dataset-run-item.\",\n };\n}\n\nfunction isScoreValue(value: unknown): value is number | string | boolean {\n return (\n (typeof value === \"number\" && Number.isFinite(value)) ||\n typeof value === \"string\" ||\n typeof value === \"boolean\"\n );\n}\n\nfunction recordField(\n value: Record<string, unknown>,\n key: string,\n): Record<string, unknown> | null {\n const field = value[key];\n return isRecord(field) ? field : null;\n}\n\nfunction recordOrNullField(\n value: Record<string, unknown>,\n key: string,\n): Record<string, unknown> | null | undefined {\n const field = value[key];\n if (field === null) return null;\n return isRecord(field) ? field : undefined;\n}\n\nfunction stringOrNullField(\n value: Record<string, unknown>,\n key: string,\n): string | null | undefined {\n const field = value[key];\n if (field === null) return null;\n return typeof field === \"string\" ? field : undefined;\n}\n\nfunction stringField(\n record: Record<string, unknown>,\n key: string,\n): string | null {\n const value = record[key];\n return typeof value === \"string\" && value.trim() ? value.trim() : null;\n}\n\nclass LangfuseApiError extends Error {\n public readonly status: number;\n\n public constructor(status: number, message: string) {\n super(message);\n this.status = status;\n }\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"langfuse-action.js","names":[],"sources":["../../../../dashboard/pages/api/langfuse-action.ts"],"sourcesContent":["import { markDatasetItemLabeled } from \"../../src/lib/dataset-item-labeling\";\nimport { resolveLangfuseCreds } from \"../../src/lib/langfuse-creds\";\nimport { DEMO_CONNECTION, isDemoMode } from \"../../src/lib/langfuse-demo\";\nimport {\n formatLangfuseHttpError,\n formatLangfuseRequestError,\n} from \"../../src/lib/langfuse-errors\";\nimport { isRecord } from \"../../src/lib/langfuse-helpers\";\nimport type {\n StudioRequest,\n StudioResponse,\n} from \"../../src/studio/server/compat\";\nimport { datasetItemsCache } from \"./langfuse-dataset\";\nimport { membershipsCache } from \"./langfuse-trace-memberships\";\n\ninterface LangfuseCreds {\n host: string;\n auth: string;\n}\n\ntype MutationRequest =\n | {\n action: \"create-score\";\n traceId: string;\n name: string;\n value: number | string | boolean;\n comment?: string;\n metadata?: Record<string, unknown>;\n }\n | {\n action: \"update-dataset-item\";\n datasetName: string;\n itemId: string;\n input?: unknown;\n expectedOutput?: unknown;\n metadata?: Record<string, unknown> | null;\n sourceTraceId?: string | null;\n status?: string | null;\n }\n | {\n action: \"create-dataset-run-item\";\n datasetItemId: string;\n traceId: string;\n runName: string;\n runDescription?: string;\n metadata?: Record<string, unknown>;\n };\n\nexport default async function handler(req: StudioRequest, res: StudioResponse) {\n if (req.method !== \"POST\") {\n res.status(405).json({ error: \"POST only\" });\n return;\n }\n\n const parsed = parseMutationRequest(req.body);\n if (!parsed.ok) {\n res.status(400).json({ error: parsed.error });\n return;\n }\n\n const creds = resolveLangfuseCreds();\n if (!creds.host || !creds.auth) {\n if (isDemoMode()) {\n res.status(200).json({\n data: null,\n success: true,\n connection: DEMO_CONNECTION,\n });\n return;\n }\n res.status(400).json({\n error: creds.connection.message,\n connection: creds.connection,\n });\n return;\n }\n\n const lfCreds: LangfuseCreds = { host: creds.host, auth: creds.auth };\n\n try {\n const data = await dispatch(lfCreds, parsed.value);\n if (\n parsed.value.action === \"update-dataset-item\" ||\n parsed.value.action === \"create-dataset-run-item\"\n ) {\n datasetItemsCache.clear();\n membershipsCache.clear();\n }\n res.status(200).json({ data });\n } catch (err) {\n const status = err instanceof LangfuseApiError ? err.status : 502;\n const error =\n err instanceof LangfuseApiError\n ? err.message\n : formatLangfuseRequestError(err);\n res.status(status).json({ error });\n }\n}\n\nasync function dispatch(\n creds: LangfuseCreds,\n request: MutationRequest,\n): Promise<unknown> {\n if (request.action === \"create-score\") {\n return langfuseJson(creds, \"the score\", {\n path: \"/api/public/scores\",\n body: {\n traceId: request.traceId,\n name: request.name,\n value: request.value,\n comment: request.comment,\n metadata: request.metadata,\n },\n });\n }\n\n if (request.action === \"create-dataset-run-item\") {\n return langfuseJson(creds, \"the dataset run item\", {\n path: \"/api/public/dataset-run-items\",\n body: {\n datasetItemId: request.datasetItemId,\n traceId: request.traceId,\n runName: request.runName,\n runDescription: request.runDescription,\n metadata: request.metadata,\n },\n });\n }\n\n // Skip the upstream GET when the client supplies all fields.\n const hasAllFields =\n request.input !== undefined &&\n request.expectedOutput !== undefined &&\n request.metadata !== undefined &&\n request.sourceTraceId !== undefined &&\n request.status !== undefined;\n\n const existing = hasAllFields\n ? null\n : await langfuseGet<Record<string, unknown>>(\n creds,\n \"the dataset item\",\n `/api/public/dataset-items/${encodeURIComponent(request.itemId)}`,\n );\n return langfuseJson(creds, \"the dataset item\", {\n path: \"/api/public/dataset-items\",\n body: {\n datasetName: request.datasetName,\n id: request.itemId,\n input: request.input === undefined ? existing?.input : request.input,\n expectedOutput:\n request.expectedOutput === undefined\n ? existing?.expectedOutput\n : request.expectedOutput,\n metadata: getUpdatedDatasetItemMetadata(existing, request),\n sourceTraceId:\n request.sourceTraceId === undefined\n ? existing?.sourceTraceId\n : request.sourceTraceId,\n status: request.status === undefined ? existing?.status : request.status,\n },\n });\n}\n\nexport function getUpdatedDatasetItemMetadata(\n existing: Record<string, unknown> | null,\n request: Extract<MutationRequest, { action: \"update-dataset-item\" }>,\n): Record<string, unknown> | null | undefined {\n const existingMetadata = existing?.metadata;\n const metadata =\n request.metadata === undefined\n ? isRecord(existingMetadata) || existingMetadata === null\n ? existingMetadata\n : undefined\n : request.metadata;\n if (request.expectedOutput === undefined) return metadata;\n return markDatasetItemLabeled(metadata);\n}\n\nasync function langfuseGet<T>(\n creds: LangfuseCreds,\n resource: string,\n path: string,\n): Promise<T> {\n const res = await fetch(`${creds.host.replace(/\\/$/, \"\")}${path}`, {\n method: \"GET\",\n headers: { Authorization: `Basic ${creds.auth}` },\n });\n if (!res.ok) {\n throw new LangfuseApiError(\n res.status,\n await formatLangfuseHttpError(res, resource),\n );\n }\n return res.json() as Promise<T>;\n}\n\nasync function langfuseJson(\n creds: LangfuseCreds,\n resource: string,\n request: { path: string; body: unknown },\n): Promise<unknown> {\n const res = await fetch(`${creds.host.replace(/\\/$/, \"\")}${request.path}`, {\n method: \"POST\",\n headers: {\n Authorization: `Basic ${creds.auth}`,\n \"Content-Type\": \"application/json\",\n },\n body: JSON.stringify(request.body),\n });\n if (!res.ok) {\n throw new LangfuseApiError(\n res.status,\n await formatLangfuseHttpError(res, resource),\n );\n }\n return res.json();\n}\n\nfunction parseMutationRequest(\n value: unknown,\n): { ok: true; value: MutationRequest } | { ok: false; error: string } {\n if (!isRecord(value)) {\n return { ok: false, error: \"Request body must be an object.\" };\n }\n\n const action = stringField(value, \"action\");\n\n if (action === \"create-score\") {\n const traceId = stringField(value, \"traceId\");\n const name = stringField(value, \"name\");\n if (!traceId) return { ok: false, error: \"traceId is required.\" };\n if (!name) return { ok: false, error: \"name is required.\" };\n if (!isScoreValue(value.value)) {\n return {\n ok: false,\n error: \"value must be a number, string, or boolean.\",\n };\n }\n return {\n ok: true,\n value: {\n action,\n traceId,\n name,\n value: value.value,\n comment: stringField(value, \"comment\") ?? undefined,\n metadata: recordField(value, \"metadata\") ?? undefined,\n },\n };\n }\n\n if (action === \"update-dataset-item\") {\n const datasetName = stringField(value, \"datasetName\");\n const itemId = stringField(value, \"itemId\");\n if (!datasetName) return { ok: false, error: \"datasetName is required.\" };\n if (!itemId) return { ok: false, error: \"itemId is required.\" };\n return {\n ok: true,\n value: {\n action,\n datasetName,\n itemId,\n input: value.input,\n expectedOutput: value.expectedOutput,\n metadata: recordOrNullField(value, \"metadata\"),\n sourceTraceId: stringOrNullField(value, \"sourceTraceId\"),\n status: stringOrNullField(value, \"status\"),\n },\n };\n }\n\n if (action === \"create-dataset-run-item\") {\n const datasetItemId = stringField(value, \"datasetItemId\");\n const traceId = stringField(value, \"traceId\");\n const runName = stringField(value, \"runName\");\n if (!datasetItemId)\n return { ok: false, error: \"datasetItemId is required.\" };\n if (!traceId) return { ok: false, error: \"traceId is required.\" };\n if (!runName) return { ok: false, error: \"runName is required.\" };\n return {\n ok: true,\n value: {\n action,\n datasetItemId,\n traceId,\n runName,\n runDescription: stringField(value, \"runDescription\") ?? undefined,\n metadata: recordField(value, \"metadata\") ?? undefined,\n },\n };\n }\n\n return {\n ok: false,\n error:\n \"action must be create-score, update-dataset-item, or create-dataset-run-item.\",\n };\n}\n\nfunction isScoreValue(value: unknown): value is number | string | boolean {\n return (\n (typeof value === \"number\" && Number.isFinite(value)) ||\n typeof value === \"string\" ||\n typeof value === \"boolean\"\n );\n}\n\nfunction recordField(\n value: Record<string, unknown>,\n key: string,\n): Record<string, unknown> | null {\n const field = value[key];\n return isRecord(field) ? field : null;\n}\n\nfunction recordOrNullField(\n value: Record<string, unknown>,\n key: string,\n): Record<string, unknown> | null | undefined {\n const field = value[key];\n if (field === null) return null;\n return isRecord(field) ? field : undefined;\n}\n\nfunction stringOrNullField(\n value: Record<string, unknown>,\n key: string,\n): string | null | undefined {\n const field = value[key];\n if (field === null) return null;\n return typeof field === \"string\" ? field : undefined;\n}\n\nfunction stringField(\n record: Record<string, unknown>,\n key: string,\n): string | null {\n const value = record[key];\n return typeof value === \"string\" && value.trim() ? value.trim() : null;\n}\n\nclass LangfuseApiError extends Error {\n public readonly status: number;\n\n public constructor(status: number, message: string) {\n super(message);\n this.status = status;\n }\n}\n"],"mappings":";;;;;;;;AAgDA,eAA8B,QAAQ,KAAoB,KAAqB;AAC7E,KAAI,IAAI,WAAW,QAAQ;AACzB,MAAI,OAAO,IAAI,CAAC,KAAK,EAAE,OAAO,aAAa,CAAC;AAC5C;;CAGF,MAAM,SAAS,qBAAqB,IAAI,KAAK;AAC7C,KAAI,CAAC,OAAO,IAAI;AACd,MAAI,OAAO,IAAI,CAAC,KAAK,EAAE,OAAO,OAAO,OAAO,CAAC;AAC7C;;CAGF,MAAM,QAAQ,sBAAsB;AACpC,KAAI,CAAC,MAAM,QAAQ,CAAC,MAAM,MAAM;AAC9B,MAAI,YAAY,EAAE;AAChB,OAAI,OAAO,IAAI,CAAC,KAAK;IACnB,MAAM;IACN,SAAS;IACT,YAAY;IACb,CAAC;AACF;;AAEF,MAAI,OAAO,IAAI,CAAC,KAAK;GACnB,OAAO,MAAM,WAAW;GACxB,YAAY,MAAM;GACnB,CAAC;AACF;;CAGF,MAAM,UAAyB;EAAE,MAAM,MAAM;EAAM,MAAM,MAAM;EAAM;AAErE,KAAI;EACF,MAAM,OAAO,MAAM,SAAS,SAAS,OAAO,MAAM;AAClD,MACE,OAAO,MAAM,WAAW,yBACxB,OAAO,MAAM,WAAW,2BACxB;AACA,qBAAkB,OAAO;AACzB,oBAAiB,OAAO;;AAE1B,MAAI,OAAO,IAAI,CAAC,KAAK,EAAE,MAAM,CAAC;UACvB,KAAK;EACZ,MAAM,SAAS,eAAe,mBAAmB,IAAI,SAAS;EAC9D,MAAM,QACJ,eAAe,mBACX,IAAI,UACJ,2BAA2B,IAAI;AACrC,MAAI,OAAO,OAAO,CAAC,KAAK,EAAE,OAAO,CAAC;;;AAItC,eAAe,SACb,OACA,SACkB;AAClB,KAAI,QAAQ,WAAW,eACrB,QAAO,aAAa,OAAO,aAAa;EACtC,MAAM;EACN,MAAM;GACJ,SAAS,QAAQ;GACjB,MAAM,QAAQ;GACd,OAAO,QAAQ;GACf,SAAS,QAAQ;GACjB,UAAU,QAAQ;GACnB;EACF,CAAC;AAGJ,KAAI,QAAQ,WAAW,0BACrB,QAAO,aAAa,OAAO,wBAAwB;EACjD,MAAM;EACN,MAAM;GACJ,eAAe,QAAQ;GACvB,SAAS,QAAQ;GACjB,SAAS,QAAQ;GACjB,gBAAgB,QAAQ;GACxB,UAAU,QAAQ;GACnB;EACF,CAAC;CAWJ,MAAM,WANJ,QAAQ,UAAU,KAAA,KAClB,QAAQ,mBAAmB,KAAA,KAC3B,QAAQ,aAAa,KAAA,KACrB,QAAQ,kBAAkB,KAAA,KAC1B,QAAQ,WAAW,KAAA,IAGjB,OACA,MAAM,YACJ,OACA,oBACA,6BAA6B,mBAAmB,QAAQ,OAAO,GAChE;AACL,QAAO,aAAa,OAAO,oBAAoB;EAC7C,MAAM;EACN,MAAM;GACJ,aAAa,QAAQ;GACrB,IAAI,QAAQ;GACZ,OAAO,QAAQ,UAAU,KAAA,IAAY,UAAU,QAAQ,QAAQ;GAC/D,gBACE,QAAQ,mBAAmB,KAAA,IACvB,UAAU,iBACV,QAAQ;GACd,UAAU,8BAA8B,UAAU,QAAQ;GAC1D,eACE,QAAQ,kBAAkB,KAAA,IACtB,UAAU,gBACV,QAAQ;GACd,QAAQ,QAAQ,WAAW,KAAA,IAAY,UAAU,SAAS,QAAQ;GACnE;EACF,CAAC;;AAGJ,SAAgB,8BACd,UACA,SAC4C;CAC5C,MAAM,mBAAmB,UAAU;CACnC,MAAM,WACJ,QAAQ,aAAa,KAAA,IACjB,SAAS,iBAAiB,IAAI,qBAAqB,OACjD,mBACA,KAAA,IACF,QAAQ;AACd,KAAI,QAAQ,mBAAmB,KAAA,EAAW,QAAO;AACjD,QAAO,uBAAuB,SAAS;;AAGzC,eAAe,YACb,OACA,UACA,MACY;CACZ,MAAM,MAAM,MAAM,MAAM,GAAG,MAAM,KAAK,QAAQ,OAAO,GAAG,GAAG,QAAQ;EACjE,QAAQ;EACR,SAAS,EAAE,eAAe,SAAS,MAAM,QAAQ;EAClD,CAAC;AACF,KAAI,CAAC,IAAI,GACP,OAAM,IAAI,iBACR,IAAI,QACJ,MAAM,wBAAwB,KAAK,SAAS,CAC7C;AAEH,QAAO,IAAI,MAAM;;AAGnB,eAAe,aACb,OACA,UACA,SACkB;CAClB,MAAM,MAAM,MAAM,MAAM,GAAG,MAAM,KAAK,QAAQ,OAAO,GAAG,GAAG,QAAQ,QAAQ;EACzE,QAAQ;EACR,SAAS;GACP,eAAe,SAAS,MAAM;GAC9B,gBAAgB;GACjB;EACD,MAAM,KAAK,UAAU,QAAQ,KAAK;EACnC,CAAC;AACF,KAAI,CAAC,IAAI,GACP,OAAM,IAAI,iBACR,IAAI,QACJ,MAAM,wBAAwB,KAAK,SAAS,CAC7C;AAEH,QAAO,IAAI,MAAM;;AAGnB,SAAS,qBACP,OACqE;AACrE,KAAI,CAAC,SAAS,MAAM,CAClB,QAAO;EAAE,IAAI;EAAO,OAAO;EAAmC;CAGhE,MAAM,SAAS,YAAY,OAAO,SAAS;AAE3C,KAAI,WAAW,gBAAgB;EAC7B,MAAM,UAAU,YAAY,OAAO,UAAU;EAC7C,MAAM,OAAO,YAAY,OAAO,OAAO;AACvC,MAAI,CAAC,QAAS,QAAO;GAAE,IAAI;GAAO,OAAO;GAAwB;AACjE,MAAI,CAAC,KAAM,QAAO;GAAE,IAAI;GAAO,OAAO;GAAqB;AAC3D,MAAI,CAAC,aAAa,MAAM,MAAM,CAC5B,QAAO;GACL,IAAI;GACJ,OAAO;GACR;AAEH,SAAO;GACL,IAAI;GACJ,OAAO;IACL;IACA;IACA;IACA,OAAO,MAAM;IACb,SAAS,YAAY,OAAO,UAAU,IAAI,KAAA;IAC1C,UAAU,YAAY,OAAO,WAAW,IAAI,KAAA;IAC7C;GACF;;AAGH,KAAI,WAAW,uBAAuB;EACpC,MAAM,cAAc,YAAY,OAAO,cAAc;EACrD,MAAM,SAAS,YAAY,OAAO,SAAS;AAC3C,MAAI,CAAC,YAAa,QAAO;GAAE,IAAI;GAAO,OAAO;GAA4B;AACzE,MAAI,CAAC,OAAQ,QAAO;GAAE,IAAI;GAAO,OAAO;GAAuB;AAC/D,SAAO;GACL,IAAI;GACJ,OAAO;IACL;IACA;IACA;IACA,OAAO,MAAM;IACb,gBAAgB,MAAM;IACtB,UAAU,kBAAkB,OAAO,WAAW;IAC9C,eAAe,kBAAkB,OAAO,gBAAgB;IACxD,QAAQ,kBAAkB,OAAO,SAAS;IAC3C;GACF;;AAGH,KAAI,WAAW,2BAA2B;EACxC,MAAM,gBAAgB,YAAY,OAAO,gBAAgB;EACzD,MAAM,UAAU,YAAY,OAAO,UAAU;EAC7C,MAAM,UAAU,YAAY,OAAO,UAAU;AAC7C,MAAI,CAAC,cACH,QAAO;GAAE,IAAI;GAAO,OAAO;GAA8B;AAC3D,MAAI,CAAC,QAAS,QAAO;GAAE,IAAI;GAAO,OAAO;GAAwB;AACjE,MAAI,CAAC,QAAS,QAAO;GAAE,IAAI;GAAO,OAAO;GAAwB;AACjE,SAAO;GACL,IAAI;GACJ,OAAO;IACL;IACA;IACA;IACA;IACA,gBAAgB,YAAY,OAAO,iBAAiB,IAAI,KAAA;IACxD,UAAU,YAAY,OAAO,WAAW,IAAI,KAAA;IAC7C;GACF;;AAGH,QAAO;EACL,IAAI;EACJ,OACE;EACH;;AAGH,SAAS,aAAa,OAAoD;AACxE,QACG,OAAO,UAAU,YAAY,OAAO,SAAS,MAAM,IACpD,OAAO,UAAU,YACjB,OAAO,UAAU;;AAIrB,SAAS,YACP,OACA,KACgC;CAChC,MAAM,QAAQ,MAAM;AACpB,QAAO,SAAS,MAAM,GAAG,QAAQ;;AAGnC,SAAS,kBACP,OACA,KAC4C;CAC5C,MAAM,QAAQ,MAAM;AACpB,KAAI,UAAU,KAAM,QAAO;AAC3B,QAAO,SAAS,MAAM,GAAG,QAAQ,KAAA;;AAGnC,SAAS,kBACP,OACA,KAC2B;CAC3B,MAAM,QAAQ,MAAM;AACpB,KAAI,UAAU,KAAM,QAAO;AAC3B,QAAO,OAAO,UAAU,WAAW,QAAQ,KAAA;;AAG7C,SAAS,YACP,QACA,KACe;CACf,MAAM,QAAQ,OAAO;AACrB,QAAO,OAAO,UAAU,YAAY,MAAM,MAAM,GAAG,MAAM,MAAM,GAAG;;AAGpE,IAAM,mBAAN,cAA+B,MAAM;CACnC;CAEA,YAAmB,QAAgB,SAAiB;AAClD,QAAM,QAAQ;AACd,OAAK,SAAS"}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { markDatasetItemLabeled } from "../../src/lib/dataset-item-labeling.js";
|
|
1
2
|
import { resolveLangfuseCreds } from "../../src/lib/langfuse-creds.js";
|
|
2
3
|
import { DEMO_CONNECTION, addDemoDatasetItemFromTrace, copyDemoDatasetItem, isDemoMode } from "../../src/lib/langfuse-demo.js";
|
|
3
4
|
import { formatLangfuseHttpError, formatLangfuseRequestError } from "../../src/lib/langfuse-errors.js";
|
|
@@ -353,18 +354,20 @@ function parseTrace(value) {
|
|
|
353
354
|
function mergeKaizenMetadata(metadata, input) {
|
|
354
355
|
const base = metadata ? { ...metadata } : {};
|
|
355
356
|
const existingKaizen = isRecord(base.kaizen) ? base.kaizen : {};
|
|
356
|
-
|
|
357
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
358
|
+
const nextMetadata = {
|
|
357
359
|
...base,
|
|
358
360
|
sourceTraceId: input.sourceTraceId ?? base.sourceTraceId,
|
|
359
361
|
kaizen: {
|
|
360
362
|
...existingKaizen,
|
|
361
363
|
notes: input.notes,
|
|
362
364
|
sourceUrl: input.sourceUrl ?? existingKaizen.sourceUrl,
|
|
363
|
-
lastEditedAt:
|
|
365
|
+
lastEditedAt: now,
|
|
364
366
|
lastEditedBy: "kaizen-studio",
|
|
365
367
|
lastAction: input.action
|
|
366
368
|
}
|
|
367
369
|
};
|
|
370
|
+
return input.action === "updated" ? markDatasetItemLabeled(nextMetadata, now) : nextMetadata;
|
|
368
371
|
}
|
|
369
372
|
function getKaizenNotes(metadata) {
|
|
370
373
|
if (!metadata) return "";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"langfuse-dataset-item.js","names":[],"sources":["../../../../dashboard/pages/api/langfuse-dataset-item.ts"],"sourcesContent":["import { resolveLangfuseCreds } from \"../../src/lib/langfuse-creds\";\nimport {\n addDemoDatasetItemFromTrace,\n copyDemoDatasetItem,\n DEMO_CONNECTION,\n isDemoMode,\n} from \"../../src/lib/langfuse-demo\";\nimport {\n formatLangfuseHttpError,\n formatLangfuseRequestError,\n} from \"../../src/lib/langfuse-errors\";\nimport type {\n StudioRequest,\n StudioResponse,\n} from \"../../src/studio/server/compat\";\nimport { datasetItemsCache } from \"./langfuse-dataset\";\nimport { membershipsCache } from \"./langfuse-trace-memberships\";\n\ninterface LangfuseCreds {\n host: string;\n auth: string;\n}\n\ninterface DatasetItem {\n id: string;\n input?: unknown;\n expectedOutput?: unknown;\n metadata?: Record<string, unknown> | null;\n status?: string | null;\n sourceTraceId?: string | null;\n}\n\ninterface Trace {\n id: string;\n input?: unknown;\n output?: unknown;\n metadata?: Record<string, unknown> | null;\n}\n\ntype MutationRequest =\n | {\n action: \"update\";\n datasetName: string;\n itemId: string;\n expectedOutput: unknown;\n notes: string;\n systemId?: string;\n }\n | {\n action: \"archive\";\n datasetName: string;\n itemId: string;\n systemId?: string;\n }\n | {\n action: \"add-from-trace\";\n datasetName: string;\n source: string;\n expectedOutput: unknown;\n notes: string;\n systemId?: string;\n }\n | {\n action: \"copy-item\";\n datasetName: string;\n input: unknown;\n expectedOutput: unknown;\n metadata: Record<string, unknown> | null;\n sourceTraceId: string | null;\n status: string | null;\n notes: string;\n systemId?: string;\n };\n\nexport default async function handler(req: StudioRequest, res: StudioResponse) {\n if (req.method !== \"POST\") {\n res.status(405).json({ error: \"POST only\" });\n return;\n }\n\n const request = parseMutationRequest(req.body);\n if (!request.ok) {\n res.status(400).json({ error: request.error });\n return;\n }\n\n const creds = resolveLangfuseCreds();\n if (!creds.host || !creds.auth) {\n if (isDemoMode()) {\n if (request.value.action === \"add-from-trace\") {\n const traceId = extractLangfuseTraceId(request.value.source);\n if (!traceId) {\n res\n .status(400)\n .json({ error: \"Could not extract trace ID from source\" });\n return;\n }\n const item = addDemoDatasetItemFromTrace(\n request.value.datasetName,\n traceId,\n {\n expectedOutput: request.value.expectedOutput,\n notes: request.value.notes,\n source: request.value.source,\n },\n );\n res.status(200).json({ data: item, connection: DEMO_CONNECTION });\n return;\n }\n if (request.value.action === \"copy-item\") {\n const item = copyDemoDatasetItem(request.value.datasetName, {\n input: request.value.input,\n expectedOutput: request.value.expectedOutput,\n metadata: request.value.metadata,\n sourceTraceId: request.value.sourceTraceId,\n status: request.value.status,\n });\n res.status(200).json({ data: item, connection: DEMO_CONNECTION });\n return;\n }\n res\n .status(200)\n .json({ data: null, success: true, connection: DEMO_CONNECTION });\n return;\n }\n res.status(400).json({\n error: creds.connection.message,\n connection: creds.connection,\n });\n return;\n }\n\n try {\n if (request.value.action === \"add-from-trace\") {\n const data = await addFromTrace(creds, request.value);\n datasetItemsCache.clear();\n membershipsCache.clear();\n res.status(200).json({ data });\n return;\n }\n\n if (request.value.action === \"copy-item\") {\n const data = await copyItem(creds, request.value);\n datasetItemsCache.clear();\n membershipsCache.clear();\n res.status(200).json({ data });\n return;\n }\n\n if (request.value.action === \"archive\") {\n const data = await archiveDatasetItem(creds, request.value);\n datasetItemsCache.clear();\n membershipsCache.clear();\n res.status(200).json({ data });\n return;\n }\n\n const data = await updateDatasetItem(creds, request.value);\n datasetItemsCache.clear();\n membershipsCache.clear();\n res.status(200).json({ data });\n } catch (err) {\n const status = err instanceof LangfuseApiError ? err.status : 502;\n const error =\n err instanceof LangfuseApiError\n ? err.message\n : formatLangfuseRequestError(err);\n res.status(status).json({ error });\n }\n}\n\nasync function addFromTrace(\n creds: LangfuseCreds,\n request: Extract<MutationRequest, { action: \"add-from-trace\" }>,\n): Promise<DatasetItem> {\n const traceId = extractLangfuseTraceId(request.source);\n if (!traceId) {\n throw new LangfuseApiError(\n 400,\n \"Paste a Langfuse trace URL or trace ID. Orbit URL to trace lookup will be added separately.\",\n );\n }\n\n const trace = await getTrace(creds, traceId);\n // Langfuse rejects null input on dataset-item create. The item's input\n // mirrors the trace input; the user is expected to fill in the\n // expectedOutput as ground truth, so we keep it empty unless the\n // caller supplied one. The trace output is preserved under\n // metadata.kaizen.sourceOutput for reference.\n const baseMetadata = mergeKaizenMetadata(trace.metadata, {\n notes: request.notes,\n sourceTraceId: trace.id,\n sourceUrl: request.source.startsWith(\"http\") ? request.source : null,\n action: \"added\",\n });\n const metadataWithSourceOutput =\n trace.output === undefined || trace.output === null\n ? baseMetadata\n : {\n ...baseMetadata,\n kaizen: {\n ...(isRecord(baseMetadata.kaizen) ? baseMetadata.kaizen : {}),\n sourceOutput: trace.output,\n },\n };\n return upsertDatasetItem(creds, {\n datasetName: request.datasetName,\n input: trace.input ?? {},\n expectedOutput:\n request.expectedOutput === undefined ? {} : request.expectedOutput,\n metadata: metadataWithSourceOutput,\n sourceTraceId: trace.id,\n });\n}\n\nasync function copyItem(\n creds: LangfuseCreds,\n request: Extract<MutationRequest, { action: \"copy-item\" }>,\n): Promise<DatasetItem> {\n return upsertDatasetItem(creds, {\n datasetName: request.datasetName,\n input: request.input,\n expectedOutput: request.expectedOutput,\n metadata: request.metadata\n ? {\n ...request.metadata,\n kaizen: {\n ...(isRecord(request.metadata.kaizen)\n ? request.metadata.kaizen\n : {}),\n lastEditedAt: new Date().toISOString(),\n lastEditedBy: \"kaizen-studio\",\n lastAction: \"copied\",\n },\n }\n : null,\n sourceTraceId: request.sourceTraceId,\n status: request.status,\n });\n}\n\nasync function updateDatasetItem(\n creds: LangfuseCreds,\n request: Extract<MutationRequest, { action: \"update\" }>,\n): Promise<DatasetItem> {\n const item = await getDatasetItem(creds, request.itemId);\n return upsertDatasetItem(creds, {\n datasetName: request.datasetName,\n id: item.id,\n input: item.input,\n expectedOutput: request.expectedOutput,\n metadata: mergeKaizenMetadata(item.metadata, {\n notes: request.notes,\n sourceTraceId: getSourceTraceId(item),\n sourceUrl: null,\n action: \"updated\",\n }),\n sourceTraceId: item.sourceTraceId,\n status: item.status,\n });\n}\n\nasync function archiveDatasetItem(\n creds: LangfuseCreds,\n request: Extract<MutationRequest, { action: \"archive\" }>,\n): Promise<DatasetItem> {\n const item = await getDatasetItem(creds, request.itemId);\n return upsertDatasetItem(creds, {\n datasetName: request.datasetName,\n id: item.id,\n input: item.input,\n expectedOutput: item.expectedOutput,\n metadata: mergeKaizenMetadata(item.metadata, {\n notes: getKaizenNotes(item.metadata),\n sourceTraceId: getSourceTraceId(item),\n sourceUrl: null,\n action: \"archived\",\n }),\n sourceTraceId: item.sourceTraceId,\n status: \"ARCHIVED\",\n });\n}\n\nasync function getDatasetItem(\n creds: LangfuseCreds,\n itemId: string,\n): Promise<DatasetItem> {\n const data = await langfuseJson(creds, \"the dataset item\", {\n path: `/api/public/dataset-items/${encodeURIComponent(itemId)}`,\n method: \"GET\",\n });\n const item = parseDatasetItem(data);\n if (!item) {\n throw new LangfuseApiError(\n 502,\n \"Langfuse returned a dataset item without an id.\",\n );\n }\n return item;\n}\n\nasync function getTrace(creds: LangfuseCreds, traceId: string): Promise<Trace> {\n const data = await langfuseJson(creds, \"the trace\", {\n path: `/api/public/traces/${encodeURIComponent(traceId)}`,\n method: \"GET\",\n });\n const trace = parseTrace(data);\n if (!trace) {\n throw new LangfuseApiError(502, \"Langfuse returned a trace without an id.\");\n }\n return trace;\n}\n\nasync function upsertDatasetItem(\n creds: LangfuseCreds,\n item: {\n datasetName: string;\n id?: string;\n input?: unknown;\n expectedOutput?: unknown;\n metadata?: Record<string, unknown> | null;\n sourceTraceId?: string | null;\n status?: string | null;\n },\n): Promise<DatasetItem> {\n const body = {\n datasetName: item.datasetName,\n id: item.id,\n input: item.input,\n expectedOutput: item.expectedOutput,\n metadata: item.metadata,\n sourceTraceId: item.sourceTraceId,\n status: item.status ?? undefined,\n };\n const data = await langfuseJson(creds, \"the dataset item\", {\n path: \"/api/public/dataset-items\",\n method: \"POST\",\n body,\n });\n const parsed = parseDatasetItem(data);\n if (!parsed) {\n throw new LangfuseApiError(\n 502,\n \"Langfuse updated the dataset item but returned an unexpected response.\",\n );\n }\n return parsed;\n}\n\nasync function langfuseJson(\n creds: LangfuseCreds,\n resource: string,\n request: {\n path: string;\n method: \"GET\" | \"POST\";\n body?: unknown;\n },\n): Promise<unknown> {\n // Only retry GETs — POSTs aren't safe to repeat (would double-insert).\n const maxAttempts = request.method === \"GET\" ? 3 : 1;\n let lastRes: Response | null = null;\n for (let attempt = 1; attempt <= maxAttempts; attempt++) {\n const res = await fetch(`${creds.host.replace(/\\/$/, \"\")}${request.path}`, {\n method: request.method,\n headers: {\n Authorization: `Basic ${creds.auth}`,\n ...(request.body === undefined\n ? {}\n : { \"Content-Type\": \"application/json\" }),\n },\n body:\n request.body === undefined ? undefined : JSON.stringify(request.body),\n });\n if (res.ok) return res.json();\n lastRes = res;\n if (attempt < maxAttempts && isTransientGatewayStatus(res.status)) {\n await sleep(400 * 2 ** (attempt - 1));\n continue;\n }\n break;\n }\n throw new LangfuseApiError(\n lastRes!.status,\n await formatLangfuseHttpError(lastRes!, resource),\n );\n}\n\nfunction isTransientGatewayStatus(status: number): boolean {\n return status === 502 || status === 503 || status === 504 || status === 524;\n}\n\nfunction sleep(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n}\n\nfunction parseMutationRequest(\n value: unknown,\n): { ok: true; value: MutationRequest } | { ok: false; error: string } {\n if (!isRecord(value)) {\n return { ok: false, error: \"Request body must be an object.\" };\n }\n\n const action = stringField(value, \"action\");\n const datasetName = stringField(value, \"datasetName\");\n if (!datasetName) {\n return { ok: false, error: \"datasetName is required.\" };\n }\n\n const systemId = stringField(value, \"systemId\") ?? undefined;\n if (action === \"update\") {\n const itemId = stringField(value, \"itemId\");\n if (!itemId) return { ok: false, error: \"itemId is required.\" };\n return {\n ok: true,\n value: {\n action,\n datasetName,\n itemId,\n expectedOutput: value.expectedOutput,\n notes: stringField(value, \"notes\") ?? \"\",\n systemId,\n },\n };\n }\n\n if (action === \"archive\") {\n const itemId = stringField(value, \"itemId\");\n if (!itemId) return { ok: false, error: \"itemId is required.\" };\n return {\n ok: true,\n value: {\n action,\n datasetName,\n itemId,\n systemId,\n },\n };\n }\n\n if (action === \"add-from-trace\") {\n const source = stringField(value, \"source\");\n if (!source) return { ok: false, error: \"source is required.\" };\n return {\n ok: true,\n value: {\n action,\n datasetName,\n source,\n expectedOutput: value.expectedOutput,\n notes: stringField(value, \"notes\") ?? \"\",\n systemId,\n },\n };\n }\n\n if (action === \"copy-item\") {\n return {\n ok: true,\n value: {\n action,\n datasetName,\n input: value.input,\n expectedOutput: value.expectedOutput,\n metadata:\n isRecord(value.metadata) || value.metadata === null\n ? (value.metadata as Record<string, unknown> | null)\n : null,\n sourceTraceId:\n typeof value.sourceTraceId === \"string\" ? value.sourceTraceId : null,\n status: typeof value.status === \"string\" ? value.status : null,\n notes: stringField(value, \"notes\") ?? \"\",\n systemId,\n },\n };\n }\n\n return {\n ok: false,\n error: \"action must be update, archive, add-from-trace, or copy-item.\",\n };\n}\n\nexport function extractLangfuseTraceId(source: string): string | null {\n const trimmed = source.trim();\n if (!trimmed) return null;\n\n if (!looksLikeUrl(trimmed)) return trimmed;\n\n try {\n const url = new URL(trimmed);\n const traceId =\n url.searchParams.get(\"traceId\") ?? url.searchParams.get(\"trace\");\n if (traceId) return traceId;\n\n const parts = url.pathname.split(\"/\").filter(Boolean);\n const traceIndex = parts.findIndex(\n (part) => part === \"traces\" || part === \"trace\",\n );\n if (traceIndex >= 0) return parts[traceIndex + 1] ?? null;\n } catch {\n return null;\n }\n\n return null;\n}\n\nfunction parseDatasetItem(value: unknown): DatasetItem | null {\n if (!isRecord(value) || typeof value.id !== \"string\") return null;\n return {\n id: value.id,\n input: value.input,\n expectedOutput: value.expectedOutput,\n metadata:\n isRecord(value.metadata) || value.metadata === null\n ? value.metadata\n : undefined,\n status:\n typeof value.status === \"string\" || value.status === null\n ? value.status\n : undefined,\n sourceTraceId:\n typeof value.sourceTraceId === \"string\" || value.sourceTraceId === null\n ? value.sourceTraceId\n : undefined,\n };\n}\n\nfunction parseTrace(value: unknown): Trace | null {\n if (!isRecord(value) || typeof value.id !== \"string\") return null;\n return {\n id: value.id,\n input: value.input,\n output: value.output,\n metadata:\n isRecord(value.metadata) || value.metadata === null\n ? value.metadata\n : undefined,\n };\n}\n\nfunction mergeKaizenMetadata(\n metadata: Record<string, unknown> | null | undefined,\n input: {\n notes: string;\n sourceTraceId: string | null;\n sourceUrl: string | null;\n action: \"added\" | \"updated\" | \"archived\";\n },\n): Record<string, unknown> {\n const base = metadata ? { ...metadata } : {};\n const existingKaizen = isRecord(base.kaizen) ? base.kaizen : {};\n return {\n ...base,\n sourceTraceId: input.sourceTraceId ?? base.sourceTraceId,\n kaizen: {\n ...existingKaizen,\n notes: input.notes,\n sourceUrl: input.sourceUrl ?? existingKaizen.sourceUrl,\n lastEditedAt: new Date().toISOString(),\n lastEditedBy: \"kaizen-studio\",\n lastAction: input.action,\n },\n };\n}\n\nfunction getKaizenNotes(\n metadata: Record<string, unknown> | null | undefined,\n): string {\n if (!metadata) return \"\";\n if (isRecord(metadata.kaizen) && typeof metadata.kaizen.notes === \"string\") {\n return metadata.kaizen.notes;\n }\n return typeof metadata.notes === \"string\" ? metadata.notes : \"\";\n}\n\nfunction getSourceTraceId(item: DatasetItem): string | null {\n if (item.sourceTraceId) return item.sourceTraceId;\n const metadata = item.metadata;\n if (!metadata) return null;\n return typeof metadata.sourceTraceId === \"string\"\n ? metadata.sourceTraceId\n : null;\n}\n\nfunction stringField(\n record: Record<string, unknown>,\n key: string,\n): string | null {\n const value = record[key];\n return typeof value === \"string\" && value.trim() ? value.trim() : null;\n}\n\nfunction looksLikeUrl(value: string): boolean {\n return value.startsWith(\"http://\") || value.startsWith(\"https://\");\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === \"object\" && value !== null && !Array.isArray(value);\n}\n\nclass LangfuseApiError extends Error {\n public readonly status: number;\n\n public constructor(status: number, message: string) {\n super(message);\n this.status = status;\n }\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"langfuse-dataset-item.js","names":[],"sources":["../../../../dashboard/pages/api/langfuse-dataset-item.ts"],"sourcesContent":["import { markDatasetItemLabeled } from \"../../src/lib/dataset-item-labeling\";\nimport { resolveLangfuseCreds } from \"../../src/lib/langfuse-creds\";\nimport {\n addDemoDatasetItemFromTrace,\n copyDemoDatasetItem,\n DEMO_CONNECTION,\n isDemoMode,\n} from \"../../src/lib/langfuse-demo\";\nimport {\n formatLangfuseHttpError,\n formatLangfuseRequestError,\n} from \"../../src/lib/langfuse-errors\";\nimport type {\n StudioRequest,\n StudioResponse,\n} from \"../../src/studio/server/compat\";\nimport { datasetItemsCache } from \"./langfuse-dataset\";\nimport { membershipsCache } from \"./langfuse-trace-memberships\";\n\ninterface LangfuseCreds {\n host: string;\n auth: string;\n}\n\ninterface DatasetItem {\n id: string;\n input?: unknown;\n expectedOutput?: unknown;\n metadata?: Record<string, unknown> | null;\n status?: string | null;\n sourceTraceId?: string | null;\n}\n\ninterface Trace {\n id: string;\n input?: unknown;\n output?: unknown;\n metadata?: Record<string, unknown> | null;\n}\n\ntype MutationRequest =\n | {\n action: \"update\";\n datasetName: string;\n itemId: string;\n expectedOutput: unknown;\n notes: string;\n systemId?: string;\n }\n | {\n action: \"archive\";\n datasetName: string;\n itemId: string;\n systemId?: string;\n }\n | {\n action: \"add-from-trace\";\n datasetName: string;\n source: string;\n expectedOutput: unknown;\n notes: string;\n systemId?: string;\n }\n | {\n action: \"copy-item\";\n datasetName: string;\n input: unknown;\n expectedOutput: unknown;\n metadata: Record<string, unknown> | null;\n sourceTraceId: string | null;\n status: string | null;\n notes: string;\n systemId?: string;\n };\n\nexport default async function handler(req: StudioRequest, res: StudioResponse) {\n if (req.method !== \"POST\") {\n res.status(405).json({ error: \"POST only\" });\n return;\n }\n\n const request = parseMutationRequest(req.body);\n if (!request.ok) {\n res.status(400).json({ error: request.error });\n return;\n }\n\n const creds = resolveLangfuseCreds();\n if (!creds.host || !creds.auth) {\n if (isDemoMode()) {\n if (request.value.action === \"add-from-trace\") {\n const traceId = extractLangfuseTraceId(request.value.source);\n if (!traceId) {\n res\n .status(400)\n .json({ error: \"Could not extract trace ID from source\" });\n return;\n }\n const item = addDemoDatasetItemFromTrace(\n request.value.datasetName,\n traceId,\n {\n expectedOutput: request.value.expectedOutput,\n notes: request.value.notes,\n source: request.value.source,\n },\n );\n res.status(200).json({ data: item, connection: DEMO_CONNECTION });\n return;\n }\n if (request.value.action === \"copy-item\") {\n const item = copyDemoDatasetItem(request.value.datasetName, {\n input: request.value.input,\n expectedOutput: request.value.expectedOutput,\n metadata: request.value.metadata,\n sourceTraceId: request.value.sourceTraceId,\n status: request.value.status,\n });\n res.status(200).json({ data: item, connection: DEMO_CONNECTION });\n return;\n }\n res\n .status(200)\n .json({ data: null, success: true, connection: DEMO_CONNECTION });\n return;\n }\n res.status(400).json({\n error: creds.connection.message,\n connection: creds.connection,\n });\n return;\n }\n\n try {\n if (request.value.action === \"add-from-trace\") {\n const data = await addFromTrace(creds, request.value);\n datasetItemsCache.clear();\n membershipsCache.clear();\n res.status(200).json({ data });\n return;\n }\n\n if (request.value.action === \"copy-item\") {\n const data = await copyItem(creds, request.value);\n datasetItemsCache.clear();\n membershipsCache.clear();\n res.status(200).json({ data });\n return;\n }\n\n if (request.value.action === \"archive\") {\n const data = await archiveDatasetItem(creds, request.value);\n datasetItemsCache.clear();\n membershipsCache.clear();\n res.status(200).json({ data });\n return;\n }\n\n const data = await updateDatasetItem(creds, request.value);\n datasetItemsCache.clear();\n membershipsCache.clear();\n res.status(200).json({ data });\n } catch (err) {\n const status = err instanceof LangfuseApiError ? err.status : 502;\n const error =\n err instanceof LangfuseApiError\n ? err.message\n : formatLangfuseRequestError(err);\n res.status(status).json({ error });\n }\n}\n\nasync function addFromTrace(\n creds: LangfuseCreds,\n request: Extract<MutationRequest, { action: \"add-from-trace\" }>,\n): Promise<DatasetItem> {\n const traceId = extractLangfuseTraceId(request.source);\n if (!traceId) {\n throw new LangfuseApiError(\n 400,\n \"Paste a Langfuse trace URL or trace ID. Orbit URL to trace lookup will be added separately.\",\n );\n }\n\n const trace = await getTrace(creds, traceId);\n // Langfuse rejects null input on dataset-item create. The item's input\n // mirrors the trace input; the user is expected to fill in the\n // expectedOutput as ground truth, so we keep it empty unless the\n // caller supplied one. The trace output is preserved under\n // metadata.kaizen.sourceOutput for reference.\n const baseMetadata = mergeKaizenMetadata(trace.metadata, {\n notes: request.notes,\n sourceTraceId: trace.id,\n sourceUrl: request.source.startsWith(\"http\") ? request.source : null,\n action: \"added\",\n });\n const metadataWithSourceOutput =\n trace.output === undefined || trace.output === null\n ? baseMetadata\n : {\n ...baseMetadata,\n kaizen: {\n ...(isRecord(baseMetadata.kaizen) ? baseMetadata.kaizen : {}),\n sourceOutput: trace.output,\n },\n };\n return upsertDatasetItem(creds, {\n datasetName: request.datasetName,\n input: trace.input ?? {},\n expectedOutput:\n request.expectedOutput === undefined ? {} : request.expectedOutput,\n metadata: metadataWithSourceOutput,\n sourceTraceId: trace.id,\n });\n}\n\nasync function copyItem(\n creds: LangfuseCreds,\n request: Extract<MutationRequest, { action: \"copy-item\" }>,\n): Promise<DatasetItem> {\n return upsertDatasetItem(creds, {\n datasetName: request.datasetName,\n input: request.input,\n expectedOutput: request.expectedOutput,\n metadata: request.metadata\n ? {\n ...request.metadata,\n kaizen: {\n ...(isRecord(request.metadata.kaizen)\n ? request.metadata.kaizen\n : {}),\n lastEditedAt: new Date().toISOString(),\n lastEditedBy: \"kaizen-studio\",\n lastAction: \"copied\",\n },\n }\n : null,\n sourceTraceId: request.sourceTraceId,\n status: request.status,\n });\n}\n\nasync function updateDatasetItem(\n creds: LangfuseCreds,\n request: Extract<MutationRequest, { action: \"update\" }>,\n): Promise<DatasetItem> {\n const item = await getDatasetItem(creds, request.itemId);\n return upsertDatasetItem(creds, {\n datasetName: request.datasetName,\n id: item.id,\n input: item.input,\n expectedOutput: request.expectedOutput,\n metadata: mergeKaizenMetadata(item.metadata, {\n notes: request.notes,\n sourceTraceId: getSourceTraceId(item),\n sourceUrl: null,\n action: \"updated\",\n }),\n sourceTraceId: item.sourceTraceId,\n status: item.status,\n });\n}\n\nasync function archiveDatasetItem(\n creds: LangfuseCreds,\n request: Extract<MutationRequest, { action: \"archive\" }>,\n): Promise<DatasetItem> {\n const item = await getDatasetItem(creds, request.itemId);\n return upsertDatasetItem(creds, {\n datasetName: request.datasetName,\n id: item.id,\n input: item.input,\n expectedOutput: item.expectedOutput,\n metadata: mergeKaizenMetadata(item.metadata, {\n notes: getKaizenNotes(item.metadata),\n sourceTraceId: getSourceTraceId(item),\n sourceUrl: null,\n action: \"archived\",\n }),\n sourceTraceId: item.sourceTraceId,\n status: \"ARCHIVED\",\n });\n}\n\nasync function getDatasetItem(\n creds: LangfuseCreds,\n itemId: string,\n): Promise<DatasetItem> {\n const data = await langfuseJson(creds, \"the dataset item\", {\n path: `/api/public/dataset-items/${encodeURIComponent(itemId)}`,\n method: \"GET\",\n });\n const item = parseDatasetItem(data);\n if (!item) {\n throw new LangfuseApiError(\n 502,\n \"Langfuse returned a dataset item without an id.\",\n );\n }\n return item;\n}\n\nasync function getTrace(creds: LangfuseCreds, traceId: string): Promise<Trace> {\n const data = await langfuseJson(creds, \"the trace\", {\n path: `/api/public/traces/${encodeURIComponent(traceId)}`,\n method: \"GET\",\n });\n const trace = parseTrace(data);\n if (!trace) {\n throw new LangfuseApiError(502, \"Langfuse returned a trace without an id.\");\n }\n return trace;\n}\n\nasync function upsertDatasetItem(\n creds: LangfuseCreds,\n item: {\n datasetName: string;\n id?: string;\n input?: unknown;\n expectedOutput?: unknown;\n metadata?: Record<string, unknown> | null;\n sourceTraceId?: string | null;\n status?: string | null;\n },\n): Promise<DatasetItem> {\n const body = {\n datasetName: item.datasetName,\n id: item.id,\n input: item.input,\n expectedOutput: item.expectedOutput,\n metadata: item.metadata,\n sourceTraceId: item.sourceTraceId,\n status: item.status ?? undefined,\n };\n const data = await langfuseJson(creds, \"the dataset item\", {\n path: \"/api/public/dataset-items\",\n method: \"POST\",\n body,\n });\n const parsed = parseDatasetItem(data);\n if (!parsed) {\n throw new LangfuseApiError(\n 502,\n \"Langfuse updated the dataset item but returned an unexpected response.\",\n );\n }\n return parsed;\n}\n\nasync function langfuseJson(\n creds: LangfuseCreds,\n resource: string,\n request: {\n path: string;\n method: \"GET\" | \"POST\";\n body?: unknown;\n },\n): Promise<unknown> {\n // Only retry GETs — POSTs aren't safe to repeat (would double-insert).\n const maxAttempts = request.method === \"GET\" ? 3 : 1;\n let lastRes: Response | null = null;\n for (let attempt = 1; attempt <= maxAttempts; attempt++) {\n const res = await fetch(`${creds.host.replace(/\\/$/, \"\")}${request.path}`, {\n method: request.method,\n headers: {\n Authorization: `Basic ${creds.auth}`,\n ...(request.body === undefined\n ? {}\n : { \"Content-Type\": \"application/json\" }),\n },\n body:\n request.body === undefined ? undefined : JSON.stringify(request.body),\n });\n if (res.ok) return res.json();\n lastRes = res;\n if (attempt < maxAttempts && isTransientGatewayStatus(res.status)) {\n await sleep(400 * 2 ** (attempt - 1));\n continue;\n }\n break;\n }\n throw new LangfuseApiError(\n lastRes!.status,\n await formatLangfuseHttpError(lastRes!, resource),\n );\n}\n\nfunction isTransientGatewayStatus(status: number): boolean {\n return status === 502 || status === 503 || status === 504 || status === 524;\n}\n\nfunction sleep(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n}\n\nfunction parseMutationRequest(\n value: unknown,\n): { ok: true; value: MutationRequest } | { ok: false; error: string } {\n if (!isRecord(value)) {\n return { ok: false, error: \"Request body must be an object.\" };\n }\n\n const action = stringField(value, \"action\");\n const datasetName = stringField(value, \"datasetName\");\n if (!datasetName) {\n return { ok: false, error: \"datasetName is required.\" };\n }\n\n const systemId = stringField(value, \"systemId\") ?? undefined;\n if (action === \"update\") {\n const itemId = stringField(value, \"itemId\");\n if (!itemId) return { ok: false, error: \"itemId is required.\" };\n return {\n ok: true,\n value: {\n action,\n datasetName,\n itemId,\n expectedOutput: value.expectedOutput,\n notes: stringField(value, \"notes\") ?? \"\",\n systemId,\n },\n };\n }\n\n if (action === \"archive\") {\n const itemId = stringField(value, \"itemId\");\n if (!itemId) return { ok: false, error: \"itemId is required.\" };\n return {\n ok: true,\n value: {\n action,\n datasetName,\n itemId,\n systemId,\n },\n };\n }\n\n if (action === \"add-from-trace\") {\n const source = stringField(value, \"source\");\n if (!source) return { ok: false, error: \"source is required.\" };\n return {\n ok: true,\n value: {\n action,\n datasetName,\n source,\n expectedOutput: value.expectedOutput,\n notes: stringField(value, \"notes\") ?? \"\",\n systemId,\n },\n };\n }\n\n if (action === \"copy-item\") {\n return {\n ok: true,\n value: {\n action,\n datasetName,\n input: value.input,\n expectedOutput: value.expectedOutput,\n metadata:\n isRecord(value.metadata) || value.metadata === null\n ? (value.metadata as Record<string, unknown> | null)\n : null,\n sourceTraceId:\n typeof value.sourceTraceId === \"string\" ? value.sourceTraceId : null,\n status: typeof value.status === \"string\" ? value.status : null,\n notes: stringField(value, \"notes\") ?? \"\",\n systemId,\n },\n };\n }\n\n return {\n ok: false,\n error: \"action must be update, archive, add-from-trace, or copy-item.\",\n };\n}\n\nexport function extractLangfuseTraceId(source: string): string | null {\n const trimmed = source.trim();\n if (!trimmed) return null;\n\n if (!looksLikeUrl(trimmed)) return trimmed;\n\n try {\n const url = new URL(trimmed);\n const traceId =\n url.searchParams.get(\"traceId\") ?? url.searchParams.get(\"trace\");\n if (traceId) return traceId;\n\n const parts = url.pathname.split(\"/\").filter(Boolean);\n const traceIndex = parts.findIndex(\n (part) => part === \"traces\" || part === \"trace\",\n );\n if (traceIndex >= 0) return parts[traceIndex + 1] ?? null;\n } catch {\n return null;\n }\n\n return null;\n}\n\nfunction parseDatasetItem(value: unknown): DatasetItem | null {\n if (!isRecord(value) || typeof value.id !== \"string\") return null;\n return {\n id: value.id,\n input: value.input,\n expectedOutput: value.expectedOutput,\n metadata:\n isRecord(value.metadata) || value.metadata === null\n ? value.metadata\n : undefined,\n status:\n typeof value.status === \"string\" || value.status === null\n ? value.status\n : undefined,\n sourceTraceId:\n typeof value.sourceTraceId === \"string\" || value.sourceTraceId === null\n ? value.sourceTraceId\n : undefined,\n };\n}\n\nfunction parseTrace(value: unknown): Trace | null {\n if (!isRecord(value) || typeof value.id !== \"string\") return null;\n return {\n id: value.id,\n input: value.input,\n output: value.output,\n metadata:\n isRecord(value.metadata) || value.metadata === null\n ? value.metadata\n : undefined,\n };\n}\n\nexport function mergeKaizenMetadata(\n metadata: Record<string, unknown> | null | undefined,\n input: {\n notes: string;\n sourceTraceId: string | null;\n sourceUrl: string | null;\n action: \"added\" | \"updated\" | \"archived\";\n },\n): Record<string, unknown> {\n const base = metadata ? { ...metadata } : {};\n const existingKaizen = isRecord(base.kaizen) ? base.kaizen : {};\n const now = new Date().toISOString();\n const nextMetadata = {\n ...base,\n sourceTraceId: input.sourceTraceId ?? base.sourceTraceId,\n kaizen: {\n ...existingKaizen,\n notes: input.notes,\n sourceUrl: input.sourceUrl ?? existingKaizen.sourceUrl,\n lastEditedAt: now,\n lastEditedBy: \"kaizen-studio\",\n lastAction: input.action,\n },\n };\n return input.action === \"updated\"\n ? markDatasetItemLabeled(nextMetadata, now)\n : nextMetadata;\n}\n\nfunction getKaizenNotes(\n metadata: Record<string, unknown> | null | undefined,\n): string {\n if (!metadata) return \"\";\n if (isRecord(metadata.kaizen) && typeof metadata.kaizen.notes === \"string\") {\n return metadata.kaizen.notes;\n }\n return typeof metadata.notes === \"string\" ? metadata.notes : \"\";\n}\n\nfunction getSourceTraceId(item: DatasetItem): string | null {\n if (item.sourceTraceId) return item.sourceTraceId;\n const metadata = item.metadata;\n if (!metadata) return null;\n return typeof metadata.sourceTraceId === \"string\"\n ? metadata.sourceTraceId\n : null;\n}\n\nfunction stringField(\n record: Record<string, unknown>,\n key: string,\n): string | null {\n const value = record[key];\n return typeof value === \"string\" && value.trim() ? value.trim() : null;\n}\n\nfunction looksLikeUrl(value: string): boolean {\n return value.startsWith(\"http://\") || value.startsWith(\"https://\");\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === \"object\" && value !== null && !Array.isArray(value);\n}\n\nclass LangfuseApiError extends Error {\n public readonly status: number;\n\n public constructor(status: number, message: string) {\n super(message);\n this.status = status;\n }\n}\n"],"mappings":"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"}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
+
import { getSourceTraceId, isRecord } from "../../src/lib/langfuse-helpers.js";
|
|
1
2
|
import { resolveLangfuseCreds } from "../../src/lib/langfuse-creds.js";
|
|
2
3
|
import { DEMO_CONNECTION, createDemoDataset, deleteDemoDatasetItem, deleteDemoDatasetItems, isDemoMode, renameDemoDataset } from "../../src/lib/langfuse-demo.js";
|
|
3
4
|
import { formatLangfuseHttpError, formatLangfuseRequestError } from "../../src/lib/langfuse-errors.js";
|
|
4
|
-
import { getSourceTraceId, isRecord } from "../../src/lib/langfuse-helpers.js";
|
|
5
5
|
import { datasetItemsCache } from "./langfuse-dataset.js";
|
|
6
6
|
import { membershipsCache } from "./langfuse-trace-memberships.js";
|
|
7
7
|
//#region dashboard/pages/api/langfuse-dataset-mutation.ts
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
+
import { getSourceTraceId, isRecord } from "../../src/lib/langfuse-helpers.js";
|
|
1
2
|
import { langfuseStatus, resolveLangfuseCreds } from "../../src/lib/langfuse-creds.js";
|
|
2
3
|
import { DEMO_CONNECTION, getDemoDatasetItems, getDemoDatasets, isDemoMode } from "../../src/lib/langfuse-demo.js";
|
|
3
4
|
import { formatLangfuseHttpError, formatLangfuseRequestError } from "../../src/lib/langfuse-errors.js";
|
|
4
|
-
import { getSourceTraceId, isRecord } from "../../src/lib/langfuse-helpers.js";
|
|
5
5
|
import { createTtlCache } from "../../src/lib/langfuse-cache.js";
|
|
6
6
|
//#region dashboard/pages/api/langfuse-trace-memberships.ts
|
|
7
7
|
const membershipsCache = createTtlCache({
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { isRecord } from "./langfuse-helpers.js";
|
|
2
|
+
//#region dashboard/src/lib/dataset-item-labeling.ts
|
|
3
|
+
const LABEL_UPDATED_BY = "kaizen-studio";
|
|
4
|
+
function markDatasetItemLabeled(metadata, timestamp = (/* @__PURE__ */ new Date()).toISOString()) {
|
|
5
|
+
const base = metadata ? { ...metadata } : {};
|
|
6
|
+
const kaizen = isRecord(base.kaizen) ? base.kaizen : {};
|
|
7
|
+
return {
|
|
8
|
+
...base,
|
|
9
|
+
kaizen: {
|
|
10
|
+
...kaizen,
|
|
11
|
+
isLabeled: true,
|
|
12
|
+
labelUpdatedAt: timestamp,
|
|
13
|
+
labelUpdatedBy: LABEL_UPDATED_BY
|
|
14
|
+
}
|
|
15
|
+
};
|
|
16
|
+
}
|
|
17
|
+
//#endregion
|
|
18
|
+
export { markDatasetItemLabeled };
|
|
19
|
+
|
|
20
|
+
//# sourceMappingURL=dataset-item-labeling.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"dataset-item-labeling.js","names":[],"sources":["../../../../dashboard/src/lib/dataset-item-labeling.ts"],"sourcesContent":["import { isRecord } from \"./langfuse-helpers\";\n\nconst LABEL_UPDATED_BY = \"kaizen-studio\";\n\nexport function markDatasetItemLabeled(\n metadata: Record<string, unknown> | null | undefined,\n timestamp = new Date().toISOString(),\n): Record<string, unknown> {\n const base = metadata ? { ...metadata } : {};\n const kaizen = isRecord(base.kaizen) ? base.kaizen : {};\n return {\n ...base,\n kaizen: {\n ...kaizen,\n isLabeled: true,\n labelUpdatedAt: timestamp,\n labelUpdatedBy: LABEL_UPDATED_BY,\n },\n };\n}\n"],"mappings":";;AAEA,MAAM,mBAAmB;AAEzB,SAAgB,uBACd,UACA,6BAAY,IAAI,MAAM,EAAC,aAAa,EACX;CACzB,MAAM,OAAO,WAAW,EAAE,GAAG,UAAU,GAAG,EAAE;CAC5C,MAAM,SAAS,SAAS,KAAK,OAAO,GAAG,KAAK,SAAS,EAAE;AACvD,QAAO;EACL,GAAG;EACH,QAAQ;GACN,GAAG;GACH,WAAW;GACX,gBAAgB;GAChB,gBAAgB;GACjB;EACF"}
|