@langfuse/client 4.3.0 → 4.4.0
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/index.cjs +79 -36
- package/dist/index.cjs.map +1 -1
- package/dist/index.mjs +88 -37
- package/dist/index.mjs.map +1 -1
- package/package.json +3 -3
package/dist/index.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/LangfuseClient.ts","../src/dataset/index.ts","../src/experiment/ExperimentManager.ts","../src/media/index.ts","../src/prompt/promptManager.ts","../src/prompt/promptCache.ts","../src/prompt/promptClients.ts","../src/prompt/types.ts","../src/score/index.ts","../src/experiment/adapters.ts"],"sourcesContent":["import {\n LangfuseAPIClient,\n LANGFUSE_SDK_VERSION,\n getGlobalLogger,\n getEnv,\n} from \"@langfuse/core\";\n\nimport { DatasetManager } from \"./dataset/index.js\";\nimport { ExperimentManager } from \"./experiment/ExperimentManager.js\";\nimport { MediaManager } from \"./media/index.js\";\nimport { PromptManager } from \"./prompt/index.js\";\nimport { ScoreManager } from \"./score/index.js\";\n\n/**\n * Configuration parameters for initializing a LangfuseClient instance.\n *\n * @public\n */\nexport interface LangfuseClientParams {\n /**\n * Public API key for authentication with Langfuse.\n * Can also be provided via LANGFUSE_PUBLIC_KEY environment variable.\n */\n publicKey?: string;\n\n /**\n * Secret API key for authentication with Langfuse.\n * Can also be provided via LANGFUSE_SECRET_KEY environment variable.\n */\n secretKey?: string;\n\n /**\n * Base URL of the Langfuse instance to connect to.\n * Can also be provided via LANGFUSE_BASE_URL environment variable.\n *\n * @defaultValue \"https://cloud.langfuse.com\"\n */\n baseUrl?: string;\n\n /**\n * Request timeout in seconds.\n * Can also be provided via LANGFUSE_TIMEOUT environment variable.\n *\n * @defaultValue 5\n */\n timeout?: number;\n\n /**\n * Additional HTTP headers to include with API requests.\n */\n additionalHeaders?: Record<string, string>;\n}\n\n/**\n * Main client for interacting with the Langfuse API.\n *\n * The LangfuseClient provides access to all Langfuse functionality including:\n * - Prompt management and retrieval\n * - Dataset operations\n * - Score creation and management\n * - Media upload and handling\n * - Direct API access for advanced use cases\n *\n * @example\n * ```typescript\n * // Initialize with explicit credentials\n * const langfuse = new LangfuseClient({\n * publicKey: \"pk_...\",\n * secretKey: \"sk_...\",\n * baseUrl: \"https://cloud.langfuse.com\"\n * });\n *\n * // Or use environment variables\n * const langfuse = new LangfuseClient();\n *\n * // Use the client\n * const prompt = await langfuse.prompt.get(\"my-prompt\");\n * const compiledPrompt = prompt.compile({ variable: \"value\" });\n * ```\n *\n * @public\n */\nexport class LangfuseClient {\n /**\n * Direct access to the underlying Langfuse API client.\n * Use this for advanced API operations not covered by the high-level managers.\n */\n public api: LangfuseAPIClient;\n\n /**\n * Manager for prompt operations including creation, retrieval, and caching.\n */\n public prompt: PromptManager;\n\n /**\n * Manager for dataset operations including retrieval and item linking.\n */\n public dataset: DatasetManager;\n\n /**\n * Manager for score creation and batch processing.\n */\n public score: ScoreManager;\n\n /**\n * Manager for media upload and reference resolution.\n */\n public media: MediaManager;\n\n /**\n * Manager for running experiments on datasets and data items.\n *\n * The experiment manager provides comprehensive functionality for:\n * - Running tasks on datasets or custom data arrays\n * - Evaluating outputs with custom or pre-built evaluators\n * - Tracking experiment runs with automatic tracing\n * - Generating formatted result summaries\n * - Integrating with AutoEvals library evaluators\n *\n * @example Basic experiment execution\n * ```typescript\n * const langfuse = new LangfuseClient();\n *\n * const result = await langfuse.experiment.run({\n * name: \"Model Evaluation\",\n * description: \"Testing model performance on Q&A tasks\",\n * data: [\n * { input: \"What is 2+2?\", expectedOutput: \"4\" },\n * { input: \"What is the capital of France?\", expectedOutput: \"Paris\" }\n * ],\n * task: async ({ input }) => {\n * // Your model/task implementation\n * const response = await myModel.generate(input);\n * return response;\n * },\n * evaluators: [\n * async ({ output, expectedOutput }) => ({\n * name: \"exact_match\",\n * value: output.trim().toLowerCase() === expectedOutput.toLowerCase() ? 1 : 0\n * })\n * ]\n * });\n *\n * console.log(await result.format());\n * ```\n *\n * @example Using with datasets\n * ```typescript\n * const dataset = await langfuse.dataset.get(\"my-test-dataset\");\n * const result = await dataset.runExperiment({\n * name: \"Production Readiness Test\",\n * task: myTask,\n * evaluators: [accuracyEvaluator, latencyEvaluator],\n * runEvaluators: [overallQualityEvaluator]\n * });\n * ```\n *\n * @see {@link ExperimentManager} for detailed API documentation\n * @see {@link ExperimentParams} for configuration options\n * @see {@link ExperimentResult} for result structure\n * @public\n * @since 4.0.0\n */\n public experiment: ExperimentManager;\n\n private baseUrl: string;\n private projectId: string | null = null;\n\n /**\n * @deprecated Use prompt.get instead\n */\n public getPrompt: typeof PromptManager.prototype.get;\n /**\n * @deprecated Use prompt.create instead\n */\n public createPrompt: typeof PromptManager.prototype.create;\n /**\n * @deprecated Use prompt.update instead\n */\n public updatePrompt: typeof PromptManager.prototype.update;\n /**\n * @deprecated Use dataset.get instead\n */\n public getDataset: typeof DatasetManager.prototype.get;\n /**\n * @deprecated Use api.trace.get instead\n */\n public fetchTrace: typeof LangfuseAPIClient.prototype.trace.get;\n /**\n * @deprecated Use api.trace.list instead\n */\n public fetchTraces: typeof LangfuseAPIClient.prototype.trace.list;\n /**\n * @deprecated Use api.observations.get instead\n */\n public fetchObservation: typeof LangfuseAPIClient.prototype.observations.get;\n /**\n * @deprecated Use api.observations.list instead\n */\n public fetchObservations: typeof LangfuseAPIClient.prototype.observations.getMany;\n /**\n * @deprecated Use api.sessions.get instead\n */\n public fetchSessions: typeof LangfuseAPIClient.prototype.sessions.get;\n /**\n * @deprecated Use api.datasets.getRun instead\n */\n public getDatasetRun: typeof LangfuseAPIClient.prototype.datasets.getRun;\n /**\n * @deprecated Use api.datasets.getRuns instead\n */\n public getDatasetRuns: typeof LangfuseAPIClient.prototype.datasets.getRuns;\n /**\n * @deprecated Use api.datasets.create instead\n */\n public createDataset: typeof LangfuseAPIClient.prototype.datasets.create;\n /**\n * @deprecated Use api.datasetItems.get instead\n */\n public getDatasetItem: typeof LangfuseAPIClient.prototype.datasetItems.get;\n /**\n * @deprecated Use api.datasetItems.create instead\n */\n public createDatasetItem: typeof LangfuseAPIClient.prototype.datasetItems.create;\n /**\n * @deprecated Use api.media.get instead\n */\n public fetchMedia: typeof LangfuseAPIClient.prototype.media.get;\n /**\n * @deprecated Use media.resolveReferences instead\n */\n public resolveMediaReferences: typeof MediaManager.prototype.resolveReferences;\n\n /**\n * Creates a new LangfuseClient instance.\n *\n * @param params - Configuration parameters. If not provided, will use environment variables.\n *\n * @throws Will log warnings if required credentials are not provided\n *\n * @example\n * ```typescript\n * // With explicit configuration\n * const client = new LangfuseClient({\n * publicKey: \"pk_...\",\n * secretKey: \"sk_...\",\n * baseUrl: \"https://your-instance.langfuse.com\"\n * });\n *\n * // Using environment variables\n * const client = new LangfuseClient();\n * ```\n */\n constructor(params?: LangfuseClientParams) {\n const logger = getGlobalLogger();\n\n const publicKey = params?.publicKey ?? getEnv(\"LANGFUSE_PUBLIC_KEY\");\n const secretKey = params?.secretKey ?? getEnv(\"LANGFUSE_SECRET_KEY\");\n this.baseUrl =\n params?.baseUrl ??\n getEnv(\"LANGFUSE_BASE_URL\") ??\n getEnv(\"LANGFUSE_BASEURL\") ?? // legacy v2\n \"https://cloud.langfuse.com\";\n\n if (!publicKey) {\n logger.warn(\n \"No public key provided in constructor or as LANGFUSE_PUBLIC_KEY env var. Client operations will fail.\",\n );\n }\n if (!secretKey) {\n logger.warn(\n \"No secret key provided in constructor or as LANGFUSE_SECRET_KEY env var. Client operations will fail.\",\n );\n }\n const timeoutSeconds =\n params?.timeout ?? Number(getEnv(\"LANGFUSE_TIMEOUT\") ?? 5);\n\n this.api = new LangfuseAPIClient({\n baseUrl: this.baseUrl,\n username: publicKey,\n password: secretKey,\n xLangfusePublicKey: publicKey,\n xLangfuseSdkVersion: LANGFUSE_SDK_VERSION,\n xLangfuseSdkName: \"javascript\",\n environment: \"\", // noop as baseUrl is set\n headers: params?.additionalHeaders,\n });\n\n logger.debug(\"Initialized LangfuseClient with params:\", {\n publicKey,\n baseUrl: this.baseUrl,\n timeoutSeconds,\n });\n\n this.prompt = new PromptManager({ apiClient: this.api });\n this.dataset = new DatasetManager({ langfuseClient: this });\n this.score = new ScoreManager({ apiClient: this.api });\n this.media = new MediaManager({ apiClient: this.api });\n this.experiment = new ExperimentManager({ langfuseClient: this });\n\n // Keep v3 compat by exposing old interface\n this.getPrompt = this.prompt.get.bind(this.prompt); // keep correct this context for cache access\n this.createPrompt = this.prompt.create.bind(this.prompt);\n this.updatePrompt = this.prompt.update.bind(this.prompt);\n this.getDataset = this.dataset.get;\n this.fetchTrace = this.api.trace.get;\n this.fetchTraces = this.api.trace.list;\n this.fetchObservation = this.api.observations.get;\n this.fetchObservations = this.api.observations.getMany;\n this.fetchSessions = this.api.sessions.get;\n this.getDatasetRun = this.api.datasets.getRun;\n this.getDatasetRuns = this.api.datasets.getRuns;\n this.createDataset = this.api.datasets.create;\n this.getDatasetItem = this.api.datasetItems.get;\n this.createDatasetItem = this.api.datasetItems.create;\n this.fetchMedia = this.api.media.get;\n this.resolveMediaReferences = this.media.resolveReferences;\n }\n\n /**\n * Flushes any pending score events to the Langfuse API.\n *\n * This method ensures all queued scores are sent immediately rather than\n * waiting for the automatic flush interval or batch size threshold.\n *\n * @returns Promise that resolves when all pending scores have been sent\n *\n * @example\n * ```typescript\n * langfuse.score.create({ name: \"quality\", value: 0.8 });\n * await langfuse.flush(); // Ensures the score is sent immediately\n * ```\n */\n public async flush() {\n return this.score.flush();\n }\n\n /**\n * Gracefully shuts down the client by flushing all pending data.\n *\n * This method should be called before your application exits to ensure\n * all data is sent to Langfuse.\n *\n * @returns Promise that resolves when shutdown is complete\n *\n * @example\n * ```typescript\n * // Before application exit\n * await langfuse.shutdown();\n * ```\n */\n public async shutdown() {\n return this.score.shutdown();\n }\n\n /**\n * Generates a URL to view a specific trace in the Langfuse UI.\n *\n * @param traceId - The ID of the trace to generate a URL for\n * @returns Promise that resolves to the trace URL\n *\n * @example\n * ```typescript\n * const traceId = \"trace-123\";\n * const url = await langfuse.getTraceUrl(traceId);\n * console.log(`View trace at: ${url}`);\n * ```\n */\n public async getTraceUrl(traceId: string) {\n let projectId = this.projectId;\n\n if (!projectId) {\n projectId = (await this.api.projects.get()).data[0].id;\n this.projectId = projectId;\n }\n\n const traceUrl = `${this.baseUrl}/project/${projectId}/traces/${traceId}`;\n\n return traceUrl;\n }\n}\n","import { Dataset, DatasetRunItem, DatasetItem } from \"@langfuse/core\";\nimport { Span } from \"@opentelemetry/api\";\n\nimport { ExperimentResult, ExperimentParams } from \"../experiment/types.js\";\nimport { LangfuseClient } from \"../LangfuseClient.js\";\n\n/**\n * Function type for running experiments on Langfuse datasets.\n *\n * This function type is attached to fetched datasets to enable convenient\n * experiment execution directly on dataset objects.\n *\n * @param params - Experiment parameters excluding data (since data comes from the dataset)\n * @returns Promise resolving to experiment results\n *\n * @example\n * ```typescript\n * const dataset = await langfuse.dataset.get(\"my-dataset\");\n * const result = await dataset.runExperiment({\n * name: \"Model Evaluation\",\n * runName: \"Model Evaluation Run 1\", // optional\n * task: myTask,\n * evaluators: [myEvaluator]\n * });\n * ```\n *\n * @public\n * @since 4.0.0\n */\nexport type RunExperimentOnDataset = (\n params: Omit<ExperimentParams<any, any, Record<string, any>>, \"data\">,\n) => Promise<ExperimentResult<any, any, Record<string, any>>>;\n\n/**\n * Enhanced dataset object with additional methods for linking and experiments.\n *\n * This type extends the base Dataset with functionality for:\n * - Linking dataset items to traces/observations\n * - Running experiments directly on the dataset\n *\n * @example Working with a fetched dataset\n * ```typescript\n * const dataset = await langfuse.dataset.get(\"my-evaluation-dataset\");\n *\n * // Access dataset metadata\n * console.log(dataset.name, dataset.description);\n *\n * // Work with individual items\n * for (const item of dataset.items) {\n * console.log(item.input, item.expectedOutput);\n *\n * // Link item to a trace\n * await item.link(myObservation, \"experiment-run-1\");\n * }\n *\n * // Run experiments on the entire dataset\n * const result = await dataset.runExperiment({\n * name: \"Model Comparison\",\n * task: myTask,\n * evaluators: [accuracyEvaluator]\n * });\n * ```\n *\n * @public\n * @since 4.0.0\n */\nexport type FetchedDataset = Dataset & {\n /** Dataset items with additional linking functionality */\n items: (DatasetItem & { link: LinkDatasetItemFunction })[];\n /** Function to run experiments directly on this dataset */\n runExperiment: RunExperimentOnDataset;\n};\n\n/**\n * Function type for linking dataset items to OpenTelemetry spans.\n *\n * This function creates a connection between a dataset item and a trace/observation,\n * enabling tracking of which dataset items were used in which experiments or runs.\n * This is essential for creating dataset runs and tracking experiment lineage.\n *\n * @param obj - Object containing the OpenTelemetry span to link to\n * @param obj.otelSpan - The OpenTelemetry span from a Langfuse observation\n * @param runName - Name of the experiment run for grouping related items\n * @param runArgs - Optional configuration for the dataset run\n * @param runArgs.description - Description of the experiment run\n * @param runArgs.metadata - Additional metadata to attach to the run\n * @returns Promise that resolves to the created dataset run item\n *\n * @example Basic linking\n * ```typescript\n * const dataset = await langfuse.dataset.get(\"my-dataset\");\n * const span = startObservation(\"my-task\", { input: \"test\" });\n * span.update({ output: \"result\" });\n * span.end();\n *\n * // Link the dataset item to this execution\n * await dataset.items[0].link(\n * { otelSpan: span.otelSpan },\n * \"experiment-run-1\"\n * );\n * ```\n *\n * @example Linking with metadata\n * ```typescript\n * await dataset.items[0].link(\n * { otelSpan: span.otelSpan },\n * \"model-comparison-v2\",\n * {\n * description: \"Comparing GPT-4 vs Claude performance\",\n * metadata: {\n * modelVersion: \"gpt-4-1106-preview\",\n * temperature: 0.7,\n * timestamp: new Date().toISOString()\n * }\n * }\n * );\n * ```\n *\n * @see {@link https://langfuse.com/docs/datasets} Langfuse datasets documentation\n * @public\n * @since 4.0.0\n */\nexport type LinkDatasetItemFunction = (\n obj: { otelSpan: Span },\n runName: string,\n runArgs?: {\n /** Description of the dataset run */\n description?: string;\n /** Additional metadata for the dataset run */\n metadata?: any;\n },\n) => Promise<DatasetRunItem>;\n\n/**\n * Manager for dataset operations in Langfuse.\n *\n * Provides methods to retrieve datasets and their items, with automatic\n * pagination handling and convenient linking functionality for experiments.\n *\n * @public\n */\nexport class DatasetManager {\n private langfuseClient: LangfuseClient;\n\n /**\n * Creates a new DatasetManager instance.\n *\n * @param params - Configuration object containing the API client\n * @internal\n */\n constructor(params: { langfuseClient: LangfuseClient }) {\n this.langfuseClient = params.langfuseClient;\n }\n\n /**\n * Retrieves a dataset by name with all its items and experiment functionality.\n *\n * This method fetches a dataset and all its associated items, with support\n * for automatic pagination to handle large datasets efficiently. The returned\n * dataset object includes enhanced functionality for linking items to traces\n * and running experiments directly on the dataset.\n *\n * @param name - The name of the dataset to retrieve\n * @param options - Optional configuration for data fetching\n * @param options.fetchItemsPageSize - Number of items to fetch per page (default: 50)\n * @returns Promise resolving to enhanced dataset with items, linking, and experiment capabilities\n *\n * @example Basic dataset retrieval\n * ```typescript\n * const dataset = await langfuse.dataset.get(\"my-evaluation-dataset\");\n * console.log(`Dataset ${dataset.name} has ${dataset.items.length} items`);\n *\n * // Access dataset properties\n * console.log(dataset.description);\n * console.log(dataset.metadata);\n * ```\n *\n * @example Working with dataset items\n * ```typescript\n * const dataset = await langfuse.dataset.get(\"qa-dataset\");\n *\n * for (const item of dataset.items) {\n * console.log(\"Question:\", item.input);\n * console.log(\"Expected Answer:\", item.expectedOutput);\n *\n * // Each item has a link function for connecting to traces\n * // await item.link(span, \"experiment-name\");\n * }\n * ```\n *\n * @example Running experiments on datasets\n * ```typescript\n * const dataset = await langfuse.dataset.get(\"benchmark-dataset\");\n *\n * const result = await dataset.runExperiment({\n * name: \"GPT-4 Benchmark\",\n * runName: \"GPT-4 Benchmark v1.2\", // optional exact run name\n * description: \"Evaluating GPT-4 on our benchmark tasks\",\n * task: async ({ input }) => {\n * const response = await openai.chat.completions.create({\n * model: \"gpt-4\",\n * messages: [{ role: \"user\", content: input }]\n * });\n * return response.choices[0].message.content;\n * },\n * evaluators: [\n * async ({ output, expectedOutput }) => ({\n * name: \"exact_match\",\n * value: output === expectedOutput ? 1 : 0\n * })\n * ]\n * });\n *\n * console.log(await result.format());\n * ```\n *\n * @example Handling large datasets\n * ```typescript\n * // For very large datasets, use smaller page sizes\n * const largeDataset = await langfuse.dataset.get(\n * \"large-dataset\",\n * { fetchItemsPageSize: 100 }\n * );\n * ```\n *\n * @throws {Error} If the dataset does not exist or cannot be accessed\n * @see {@link FetchedDataset} for the complete return type specification\n * @see {@link RunExperimentOnDataset} for experiment execution details\n * @public\n * @since 4.0.0\n */\n async get(\n name: string,\n options?: {\n fetchItemsPageSize: number;\n },\n ): Promise<FetchedDataset> {\n const dataset = await this.langfuseClient.api.datasets.get(name);\n const items: DatasetItem[] = [];\n\n let page = 1;\n\n while (true) {\n const itemsResponse = await this.langfuseClient.api.datasetItems.list({\n datasetName: name,\n limit: options?.fetchItemsPageSize ?? 50,\n page,\n });\n\n items.push(...itemsResponse.data);\n\n if (itemsResponse.meta.totalPages <= page) {\n break;\n }\n\n page++;\n }\n\n const itemsWithLinkMethod = items.map((item) => ({\n ...item,\n link: this.createDatasetItemLinkFunction(item),\n }));\n\n const runExperiment: RunExperimentOnDataset = (params) => {\n return this.langfuseClient.experiment.run({\n data: items,\n ...params,\n });\n };\n\n const returnDataset = {\n ...dataset,\n items: itemsWithLinkMethod,\n runExperiment,\n };\n\n return returnDataset;\n }\n\n /**\n * Creates a link function for a specific dataset item.\n *\n * @param item - The dataset item to create a link function for\n * @returns A function that can link the item to OpenTelemetry spans\n * @internal\n */\n private createDatasetItemLinkFunction(\n item: DatasetItem,\n ): LinkDatasetItemFunction {\n const linkFunction = async (\n obj: { otelSpan: Span },\n runName: string,\n runArgs?: {\n description?: string;\n metadata?: any;\n },\n ): Promise<DatasetRunItem> => {\n return await this.langfuseClient.api.datasetRunItems.create({\n runName,\n datasetItemId: item.id,\n traceId: obj.otelSpan.spanContext().traceId,\n runDescription: runArgs?.description,\n metadata: runArgs?.metadata,\n });\n };\n\n return linkFunction;\n }\n}\n","import { DatasetItem, getGlobalLogger } from \"@langfuse/core\";\nimport { startActiveObservation } from \"@langfuse/tracing\";\nimport { ProxyTracerProvider, trace } from \"@opentelemetry/api\";\n\nimport { LangfuseClient } from \"../LangfuseClient.js\";\n\nimport {\n ExperimentParams,\n ExperimentResult,\n ExperimentTask,\n ExperimentItem,\n ExperimentItemResult,\n Evaluator,\n Evaluation,\n} from \"./types.js\";\n\n/**\n * Manages the execution and evaluation of experiments on datasets.\n *\n * The ExperimentManager provides a comprehensive framework for running experiments\n * that test models or tasks against datasets, with support for automatic evaluation,\n * scoring.\n *\n * @example Basic experiment usage\n * ```typescript\n * const langfuse = new LangfuseClient();\n *\n * const result = await langfuse.experiment.run({\n * name: \"Capital Cities Test\",\n * description: \"Testing model knowledge of world capitals\",\n * data: [\n * { input: \"France\", expectedOutput: \"Paris\" },\n * { input: \"Germany\", expectedOutput: \"Berlin\" }\n * ],\n * task: async ({ input }) => {\n * const response = await openai.chat.completions.create({\n * model: \"gpt-4\",\n * messages: [{ role: \"user\", content: `What is the capital of ${input}?` }]\n * });\n * return response.choices[0].message.content;\n * },\n * evaluators: [\n * async ({ input, output, expectedOutput }) => ({\n * name: \"exact_match\",\n * value: output === expectedOutput ? 1 : 0\n * })\n * ]\n * });\n *\n * console.log(await result.format());\n * ```\n *\n * @example Using with Langfuse datasets\n * ```typescript\n * const dataset = await langfuse.dataset.get(\"my-dataset\");\n *\n * const result = await dataset.runExperiment({\n * name: \"Model Comparison\",\n * task: myTask,\n * evaluators: [myEvaluator],\n * runEvaluators: [averageScoreEvaluator]\n * });\n * ```\n *\n * @public\n */\nexport class ExperimentManager {\n private langfuseClient: LangfuseClient;\n\n /**\n * Creates a new ExperimentManager instance.\n *\n * @param params - Configuration object\n * @param params.langfuseClient - The Langfuse client instance for API communication\n * @internal\n */\n constructor(params: { langfuseClient: LangfuseClient }) {\n this.langfuseClient = params.langfuseClient;\n }\n\n /**\n * Gets the global logger instance for experiment-related logging.\n *\n * @returns The global logger instance\n * @internal\n */\n get logger() {\n return getGlobalLogger();\n }\n\n /**\n * Executes an experiment by running a task on each data item and evaluating the results.\n *\n * This method orchestrates the complete experiment lifecycle:\n * 1. Executes the task function on each data item with proper tracing\n * 2. Runs item-level evaluators on each task output\n * 3. Executes run-level evaluators on the complete result set\n * 4. Links results to dataset runs (for Langfuse datasets)\n * 5. Stores all scores and traces in Langfuse\n *\n * @param config - The experiment configuration\n * @param config.name - Human-readable name for the experiment\n * @param config.runName - Optional exact name for the experiment run (defaults to name + timestamp)\n * @param config.description - Optional description of the experiment's purpose\n * @param config.metadata - Optional metadata to attach to the experiment run\n * @param config.data - Array of data items to process (ExperimentItem[] or DatasetItem[])\n * @param config.task - Function that processes each data item and returns output\n * @param config.evaluators - Optional array of functions to evaluate each item's output\n * @param config.runEvaluators - Optional array of functions to evaluate the entire run\n * @param config.maxConcurrency - Maximum number of concurrent task executions (default: 50)\n *\n * @returns Promise that resolves to experiment results including:\n * - runName: The experiment run name (either provided or generated)\n * - itemResults: Results for each processed data item\n * - runEvaluations: Results from run-level evaluators\n * - datasetRunId: ID of the dataset run (if using Langfuse datasets)\n * - format: Function to format results for display\n *\n * @throws {Error} When task execution fails and cannot be handled gracefully\n * @throws {Error} When required evaluators fail critically\n *\n * @example Simple experiment\n * ```typescript\n * const result = await langfuse.experiment.run({\n * name: \"Translation Quality Test\",\n * data: [\n * { input: \"Hello world\", expectedOutput: \"Hola mundo\" },\n * { input: \"Good morning\", expectedOutput: \"Buenos días\" }\n * ],\n * task: async ({ input }) => translateText(input, 'es'),\n * evaluators: [\n * async ({ output, expectedOutput }) => ({\n * name: \"bleu_score\",\n * value: calculateBleuScore(output, expectedOutput)\n * })\n * ]\n * });\n * ```\n *\n * @example Experiment with concurrency control\n * ```typescript\n * const result = await langfuse.experiment.run({\n * name: \"Large Scale Evaluation\",\n * data: largeBatchOfItems,\n * task: expensiveModelCall,\n * maxConcurrency: 5, // Process max 5 items simultaneously\n * evaluators: [myEvaluator],\n * runEvaluators: [\n * async ({ itemResults }) => ({\n * name: \"average_score\",\n * value: itemResults.reduce((acc, r) => acc + r.evaluations[0].value, 0) / itemResults.length\n * })\n * ]\n * });\n * ```\n *\n * @see {@link ExperimentParams} for detailed parameter documentation\n * @see {@link ExperimentResult} for detailed return value documentation\n * @see {@link Evaluator} for evaluator function specifications\n * @see {@link RunEvaluator} for run evaluator function specifications\n *\n * @public\n */\n async run<\n Input = any,\n ExpectedOutput = any,\n Metadata extends Record<string, any> = Record<string, any>,\n >(\n config: ExperimentParams<Input, ExpectedOutput, Metadata>,\n ): Promise<ExperimentResult<Input, ExpectedOutput, Metadata>> {\n const {\n data,\n evaluators,\n task,\n name,\n runName: providedRunName,\n description,\n metadata,\n maxConcurrency: batchSize = 50,\n runEvaluators,\n } = config;\n\n const runName = this.createExperimentRunName({\n name,\n runName: providedRunName,\n });\n\n if (!this.isOtelRegistered()) {\n this.logger.warn(\n \"OpenTelemetry has not been set up. Traces will not be sent to Langfuse.See our docs on how to set up OpenTelemetry: https://langfuse.com/docs/observability/sdk/typescript/setup#tracing-setup\",\n );\n }\n\n const itemResults: ExperimentItemResult<Input, ExpectedOutput, Metadata>[] =\n [];\n\n for (let i = 0; i < data.length; i += batchSize) {\n const batch = data.slice(i, i + batchSize);\n\n const promises: Promise<\n ExperimentItemResult<Input, ExpectedOutput, Metadata>\n >[] = batch.map(async (item) => {\n return this.runItem({\n item,\n evaluators,\n task,\n experimentName: name,\n experimentRunName: runName,\n experimentDescription: description,\n experimentMetadata: metadata,\n });\n });\n\n const settledResults = await Promise.allSettled(promises);\n const results = settledResults.reduce(\n (acc, settledResult) => {\n if (settledResult.status === \"fulfilled\") {\n acc.push(settledResult.value);\n } else {\n const errorMessage =\n settledResult.reason instanceof Error\n ? settledResult.reason.message\n : String(settledResult.reason);\n this.logger.error(\n `Task failed with error: ${errorMessage}. Skipping item.`,\n );\n }\n return acc;\n },\n [] as ExperimentItemResult<Input, ExpectedOutput, Metadata>[],\n );\n\n itemResults.push(...results);\n }\n\n // Get dataset run URL\n const datasetRunId =\n itemResults.length > 0 ? itemResults[0].datasetRunId : undefined;\n\n let datasetRunUrl = undefined;\n if (datasetRunId && data.length > 0 && \"datasetId\" in data[0]) {\n const datasetId = data[0].datasetId;\n const projectUrl = (await this.langfuseClient.getTraceUrl(\"mock\")).split(\n \"/traces\",\n )[0];\n\n datasetRunUrl = `${projectUrl}/datasets/${datasetId}/runs/${datasetRunId}`;\n }\n\n // Execute run evaluators\n let runEvaluations: Evaluation[] = [];\n if (runEvaluators && runEvaluators?.length > 0) {\n const promises = runEvaluators.map(async (runEvaluator) => {\n return runEvaluator({ itemResults })\n .then((result) => {\n // Handle both single evaluation and array of evaluations\n return Array.isArray(result) ? result : [result];\n })\n .catch((err) => {\n this.logger.error(\"Run evaluator failed with error \", err);\n\n throw err;\n });\n });\n\n runEvaluations = (await Promise.allSettled(promises)).reduce(\n (acc, settledPromise) => {\n if (settledPromise.status === \"fulfilled\") {\n acc.push(...settledPromise.value);\n }\n\n return acc;\n },\n [] as Evaluation[],\n );\n\n if (datasetRunId) {\n runEvaluations.forEach((runEval) =>\n this.langfuseClient.score.create({ datasetRunId, ...runEval }),\n );\n }\n }\n\n await this.langfuseClient.score.flush();\n\n return {\n runName,\n itemResults,\n datasetRunId,\n datasetRunUrl,\n runEvaluations,\n format: async (options?: { includeItemResults?: boolean }) =>\n await this.prettyPrintResults({\n datasetRunUrl,\n itemResults,\n originalData: data,\n runEvaluations,\n name: config.name,\n runName,\n description: config.description,\n includeItemResults: options?.includeItemResults ?? false,\n }),\n };\n }\n\n /**\n * Executes the task and evaluators for a single data item.\n *\n * This method handles the complete processing pipeline for one data item:\n * 1. Executes the task within a traced observation span\n * 2. Links the result to a dataset run (if applicable)\n * 3. Runs all item-level evaluators on the output\n * 4. Stores evaluation scores in Langfuse\n * 5. Handles errors gracefully by continuing with remaining evaluators\n *\n * @param params - Parameters for item execution\n * @param params.experimentName - Name of the parent experiment\n * @param params.experimentRunName - Run name for the parent experiment\n * @param params.experimentDescription - Description of the parent experiment\n * @param params.experimentMetadata - Metadata for the parent experiment\n * @param params.item - The data item to process\n * @param params.task - The task function to execute\n * @param params.evaluators - Optional evaluators to run on the output\n *\n * @returns Promise resolving to the item result with output, evaluations, and trace info\n *\n * @throws {Error} When task execution fails (propagated from task function)\n *\n * @internal\n */\n private async runItem<\n Input = any,\n ExpectedOutput = any,\n Metadata extends Record<string, any> = Record<string, any>,\n >(params: {\n experimentName: ExperimentParams<Input, ExpectedOutput, Metadata>[\"name\"];\n experimentRunName: string;\n experimentDescription: ExperimentParams<\n Input,\n ExpectedOutput,\n Metadata\n >[\"description\"];\n experimentMetadata: ExperimentParams<\n Input,\n ExpectedOutput,\n Metadata\n >[\"metadata\"];\n item: ExperimentParams<Input, ExpectedOutput, Metadata>[\"data\"][0];\n task: ExperimentTask<Input, ExpectedOutput, Metadata>;\n evaluators?: Evaluator<Input, ExpectedOutput, Metadata>[];\n }): Promise<ExperimentItemResult<Input, ExpectedOutput, Metadata>> {\n const { item, evaluators = [], task, experimentMetadata = {} } = params;\n\n const { output, traceId, observationId } = await startActiveObservation(\n \"experiment-item-run\",\n async (span) => {\n const output = await task(item);\n\n span.update({\n input: item.input,\n output,\n metadata: {\n experiment_name: params.experimentName,\n experiment_run_name: params.experimentRunName,\n ...experimentMetadata,\n ...(item.metadata ?? {}),\n ...(\"id\" in item && \"datasetId\" in item\n ? {\n dataset_id: item[\"datasetId\"],\n dataset_item_id: item[\"id\"],\n }\n : {}),\n },\n });\n\n return { output, traceId: span.traceId, observationId: span.id };\n },\n );\n\n let datasetRunId: string | undefined = undefined;\n\n if (\"id\" in item) {\n await this.langfuseClient.api.datasetRunItems\n .create({\n runName: params.experimentRunName,\n runDescription: params.experimentDescription,\n metadata: params.experimentMetadata,\n datasetItemId: item.id,\n traceId,\n observationId,\n })\n .then((result) => {\n datasetRunId = result.datasetRunId;\n })\n .catch((err) =>\n this.logger.error(\"Linking dataset run item failed\", err),\n );\n }\n\n const evalPromises: Promise<Evaluation[]>[] = evaluators.map(\n async (evaluator) => {\n const params = {\n input: item.input as any,\n expectedOutput: item.expectedOutput as any,\n output,\n };\n\n return evaluator(params)\n .then((result) => {\n // Handle both single evaluation and array of evaluations\n return Array.isArray(result) ? result : [result];\n })\n .catch((err) => {\n this.logger.error(\n `Evaluator '${evaluator.name}' failed for params \\n\\n${JSON.stringify(params)}\\n\\n with error: ${err}`,\n );\n\n throw err;\n });\n },\n );\n\n const evals = (await Promise.allSettled(evalPromises)).reduce(\n (acc, promiseResult) => {\n if (promiseResult.status === \"fulfilled\") {\n acc.push(...promiseResult.value.flat());\n }\n\n return acc;\n },\n [] as Evaluation[],\n );\n\n for (const ev of evals) {\n this.langfuseClient.score.create({\n traceId,\n ...ev,\n });\n }\n\n return {\n output,\n evaluations: evals,\n traceId,\n datasetRunId,\n item,\n };\n }\n\n /**\n * Formats experiment results into a human-readable string representation.\n *\n * Creates a comprehensive, nicely formatted summary of the experiment including:\n * - Individual item results with inputs, outputs, expected values, and scores\n * - Dataset item and trace links (when available)\n * - Experiment overview with aggregate statistics\n * - Average scores across all evaluations\n * - Run-level evaluation results\n * - Links to dataset runs in the Langfuse UI\n *\n * @param params - Formatting parameters\n * @param params.datasetRunUrl - Optional URL to the dataset run in Langfuse UI\n * @param params.itemResults - Results from processing each data item\n * @param params.originalData - The original input data items\n * @param params.runEvaluations - Results from run-level evaluators\n * @param params.name - Name of the experiment\n * @param params.description - Optional description of the experiment\n * @param params.includeItemResults - Whether to include individual item details (default: false)\n *\n * @returns Promise resolving to formatted string representation\n *\n * @example Output format\n * ```\n * 1. Item 1:\n * Input: What is the capital of France?\n * Expected: Paris\n * Actual: Paris\n * Scores:\n * • exact_match: 1.000\n * • similarity: 0.95\n * 💭 Very close match with expected output\n *\n * Dataset Item:\n * https://cloud.langfuse.com/project/123/datasets/456/items/789\n *\n * Trace:\n * https://cloud.langfuse.com/project/123/traces/abc123\n *\n * ──────────────────────────────────────────────────\n * 📊 Translation Quality Test - Testing model accuracy\n * 2 items\n * Evaluations:\n * • exact_match\n * • similarity\n *\n * Average Scores:\n * • exact_match: 0.850\n * • similarity: 0.923\n *\n * Run Evaluations:\n * • overall_quality: 0.887\n * 💭 Good performance with room for improvement\n *\n * 🔗 Dataset Run:\n * https://cloud.langfuse.com/project/123/datasets/456/runs/def456\n * ```\n *\n * @internal\n */\n private async prettyPrintResults<\n Input = any,\n ExpectedOutput = any,\n Metadata extends Record<string, any> = Record<string, any>,\n >(params: {\n datasetRunUrl?: string;\n itemResults: ExperimentItemResult<Input, ExpectedOutput, Metadata>[];\n originalData:\n | ExperimentItem<Input, ExpectedOutput, Metadata>[]\n | DatasetItem[];\n runEvaluations: Evaluation[];\n name: string;\n runName: string;\n description?: string;\n includeItemResults?: boolean;\n }): Promise<string> {\n const {\n itemResults,\n originalData,\n runEvaluations,\n name,\n runName,\n description,\n includeItemResults = false,\n } = params;\n\n if (itemResults.length === 0) {\n return \"No experiment results to display.\";\n }\n\n let output = \"\";\n\n // Individual results\n if (includeItemResults) {\n for (let index = 0; index < itemResults.length; index++) {\n const result = itemResults[index];\n const originalItem = originalData[index];\n\n output += `\\n${index + 1}. Item ${index + 1}:\\n`;\n\n // Input, expected, and actual on separate lines\n if (originalItem?.input !== undefined) {\n output += ` Input: ${this.formatValue(originalItem.input)}\\n`;\n }\n\n const expectedOutput =\n originalItem?.expectedOutput ?? result.expectedOutput ?? null;\n output += ` Expected: ${expectedOutput !== null ? this.formatValue(expectedOutput) : \"null\"}\\n`;\n output += ` Actual: ${this.formatValue(result.output)}\\n`;\n\n // Scores on separate lines\n if (result.evaluations.length > 0) {\n output += ` Scores:\\n`;\n result.evaluations.forEach((evaluation) => {\n const score =\n typeof evaluation.value === \"number\"\n ? evaluation.value.toFixed(3)\n : evaluation.value;\n output += ` • ${evaluation.name}: ${score}`;\n if (evaluation.comment) {\n output += `\\n 💭 ${evaluation.comment}`;\n }\n output += \"\\n\";\n });\n }\n\n // Dataset item link on separate line\n if (\n originalItem &&\n \"id\" in originalItem &&\n \"datasetId\" in originalItem\n ) {\n const projectUrl = (\n await this.langfuseClient.getTraceUrl(\"mock\")\n ).split(\"/traces\")[0];\n const datasetItemUrl = `${projectUrl}/datasets/${originalItem.datasetId}/items/${originalItem.id}`;\n output += `\\n Dataset Item:\\n ${datasetItemUrl}\\n`;\n }\n\n // Trace link on separate line\n if (result.traceId) {\n const traceUrl = await this.langfuseClient.getTraceUrl(\n result.traceId,\n );\n output += `\\n Trace:\\n ${traceUrl}\\n`;\n }\n }\n } else {\n output += `Individual Results: Hidden (${itemResults.length} items)\\n`;\n output += \"💡 Call format({ includeItemResults: true }) to view them\\n\";\n }\n\n // Experiment Overview\n const totalItems = itemResults.length;\n const evaluationNames = new Set(\n itemResults.flatMap((r) => r.evaluations.map((e) => e.name)),\n );\n\n output += `\\n${\"─\".repeat(50)}\\n`;\n output += `🧪 Experiment: ${name}`;\n output += `\\n📋 Run name: ${runName}`;\n if (description) {\n output += ` - ${description}`;\n }\n\n output += `\\n${totalItems} items`;\n\n if (evaluationNames.size > 0) {\n output += `\\nEvaluations:`;\n Array.from(evaluationNames).forEach((evalName) => {\n output += `\\n • ${evalName}`;\n });\n output += \"\\n\";\n }\n\n // Average scores in bulleted list\n if (evaluationNames.size > 0) {\n output += `\\nAverage Scores:`;\n for (const evalName of evaluationNames) {\n const scores = itemResults\n .flatMap((r) => r.evaluations)\n .filter((e) => e.name === evalName && typeof e.value === \"number\")\n .map((e) => e.value as number);\n\n if (scores.length > 0) {\n const avg = scores.reduce((a, b) => a + b, 0) / scores.length;\n output += `\\n • ${evalName}: ${avg.toFixed(3)}`;\n }\n }\n output += \"\\n\";\n }\n\n // Run evaluations\n if (runEvaluations.length > 0) {\n output += `\\nRun Evaluations:`;\n runEvaluations.forEach((runEval) => {\n const score =\n typeof runEval.value === \"number\"\n ? runEval.value.toFixed(3)\n : runEval.value;\n output += `\\n • ${runEval.name}: ${score}`;\n if (runEval.comment) {\n output += `\\n 💭 ${runEval.comment}`;\n }\n });\n output += \"\\n\";\n }\n\n if (params.datasetRunUrl) {\n output += `\\n🔗 Dataset Run:\\n ${params.datasetRunUrl}`;\n }\n\n return output;\n }\n\n /**\n * Formats a value for display in pretty-printed output.\n *\n * Handles different value types appropriately:\n * - Strings: Truncates long strings to 50 characters with \"...\"\n * - Objects/Arrays: Converts to JSON string representation\n * - Primitives: Uses toString() representation\n *\n * @param value - The value to format\n * @returns Formatted string representation suitable for display\n *\n * @internal\n */\n private formatValue(value: any): string {\n if (typeof value === \"string\") {\n return value.length > 50 ? `${value.substring(0, 47)}...` : value;\n }\n return JSON.stringify(value);\n }\n\n private isOtelRegistered(): boolean {\n let tracerProvider = trace.getTracerProvider();\n\n if (tracerProvider instanceof ProxyTracerProvider) {\n tracerProvider = tracerProvider.getDelegate();\n }\n\n return tracerProvider.constructor.name !== \"NoopTracerProvider\";\n }\n\n /**\n * Creates an experiment run name based on provided parameters.\n *\n * If runName is provided, returns it directly. Otherwise, generates\n * a name by combining the experiment name with an ISO timestamp.\n *\n * @param params - Parameters for run name creation\n * @param params.name - The experiment name\n * @param params.runName - Optional provided run name\n * @returns The final run name to use\n *\n * @internal\n */\n private createExperimentRunName(params: {\n name: string;\n runName?: string;\n }): string {\n if (params.runName) {\n return params.runName;\n }\n\n const isoTimestamp = new Date().toISOString();\n return `${params.name} - ${isoTimestamp}`;\n }\n}\n","import {\n LangfuseAPIClient,\n ParsedMediaReference,\n MediaContentType,\n getGlobalLogger,\n bytesToBase64,\n} from \"@langfuse/core\";\n\n/**\n * Parameters for resolving media references in objects.\n *\n * @template T - The type of the object being processed\n * @public\n */\nexport type LangfuseMediaResolveMediaReferencesParams<T> = {\n /** The object to process for media references */\n obj: T;\n /** The format to resolve media references to (currently only \"base64DataUri\" is supported) */\n resolveWith: \"base64DataUri\";\n /** Maximum depth to traverse when processing nested objects (default: 10) */\n maxDepth?: number;\n};\n\n/**\n * Manager for media operations in Langfuse.\n *\n * Provides methods to resolve media references in objects by replacing\n * them with actual media content (e.g., base64 data URIs).\n *\n * @public\n */\nexport class MediaManager {\n private apiClient: LangfuseAPIClient;\n\n /**\n * Creates a new MediaManager instance.\n *\n * @param params - Configuration object containing the API client\n * @internal\n */\n constructor(params: { apiClient: LangfuseAPIClient }) {\n this.apiClient = params.apiClient;\n }\n\n /**\n * Replaces media reference strings in an object with base64 data URIs.\n *\n * This method recursively traverses an object looking for media reference strings\n * in the format \"@@@langfuseMedia:...@@@\". When found, it fetches the actual media\n * content from Langfuse and replaces the reference string with a base64 data URI.\n *\n * If fetching media content fails for a reference string, a warning is logged\n * and the reference string is left unchanged.\n *\n * @param params - Configuration object\n * @returns A deep copy of the input object with media references resolved\n *\n * @example\n * ```typescript\n * const obj = {\n * image: \"@@@langfuseMedia:type=image/jpeg|id=123|source=bytes@@@\",\n * nested: {\n * pdf: \"@@@langfuseMedia:type=application/pdf|id=456|source=bytes@@@\"\n * }\n * };\n *\n * const result = await langfuse.media.resolveReferences({\n * obj,\n * resolveWith: \"base64DataUri\"\n * });\n *\n * // Result:\n * // {\n * // image: \"data:image/jpeg;base64,/9j/4AAQSkZJRg...\",\n * // nested: {\n * // pdf: \"data:application/pdf;base64,JVBERi0xLjcK...\"\n * // }\n * // }\n * ```\n */\n public async resolveReferences<T>(\n params: LangfuseMediaResolveMediaReferencesParams<T>,\n ): Promise<T> {\n const { obj, maxDepth = 10 } = params;\n\n const traverse = async <T>(obj: T, depth: number): Promise<T> => {\n if (depth > maxDepth) {\n return obj;\n }\n\n // Handle string with potential media references\n if (typeof obj === \"string\") {\n const regex = /@@@langfuseMedia:.+?@@@/g;\n const referenceStringMatches = obj.match(regex);\n if (!referenceStringMatches) {\n return obj;\n }\n\n let result = obj;\n const referenceStringToMediaContentMap = new Map<string, string>();\n\n await Promise.all(\n referenceStringMatches.map(async (referenceString) => {\n try {\n const parsedMediaReference =\n MediaManager.parseReferenceString(referenceString);\n const mediaData = await this.apiClient.media.get(\n parsedMediaReference.mediaId,\n );\n const mediaContent = await fetch(mediaData.url, {\n method: \"GET\",\n headers: {},\n });\n if (mediaContent.status !== 200) {\n throw new Error(\"Failed to fetch media content\");\n }\n\n const uint8Content = new Uint8Array(\n await mediaContent.arrayBuffer(),\n );\n\n const base64MediaContent = bytesToBase64(uint8Content);\n const base64DataUri = `data:${mediaData.contentType};base64,${base64MediaContent}`;\n\n referenceStringToMediaContentMap.set(\n referenceString,\n base64DataUri,\n );\n } catch (error) {\n getGlobalLogger().warn(\n \"Error fetching media content for reference string\",\n referenceString,\n error,\n );\n }\n }),\n );\n\n for (const [\n referenceString,\n base64MediaContent,\n ] of referenceStringToMediaContentMap.entries()) {\n result = result.replaceAll(referenceString, base64MediaContent) as T &\n string;\n }\n\n return result;\n }\n\n // Handle arrays\n if (Array.isArray(obj)) {\n return Promise.all(\n obj.map(async (item) => await traverse(item, depth + 1)),\n ) as Promise<T>;\n }\n\n // Handle objects\n if (typeof obj === \"object\" && obj !== null) {\n return Object.fromEntries(\n await Promise.all(\n Object.entries(obj).map(async ([key, value]) => [\n key,\n await traverse(value, depth + 1),\n ]),\n ),\n );\n }\n\n return obj;\n };\n\n return traverse(obj, 0);\n }\n\n /**\n * Parses a media reference string into a ParsedMediaReference.\n *\n * Example reference string:\n * \"@@@langfuseMedia:type=image/jpeg|id=some-uuid|source=base64DataUri@@@\"\n *\n * @param referenceString - The reference string to parse.\n * @returns An object with the mediaId, source, and contentType.\n *\n * @throws Error if the reference string is invalid or missing required fields.\n */\n public static parseReferenceString(\n referenceString: string,\n ): ParsedMediaReference {\n const prefix = \"@@@langfuseMedia:\";\n const suffix = \"@@@\";\n\n if (!referenceString.startsWith(prefix)) {\n throw new Error(\n \"Reference string does not start with '@@@langfuseMedia:type='\",\n );\n }\n\n if (!referenceString.endsWith(suffix)) {\n throw new Error(\"Reference string does not end with '@@@'\");\n }\n\n const content = referenceString.slice(prefix.length, -suffix.length);\n\n const pairs = content.split(\"|\");\n const parsedData: { [key: string]: string } = {};\n\n for (const pair of pairs) {\n const [key, value] = pair.split(\"=\", 2);\n parsedData[key] = value;\n }\n\n if (\n !(\"type\" in parsedData && \"id\" in parsedData && \"source\" in parsedData)\n ) {\n throw new Error(\"Missing required fields in reference string\");\n }\n\n return {\n mediaId: parsedData[\"id\"],\n source: parsedData[\"source\"],\n contentType: parsedData[\"type\"] as MediaContentType,\n };\n }\n}\n","import {\n CreatePromptRequest,\n getGlobalLogger,\n LangfuseAPIClient,\n PlaceholderMessage,\n Prompt,\n ChatMessage,\n} from \"@langfuse/core\";\n\nimport { LangfusePromptCache } from \"./promptCache.js\";\nimport {\n ChatPromptClient,\n TextPromptClient,\n LangfusePromptClient,\n} from \"./promptClients.js\";\nimport {\n ChatMessageType,\n CreateChatPromptBodyWithPlaceholders,\n} from \"./types.js\";\n\n/**\n * Manager for prompt operations in Langfuse.\n *\n * Provides methods to create, retrieve, and manage prompts with built-in caching\n * for optimal performance. Supports both text and chat prompts with variable\n * substitution and placeholder functionality.\n *\n * @public\n */\nexport class PromptManager {\n private cache: LangfusePromptCache;\n private apiClient: LangfuseAPIClient;\n\n /**\n * Creates a new PromptManager instance.\n *\n * @param params - Configuration object containing the API client\n * @internal\n */\n constructor(params: { apiClient: LangfuseAPIClient }) {\n const { apiClient } = params;\n\n this.apiClient = apiClient;\n this.cache = new LangfusePromptCache();\n }\n\n get logger() {\n return getGlobalLogger();\n }\n\n /**\n * Creates a new prompt in Langfuse.\n *\n * @param body - The prompt data to create (chat prompt)\n * @returns Promise that resolves to a ChatPromptClient\n */\n async create(\n body: CreateChatPromptBodyWithPlaceholders,\n ): Promise<ChatPromptClient>;\n\n /**\n * Creates a new prompt in Langfuse.\n *\n * @param body - The prompt data to create (text prompt)\n * @returns Promise that resolves to a TextPromptClient\n */\n async create(\n body: Omit<CreatePromptRequest.Text, \"type\"> & { type?: \"text\" },\n ): Promise<TextPromptClient>;\n\n /**\n * Creates a new prompt in Langfuse.\n *\n * @param body - The prompt data to create (chat prompt)\n * @returns Promise that resolves to a ChatPromptClient\n */\n async create(body: CreatePromptRequest.Chat): Promise<ChatPromptClient>;\n\n /**\n * Creates a new prompt in Langfuse.\n *\n * Supports both text and chat prompts. Chat prompts can include placeholders\n * for dynamic content insertion.\n *\n * @param body - The prompt data to create\n * @returns Promise that resolves to the appropriate prompt client\n *\n * @example\n * ```typescript\n * // Create a text prompt\n * const textPrompt = await langfuse.prompt.create({\n * name: \"greeting\",\n * prompt: \"Hello {{name}}!\",\n * type: \"text\"\n * });\n *\n * // Create a chat prompt\n * const chatPrompt = await langfuse.prompt.create({\n * name: \"conversation\",\n * type: \"chat\",\n * prompt: [\n * { role: \"system\", content: \"You are a helpful assistant.\" },\n * { role: \"user\", content: \"{{user_message}}\" }\n * ]\n * });\n * ```\n */\n async create(\n body:\n | CreatePromptRequest.Chat\n | (Omit<CreatePromptRequest.Text, \"type\"> & { type?: \"text\" })\n | CreateChatPromptBodyWithPlaceholders,\n ): Promise<LangfusePromptClient> {\n const requestBody: CreatePromptRequest =\n body.type === \"chat\"\n ? {\n ...body,\n prompt: body.prompt.map((item) => {\n if (\"type\" in item && item.type === ChatMessageType.Placeholder) {\n return {\n type: ChatMessageType.Placeholder,\n name: (item as PlaceholderMessage).name,\n };\n } else {\n // Handle regular ChatMessage (without type field) from API\n return { type: ChatMessageType.ChatMessage, ...item };\n }\n }),\n }\n : {\n ...body,\n type: body.type ?? \"text\",\n };\n\n const promptResponse = await this.apiClient.prompts.create(requestBody);\n\n if (promptResponse.type === \"chat\") {\n return new ChatPromptClient(promptResponse);\n }\n\n return new TextPromptClient(promptResponse);\n }\n\n /**\n * Updates the labels of an existing prompt version.\n *\n * @param params - Update parameters\n * @param params.name - Name of the prompt to update\n * @param params.version - Version number of the prompt to update\n * @param params.newLabels - New labels to apply to the prompt version\n *\n * @returns Promise that resolves to the updated prompt\n *\n * @example\n * ```typescript\n * const updatedPrompt = await langfuse.prompt.update({\n * name: \"my-prompt\",\n * version: 1,\n * newLabels: [\"production\", \"v2\"]\n * });\n * ```\n */\n async update(params: {\n name: string;\n version: number;\n newLabels: string[];\n }): Promise<Prompt> {\n const { name, version, newLabels } = params;\n\n const newPrompt = await this.apiClient.promptVersion.update(name, version, {\n newLabels,\n });\n\n this.cache.invalidate(name);\n\n return newPrompt;\n }\n\n /**\n * Retrieves a text prompt by name.\n *\n * @param name - Name of the prompt to retrieve\n * @param options - Optional retrieval configuration\n * @returns Promise that resolves to a TextPromptClient\n */\n async get(\n name: string,\n options?: {\n /** Specific version to retrieve (defaults to latest) */\n version?: number;\n /** Label to filter by */\n label?: string;\n /** Cache TTL in seconds (0 to disable caching) */\n cacheTtlSeconds?: number;\n /** Fallback text content if prompt fetch fails */\n fallback?: string;\n /** Maximum retry attempts for failed requests */\n maxRetries?: number;\n /** Specify text prompt type */\n type?: \"text\";\n /** Request timeout in milliseconds */\n fetchTimeoutMs?: number;\n },\n ): Promise<TextPromptClient>;\n\n /**\n * Retrieves a chat prompt by name.\n *\n * @param name - Name of the prompt to retrieve\n * @param options - Optional retrieval configuration\n * @returns Promise that resolves to a ChatPromptClient\n */\n async get(\n name: string,\n options?: {\n /** Specific version to retrieve (defaults to latest) */\n version?: number;\n /** Label to filter by */\n label?: string;\n /** Cache TTL in seconds (0 to disable caching) */\n cacheTtlSeconds?: number;\n /** Fallback chat messages if prompt fetch fails */\n fallback?: ChatMessage[];\n /** Maximum retry attempts for failed requests */\n maxRetries?: number;\n /** Specify chat prompt type */\n type: \"chat\";\n /** Request timeout in milliseconds */\n fetchTimeoutMs?: number;\n },\n ): Promise<ChatPromptClient>;\n\n /**\n * Retrieves a prompt by name with intelligent caching.\n *\n * This method implements sophisticated caching behavior:\n * - Fresh prompts are returned immediately from cache\n * - Expired prompts are returned from cache while being refreshed in background\n * - Cache misses trigger immediate fetch with optional fallback support\n *\n * @param name - Name of the prompt to retrieve\n * @param options - Optional retrieval configuration\n * @returns Promise that resolves to the appropriate prompt client\n *\n * @example\n * ```typescript\n * // Get latest version with caching\n * const prompt = await langfuse.prompt.get(\"my-prompt\");\n *\n * // Get specific version\n * const v2Prompt = await langfuse.prompt.get(\"my-prompt\", {\n * version: 2\n * });\n *\n * // Get with label filter\n * const prodPrompt = await langfuse.prompt.get(\"my-prompt\", {\n * label: \"production\"\n * });\n *\n * // Get with fallback\n * const promptWithFallback = await langfuse.prompt.get(\"my-prompt\", {\n * type: \"text\",\n * fallback: \"Hello {{name}}!\"\n * });\n * ```\n */\n async get(\n name: string,\n options?: {\n /** Specific version to retrieve (defaults to latest) */\n version?: number;\n /** Label to filter by */\n label?: string;\n /** Cache TTL in seconds (0 to disable caching) */\n cacheTtlSeconds?: number;\n /** Fallback content if prompt fetch fails */\n fallback?: ChatMessage[] | string;\n /** Maximum retry attempts for failed requests */\n maxRetries?: number;\n /** Prompt type (auto-detected if not specified) */\n type?: \"chat\" | \"text\";\n /** Request timeout in milliseconds */\n fetchTimeoutMs?: number;\n },\n ): Promise<LangfusePromptClient> {\n const cacheKey = this.cache.createKey({\n name,\n label: options?.label,\n });\n const cachedPrompt = this.cache.getIncludingExpired(cacheKey);\n if (!cachedPrompt || options?.cacheTtlSeconds === 0) {\n try {\n return await this.fetchPromptAndUpdateCache({\n name,\n version: options?.version,\n label: options?.label,\n cacheTtlSeconds: options?.cacheTtlSeconds,\n maxRetries: options?.maxRetries,\n fetchTimeoutMs: options?.fetchTimeoutMs,\n });\n } catch (err) {\n if (options?.fallback) {\n const sharedFallbackParams = {\n name,\n version: options?.version ?? 0,\n labels: options.label ? [options.label] : [],\n cacheTtlSeconds: options?.cacheTtlSeconds,\n config: {},\n tags: [],\n };\n\n if (options.type === \"chat\") {\n return new ChatPromptClient(\n {\n ...sharedFallbackParams,\n type: \"chat\",\n prompt: (options.fallback as ChatMessage[]).map((msg) => ({\n type: ChatMessageType.ChatMessage,\n ...msg,\n })),\n },\n true,\n );\n } else {\n return new TextPromptClient(\n {\n ...sharedFallbackParams,\n type: \"text\",\n prompt: options.fallback as string,\n },\n true,\n );\n }\n }\n\n throw err;\n }\n }\n\n if (cachedPrompt.isExpired) {\n // If the cache is not currently being refreshed, start refreshing it and register the promise in the cache\n if (!this.cache.isRefreshing(cacheKey)) {\n const refreshPromptPromise = this.fetchPromptAndUpdateCache({\n name,\n version: options?.version,\n label: options?.label,\n cacheTtlSeconds: options?.cacheTtlSeconds,\n maxRetries: options?.maxRetries,\n fetchTimeoutMs: options?.fetchTimeoutMs,\n }).catch(() => {\n this.logger.warn(\n `Failed to refresh prompt cache '${cacheKey}', stale cache will be used until next refresh succeeds.`,\n );\n });\n this.cache.addRefreshingPromise(cacheKey, refreshPromptPromise);\n }\n\n return cachedPrompt.value;\n }\n\n return cachedPrompt.value;\n }\n\n private async fetchPromptAndUpdateCache(params: {\n name: string;\n version?: number;\n cacheTtlSeconds?: number;\n label?: string;\n maxRetries?: number;\n fetchTimeoutMs?: number;\n }): Promise<LangfusePromptClient> {\n const cacheKey = this.cache.createKey(params);\n\n try {\n const {\n name,\n version,\n cacheTtlSeconds,\n label,\n maxRetries,\n fetchTimeoutMs,\n } = params;\n\n const data = await this.apiClient.prompts.get(\n name,\n {\n version,\n label,\n },\n {\n maxRetries,\n timeoutInSeconds: fetchTimeoutMs ? fetchTimeoutMs / 1_000 : undefined,\n },\n );\n\n let prompt: LangfusePromptClient;\n if (data.type === \"chat\") {\n prompt = new ChatPromptClient(data);\n } else {\n prompt = new TextPromptClient(data);\n }\n\n this.cache.set(cacheKey, prompt, cacheTtlSeconds);\n\n return prompt;\n } catch (error) {\n this.logger.error(`Error fetching prompt '${cacheKey}':`, error);\n\n throw error;\n }\n }\n}\n","import { getGlobalLogger } from \"@langfuse/core\";\n\nimport type { LangfusePromptClient } from \"./promptClients.js\";\n\nexport const DEFAULT_PROMPT_CACHE_TTL_SECONDS = 60;\n\nclass LangfusePromptCacheItem {\n private _expiry: number;\n\n constructor(\n public value: LangfusePromptClient,\n ttlSeconds: number,\n ) {\n this._expiry = Date.now() + ttlSeconds * 1000;\n }\n\n get isExpired(): boolean {\n return Date.now() > this._expiry;\n }\n}\nexport class LangfusePromptCache {\n private _cache: Map<string, LangfusePromptCacheItem>;\n private _defaultTtlSeconds: number;\n private _refreshingKeys: Map<string, Promise<void>>;\n\n constructor() {\n this._cache = new Map<string, LangfusePromptCacheItem>();\n this._defaultTtlSeconds = DEFAULT_PROMPT_CACHE_TTL_SECONDS;\n this._refreshingKeys = new Map<string, Promise<void>>();\n }\n\n public getIncludingExpired(key: string): LangfusePromptCacheItem | null {\n return this._cache.get(key) ?? null;\n }\n\n public createKey(params: {\n name: string;\n version?: number;\n label?: string;\n }): string {\n const { name, version, label } = params;\n const parts = [name];\n\n if (version !== undefined) {\n parts.push(\"version:\" + version.toString());\n } else if (label !== undefined) {\n parts.push(\"label:\" + label);\n } else {\n parts.push(\"label:production\");\n }\n\n return parts.join(\"-\");\n }\n\n public set(\n key: string,\n value: LangfusePromptClient,\n ttlSeconds?: number,\n ): void {\n const effectiveTtlSeconds = ttlSeconds ?? this._defaultTtlSeconds;\n this._cache.set(\n key,\n new LangfusePromptCacheItem(value, effectiveTtlSeconds),\n );\n }\n\n public addRefreshingPromise(key: string, promise: Promise<any>): void {\n this._refreshingKeys.set(key, promise);\n promise\n .then(() => {\n this._refreshingKeys.delete(key);\n })\n .catch(() => {\n this._refreshingKeys.delete(key);\n });\n }\n\n public isRefreshing(key: string): boolean {\n return this._refreshingKeys.has(key);\n }\n\n public invalidate(promptName: string): void {\n getGlobalLogger().debug(\n \"Invalidating cache keys for\",\n promptName,\n this._cache.keys(),\n );\n\n for (const key of this._cache.keys()) {\n if (key.startsWith(promptName)) {\n this._cache.delete(key);\n }\n }\n }\n}\n","import {\n Prompt,\n ChatMessage,\n BasePrompt,\n ChatMessageWithPlaceholders,\n} from \"@langfuse/core\";\nimport mustache from \"mustache\";\n\nimport {\n ChatMessageOrPlaceholder,\n ChatMessageType,\n LangchainMessagesPlaceholder,\n} from \"./types.js\";\n\nmustache.escape = function (text) {\n return text;\n};\n\n/**\n * Base class for all prompt clients.\n *\n * @internal\n */\nabstract class BasePromptClient {\n /** The name of the prompt */\n public readonly name: string;\n /** The version number of the prompt */\n public readonly version: number;\n /** Configuration object associated with the prompt */\n public readonly config: unknown;\n /** Labels associated with the prompt */\n public readonly labels: string[];\n /** Tags associated with the prompt */\n public readonly tags: string[];\n /** Whether this prompt client is using fallback content */\n public readonly isFallback: boolean;\n /** The type of prompt (\"text\" or \"chat\") */\n public readonly type: \"text\" | \"chat\";\n /** Optional commit message for the prompt version */\n public readonly commitMessage: string | null | undefined;\n\n /**\n * Creates a new BasePromptClient instance.\n *\n * @param prompt - The base prompt data\n * @param isFallback - Whether this is fallback content\n * @param type - The prompt type\n * @internal\n */\n constructor(prompt: BasePrompt, isFallback = false, type: \"text\" | \"chat\") {\n this.name = prompt.name;\n this.version = prompt.version;\n this.config = prompt.config;\n this.labels = prompt.labels;\n this.tags = prompt.tags;\n this.isFallback = isFallback;\n this.type = type;\n this.commitMessage = prompt.commitMessage;\n }\n\n /** Gets the raw prompt content */\n abstract get prompt(): string | ChatMessageWithPlaceholders[];\n\n /** Sets the raw prompt content */\n abstract set prompt(value: string | ChatMessageWithPlaceholders[]);\n\n /**\n * Compiles the prompt by substituting variables and resolving placeholders.\n *\n * @param variables - Key-value pairs for variable substitution\n * @param placeholders - Key-value pairs for placeholder resolution\n * @returns The compiled prompt content\n */\n abstract compile(\n variables?: Record<string, string>,\n placeholders?: Record<string, any>,\n ): string | ChatMessage[] | (ChatMessageOrPlaceholder | any)[];\n\n /**\n * Converts the prompt to a format compatible with LangChain.\n *\n * @param options - Options for conversion\n * @param options.placeholders - Placeholders to resolve during conversion\n * @returns The prompt in LangChain-compatible format\n */\n public abstract getLangchainPrompt(options?: {\n placeholders?: Record<string, any>;\n }):\n | string\n | ChatMessage[]\n | ChatMessageOrPlaceholder[]\n | (ChatMessage | LangchainMessagesPlaceholder | any)[];\n\n protected _transformToLangchainVariables(content: string): string {\n const jsonEscapedContent = this.escapeJsonForLangchain(content);\n\n return jsonEscapedContent.replace(/\\{\\{(\\w+)\\}\\}/g, \"{$1}\");\n }\n\n /**\n * Escapes every curly brace that is part of a JSON object by doubling it.\n *\n * A curly brace is considered “JSON-related” when, after skipping any immediate\n * whitespace, the next non-whitespace character is a single (') or double (\") quote.\n *\n * Braces that are already doubled (e.g. `{{variable}}` placeholders) are left untouched.\n *\n * @param text - Input string that may contain JSON snippets.\n * @returns The string with JSON-related braces doubled.\n */\n protected escapeJsonForLangchain(text: string): string {\n const out: string[] = []; // collected characters\n const stack: boolean[] = []; // true = “this { belongs to JSON”, false = normal “{”\n let i = 0;\n const n = text.length;\n\n while (i < n) {\n const ch = text[i];\n\n // ---------- opening brace ----------\n if (ch === \"{\") {\n // leave existing “{{ …” untouched\n if (i + 1 < n && text[i + 1] === \"{\") {\n out.push(\"{{\");\n i += 2;\n continue;\n }\n\n // look ahead to find the next non-space character\n let j = i + 1;\n while (j < n && /\\s/.test(text[j])) {\n j++;\n }\n\n const isJson = j < n && (text[j] === \"'\" || text[j] === '\"');\n out.push(isJson ? \"{{\" : \"{\");\n stack.push(isJson); // remember how this “{” was treated\n i += 1;\n continue;\n }\n\n // ---------- closing brace ----------\n if (ch === \"}\") {\n // leave existing “… }}” untouched\n if (i + 1 < n && text[i + 1] === \"}\") {\n out.push(\"}}\");\n i += 2;\n continue;\n }\n\n const isJson = stack.pop() ?? false;\n out.push(isJson ? \"}}\" : \"}\");\n i += 1;\n continue;\n }\n\n // ---------- any other character ----------\n out.push(ch);\n i += 1;\n }\n\n return out.join(\"\");\n }\n\n /**\n * Serializes the prompt client to JSON.\n *\n * @returns JSON string representation of the prompt\n */\n public abstract toJSON(): string;\n}\n\n/**\n * Client for working with text-based prompts.\n *\n * Provides methods to compile text prompts with variable substitution\n * and convert them to LangChain-compatible formats.\n *\n * @public\n */\nexport class TextPromptClient extends BasePromptClient {\n /** The original prompt response from the API */\n public readonly promptResponse: Prompt.Text;\n /** The text content of the prompt */\n public readonly prompt: string;\n\n /**\n * Creates a new TextPromptClient instance.\n *\n * @param prompt - The text prompt data\n * @param isFallback - Whether this is fallback content\n */\n constructor(prompt: Prompt.Text, isFallback = false) {\n super(prompt, isFallback, \"text\");\n this.promptResponse = prompt;\n this.prompt = prompt.prompt;\n }\n\n /**\n * Compiles the text prompt by substituting variables.\n *\n * Uses Mustache templating to replace {{variable}} placeholders with provided values.\n *\n * @param variables - Key-value pairs for variable substitution\n * @param _placeholders - Ignored for text prompts\n * @returns The compiled text with variables substituted\n *\n * @example\n * ```typescript\n * const prompt = await langfuse.prompt.get(\"greeting\", { type: \"text\" });\n * const compiled = prompt.compile({ name: \"Alice\" });\n * // If prompt is \"Hello {{name}}!\", result is \"Hello Alice!\"\n * ```\n */\n compile(\n variables?: Record<string, string>,\n _placeholders?: Record<string, any>,\n ): string {\n return mustache.render(this.promptResponse.prompt, variables ?? {});\n }\n\n /**\n * Converts the prompt to LangChain PromptTemplate format.\n *\n * Transforms Mustache-style {{variable}} syntax to LangChain's {variable} format.\n *\n * @param _options - Ignored for text prompts\n * @returns The prompt string compatible with LangChain PromptTemplate\n *\n * @example\n * ```typescript\n * const prompt = await langfuse.prompt.get(\"greeting\", { type: \"text\" });\n * const langchainFormat = prompt.getLangchainPrompt();\n * // Transforms \"Hello {{name}}!\" to \"Hello {name}!\"\n * ```\n */\n public getLangchainPrompt(_options?: {\n placeholders?: Record<string, any>;\n }): string {\n return this._transformToLangchainVariables(this.prompt);\n }\n\n public toJSON(): string {\n return JSON.stringify({\n name: this.name,\n prompt: this.prompt,\n version: this.version,\n isFallback: this.isFallback,\n tags: this.tags,\n labels: this.labels,\n type: this.type,\n config: this.config,\n });\n }\n}\n\n/**\n * Client for working with chat-based prompts.\n *\n * Provides methods to compile chat prompts with variable substitution and\n * placeholder resolution, and convert them to LangChain-compatible formats.\n *\n * @public\n */\nexport class ChatPromptClient extends BasePromptClient {\n /** The original prompt response from the API */\n public readonly promptResponse: Prompt.Chat;\n /** The chat messages that make up the prompt */\n public readonly prompt: ChatMessageWithPlaceholders[];\n\n /**\n * Creates a new ChatPromptClient instance.\n *\n * @param prompt - The chat prompt data\n * @param isFallback - Whether this is fallback content\n */\n constructor(prompt: Prompt.Chat, isFallback = false) {\n const normalizedPrompt = ChatPromptClient.normalizePrompt(prompt.prompt);\n const typedPrompt: Prompt.Chat = {\n ...prompt,\n prompt: normalizedPrompt,\n };\n\n super(typedPrompt, isFallback, \"chat\");\n this.promptResponse = typedPrompt;\n this.prompt = normalizedPrompt;\n }\n\n private static normalizePrompt(\n prompt: ChatMessage[] | ChatMessageWithPlaceholders[],\n ): ChatMessageWithPlaceholders[] {\n // Convert ChatMessages to ChatMessageWithPlaceholders for backward compatibility\n return prompt.map((item): ChatMessageWithPlaceholders => {\n if (\"type\" in item) {\n // Already has type field (new format)\n return item as ChatMessageWithPlaceholders;\n } else {\n // Plain ChatMessage (legacy format) - add type field\n return {\n type: ChatMessageType.ChatMessage,\n ...item,\n } as ChatMessageWithPlaceholders;\n }\n });\n }\n\n /**\n * Compiles the chat prompt by replacing placeholders and variables.\n *\n * First resolves placeholders with provided values, then applies variable substitution\n * to message content using Mustache templating. Unresolved placeholders remain\n * as placeholder objects in the output.\n *\n * @param variables - Key-value pairs for Mustache variable substitution in message content\n * @param placeholders - Key-value pairs where keys are placeholder names and values are ChatMessage arrays\n * @returns Array of ChatMessage objects and unresolved placeholder objects\n *\n * @example\n * ```typescript\n * const prompt = await langfuse.prompt.get(\"conversation\", { type: \"chat\" });\n * const compiled = prompt.compile(\n * { user_name: \"Alice\" },\n * { examples: [{ role: \"user\", content: \"Hello\" }, { role: \"assistant\", content: \"Hi!\" }] }\n * );\n * ```\n */\n compile(\n variables?: Record<string, string>,\n placeholders?: Record<string, any>,\n ): (ChatMessageOrPlaceholder | any)[] {\n const messagesWithPlaceholdersReplaced: (ChatMessageOrPlaceholder | any)[] =\n [];\n const placeholderValues = placeholders ?? {};\n\n for (const item of this.prompt) {\n if (\"type\" in item && item.type === ChatMessageType.Placeholder) {\n const placeholderValue = placeholderValues[item.name];\n if (\n Array.isArray(placeholderValue) &&\n placeholderValue.length > 0 &&\n placeholderValue.every(\n (msg) =>\n typeof msg === \"object\" && \"role\" in msg && \"content\" in msg,\n )\n ) {\n messagesWithPlaceholdersReplaced.push(\n ...(placeholderValue as ChatMessage[]),\n );\n } else if (\n Array.isArray(placeholderValue) &&\n placeholderValue.length === 0\n ) {\n // Empty array provided - skip placeholder (don't include it)\n } else if (placeholderValue !== undefined) {\n // Non-standard placeholder value format, just stringfiy\n messagesWithPlaceholdersReplaced.push(\n JSON.stringify(placeholderValue),\n );\n } else {\n // Keep unresolved placeholder in the output\n messagesWithPlaceholdersReplaced.push(\n item as { type: ChatMessageType.Placeholder } & typeof item,\n );\n }\n } else if (\n \"role\" in item &&\n \"content\" in item &&\n item.type === ChatMessageType.ChatMessage\n ) {\n messagesWithPlaceholdersReplaced.push({\n role: item.role,\n content: item.content,\n });\n }\n }\n\n return messagesWithPlaceholdersReplaced.map((item) => {\n if (\n typeof item === \"object\" &&\n item !== null &&\n \"role\" in item &&\n \"content\" in item\n ) {\n return {\n ...item,\n content: mustache.render(item.content, variables ?? {}),\n };\n } else {\n // Return placeholder or stringified value as-is\n return item;\n }\n });\n }\n\n /**\n * Converts the prompt to LangChain ChatPromptTemplate format.\n *\n * Resolves placeholders with provided values and converts unresolved ones\n * to LangChain MessagesPlaceholder objects. Transforms variables from\n * {{var}} to {var} format without rendering them.\n *\n * @param options - Configuration object\n * @param options.placeholders - Key-value pairs for placeholder resolution\n * @returns Array of ChatMessage objects and LangChain MessagesPlaceholder objects\n *\n * @example\n * ```typescript\n * const prompt = await langfuse.prompt.get(\"conversation\", { type: \"chat\" });\n * const langchainFormat = prompt.getLangchainPrompt({\n * placeholders: { examples: [{ role: \"user\", content: \"Hello\" }] }\n * });\n * ```\n */\n public getLangchainPrompt(options?: {\n placeholders?: Record<string, any>;\n }): (ChatMessage | LangchainMessagesPlaceholder | any)[] {\n const messagesWithPlaceholdersReplaced: (\n | ChatMessage\n | LangchainMessagesPlaceholder\n | any\n )[] = [];\n const placeholderValues = options?.placeholders ?? {};\n\n for (const item of this.prompt) {\n if (\"type\" in item && item.type === ChatMessageType.Placeholder) {\n const placeholderValue = placeholderValues[item.name];\n if (\n Array.isArray(placeholderValue) &&\n placeholderValue.length > 0 &&\n placeholderValue.every(\n (msg) =>\n typeof msg === \"object\" && \"role\" in msg && \"content\" in msg,\n )\n ) {\n // Complete placeholder fill-in, replace with it\n messagesWithPlaceholdersReplaced.push(\n ...(placeholderValue as ChatMessage[]).map((msg) => {\n return {\n role: msg.role,\n content: this._transformToLangchainVariables(msg.content),\n };\n }),\n );\n } else if (\n Array.isArray(placeholderValue) &&\n placeholderValue.length === 0\n ) {\n // Skip empty array placeholder\n } else if (placeholderValue !== undefined) {\n // Non-standard placeholder value, just stringify and add directly\n messagesWithPlaceholdersReplaced.push(\n JSON.stringify(placeholderValue),\n );\n } else {\n // Convert unresolved placeholder to Langchain MessagesPlaceholder format\n // see: https://js.langchain.com/docs/concepts/prompt_templates/#messagesplaceholder\n // we convert it to the format without using the class explicitly. Therefore, we\n // don't have to import langchain as a dependency.\n messagesWithPlaceholdersReplaced.push([\n \"placeholder\",\n `{${item.name}}`,\n ]);\n }\n } else if (\n \"role\" in item &&\n \"content\" in item &&\n item.type === ChatMessageType.ChatMessage\n ) {\n messagesWithPlaceholdersReplaced.push({\n role: item.role,\n content: this._transformToLangchainVariables(item.content),\n });\n }\n }\n\n return messagesWithPlaceholdersReplaced;\n }\n\n public toJSON(): string {\n return JSON.stringify({\n name: this.name,\n prompt: this.promptResponse.prompt.map((item) => {\n if (\"type\" in item && item.type === ChatMessageType.ChatMessage) {\n const { type: _, ...messageWithoutType } = item;\n return messageWithoutType;\n }\n return item;\n }),\n version: this.version,\n isFallback: this.isFallback,\n tags: this.tags,\n labels: this.labels,\n type: this.type,\n config: this.config,\n });\n }\n}\n\n/**\n * Union type representing either a text or chat prompt client.\n *\n * @public\n */\nexport type LangfusePromptClient = TextPromptClient | ChatPromptClient;\n","import {\n ChatMessage,\n PlaceholderMessage,\n ChatMessageWithPlaceholders,\n CreatePromptRequest,\n} from \"@langfuse/core\";\n\n/**\n * Enumeration of chat message types in Langfuse prompts.\n *\n * @public\n */\nexport enum ChatMessageType {\n /** Regular chat message with role and content */\n ChatMessage = \"chatmessage\",\n /** Placeholder for dynamic content insertion */\n Placeholder = \"placeholder\",\n}\n\n/**\n * Union type representing either a chat message or a placeholder.\n *\n * Used in compiled prompts where placeholders may remain unresolved.\n *\n * @public\n */\nexport type ChatMessageOrPlaceholder =\n | ChatMessage\n | ({ type: ChatMessageType.Placeholder } & PlaceholderMessage);\n\n/**\n * Represents a LangChain MessagesPlaceholder object.\n *\n * Used when converting Langfuse prompts to LangChain format,\n * unresolved placeholders become LangChain MessagesPlaceholder objects.\n *\n * @public\n */\nexport type LangchainMessagesPlaceholder = {\n /** Name of the variable that will provide the messages */\n variableName: string;\n /** Whether the placeholder is optional (defaults to false) */\n optional?: boolean;\n};\n\n/**\n * Type for creating chat prompts that support both regular messages and placeholders.\n *\n * Extends the standard chat prompt creation request to allow mixed content types.\n *\n * @public\n */\nexport type CreateChatPromptBodyWithPlaceholders = {\n /** Specifies this is a chat prompt */\n type: \"chat\";\n} & Omit<CreatePromptRequest.Chat, \"type\" | \"prompt\"> & {\n /** Array of chat messages and/or placeholders */\n prompt: (ChatMessage | ChatMessageWithPlaceholders)[];\n };\n","import {\n LangfuseAPIClient,\n IngestionEvent,\n getEnv,\n generateUUID,\n ScoreBody,\n getGlobalLogger,\n safeSetTimeout,\n IngestionResponse,\n} from \"@langfuse/core\";\nimport { Span, trace } from \"@opentelemetry/api\";\n\nconst MAX_QUEUE_SIZE = 100_000; // prevent memory leaks\nconst MAX_BATCH_SIZE = 100;\n\n/**\n * Manager for creating and batching score events in Langfuse.\n *\n * The ScoreManager handles automatic batching and flushing of score events\n * to optimize API usage. Scores are automatically sent when the queue reaches\n * a certain size or after a time interval.\n *\n * @public\n */\nexport class ScoreManager {\n private apiClient: LangfuseAPIClient;\n private eventQueue: IngestionEvent[] = [];\n private flushPromise: Promise<void> | null = null;\n private flushTimer: any = null;\n private flushAtCount: number;\n private flushIntervalSeconds: number;\n\n /**\n * Creates a new ScoreManager instance.\n *\n * @param params - Configuration object containing the API client\n * @internal\n */\n constructor(params: { apiClient: LangfuseAPIClient }) {\n this.apiClient = params.apiClient;\n\n const envFlushAtCount = getEnv(\"LANGFUSE_FLUSH_AT\");\n const envFlushIntervalSeconds = getEnv(\"LANGFUSE_FLUSH_INTERVAL\");\n\n this.flushAtCount = envFlushAtCount ? Number(envFlushAtCount) : 10;\n this.flushIntervalSeconds = envFlushIntervalSeconds\n ? Number(envFlushIntervalSeconds)\n : 1;\n }\n\n get logger() {\n return getGlobalLogger();\n }\n\n /**\n * Creates a new score event and adds it to the processing queue.\n *\n * Scores are queued and sent in batches for efficiency. The score will be\n * automatically sent when the queue reaches the flush threshold or after\n * the flush interval expires.\n *\n * @param data - The score data to create\n *\n * @example\n * ```typescript\n * langfuse.score.create({\n * name: \"quality\",\n * value: 0.85,\n * traceId: \"trace-123\",\n * comment: \"High quality response\"\n * });\n * ```\n */\n public create(data: ScoreBody): void {\n const scoreData: ScoreBody = {\n ...data,\n id: data.id ?? generateUUID(),\n environment: data.environment ?? getEnv(\"LANGFUSE_TRACING_ENVIRONMENT\"),\n };\n\n const scoreIngestionEvent: IngestionEvent = {\n id: generateUUID(),\n type: \"score-create\",\n timestamp: new Date().toISOString(),\n body: scoreData,\n };\n\n if (this.eventQueue.length >= MAX_QUEUE_SIZE) {\n this.logger.error(\n `Score queue is at max size ${MAX_QUEUE_SIZE}. Dropping score.`,\n );\n return;\n }\n\n this.eventQueue.push(scoreIngestionEvent);\n this.logger.debug(\n \"Added score event to queue:\\n\",\n JSON.stringify(scoreIngestionEvent, null, 2),\n );\n\n if (this.eventQueue.length >= this.flushAtCount) {\n this.flushPromise = this.flush();\n } else if (!this.flushTimer) {\n this.flushTimer = safeSetTimeout(() => {\n this.flushPromise = this.flush();\n }, this.flushIntervalSeconds * 1_000);\n }\n }\n\n /**\n * Creates a score for a specific observation using its OpenTelemetry span.\n *\n * This method automatically extracts the trace ID and observation ID from\n * the provided span context.\n *\n * @param observation - Object containing the OpenTelemetry span\n * @param data - Score data (traceId and observationId will be auto-populated)\n *\n * @example\n * ```typescript\n * import { startSpan } from '@langfuse/tracing';\n *\n * const span = startSpan({ name: \"my-operation\" });\n * langfuse.score.observation(\n * { otelSpan: span },\n * { name: \"accuracy\", value: 0.92 }\n * );\n * ```\n */\n public observation(\n observation: { otelSpan: Span },\n data: Omit<\n ScoreBody,\n \"traceId\" | \"sessionId\" | \"observationId\" | \"datasetRunId\"\n >,\n ) {\n const { spanId, traceId } = observation.otelSpan.spanContext();\n\n this.create({\n ...data,\n traceId,\n observationId: spanId,\n });\n }\n\n /**\n * Creates a score for a trace using an OpenTelemetry span.\n *\n * This method automatically extracts the trace ID from the provided\n * span context and creates a trace-level score.\n *\n * @param observation - Object containing the OpenTelemetry span\n * @param data - Score data (traceId will be auto-populated)\n *\n * @example\n * ```typescript\n * import { startSpan } from '@langfuse/tracing';\n *\n * const span = startSpan({ name: \"my-operation\" });\n * langfuse.score.trace(\n * { otelSpan: span },\n * { name: \"overall_quality\", value: 0.88 }\n * );\n * ```\n */\n public trace(\n observation: { otelSpan: Span },\n data: Omit<\n ScoreBody,\n \"traceId\" | \"sessionId\" | \"observationId\" | \"datasetRunId\"\n >,\n ) {\n const { traceId } = observation.otelSpan.spanContext();\n\n this.create({\n ...data,\n traceId,\n });\n }\n\n /**\n * Creates a score for the currently active observation.\n *\n * This method automatically detects the active OpenTelemetry span and\n * creates an observation-level score. If no active span is found,\n * a warning is logged and the operation is skipped.\n *\n * @param data - Score data (traceId and observationId will be auto-populated)\n *\n * @example\n * ```typescript\n * import { startActiveSpan } from '@langfuse/tracing';\n *\n * startActiveSpan({ name: \"my-operation\" }, (span) => {\n * // Inside the active span\n * langfuse.score.activeObservation({\n * name: \"relevance\",\n * value: 0.95\n * });\n * });\n * ```\n */\n public activeObservation(\n data: Omit<\n ScoreBody,\n \"traceId\" | \"sessionId\" | \"observationId\" | \"datasetRunId\"\n >,\n ) {\n const currentOtelSpan = trace.getActiveSpan();\n if (!currentOtelSpan) {\n this.logger.warn(\"No active span in context to score.\");\n\n return;\n }\n\n const { spanId, traceId } = currentOtelSpan.spanContext();\n\n this.create({\n ...data,\n traceId,\n observationId: spanId,\n });\n }\n\n /**\n * Creates a score for the currently active trace.\n *\n * This method automatically detects the active OpenTelemetry span and\n * creates a trace-level score. If no active span is found,\n * a warning is logged and the operation is skipped.\n *\n * @param data - Score data (traceId will be auto-populated)\n *\n * @example\n * ```typescript\n * import { startActiveSpan } from '@langfuse/tracing';\n *\n * startActiveSpan({ name: \"my-operation\" }, (span) => {\n * // Inside the active span\n * langfuse.score.activeTrace({\n * name: \"user_satisfaction\",\n * value: 4,\n * comment: \"User rated 4 out of 5 stars\"\n * });\n * });\n * ```\n */\n public activeTrace(\n data: Omit<\n ScoreBody,\n \"traceId\" | \"sessionId\" | \"observationId\" | \"datasetRunId\"\n >,\n ) {\n const currentOtelSpan = trace.getActiveSpan();\n if (!currentOtelSpan) {\n this.logger.warn(\"No active span in context to score trace.\");\n\n return;\n }\n\n const { traceId } = currentOtelSpan.spanContext();\n\n this.create({\n ...data,\n traceId,\n });\n }\n\n private async handleFlush() {\n try {\n if (this.flushTimer) {\n clearTimeout(this.flushTimer);\n this.flushTimer = null;\n }\n\n const promises: Promise<IngestionResponse | void>[] = [];\n\n while (this.eventQueue.length > 0) {\n const batch = this.eventQueue.splice(0, MAX_BATCH_SIZE);\n\n promises.push(\n this.apiClient.ingestion\n .batch({ batch })\n .then((res) => {\n if (res.errors?.length > 0) {\n this.logger.error(\"Error ingesting scores:\", res.errors);\n }\n })\n .catch((err) => {\n this.logger.error(\"Failed to export score batch:\", err);\n }),\n );\n }\n\n await Promise.all(promises);\n } catch (err) {\n this.logger.error(\"Error flushing Score Manager: \", err);\n } finally {\n this.flushPromise = null;\n }\n }\n\n /**\n * Flushes all pending score events to the Langfuse API.\n *\n * This method ensures all queued scores are sent immediately rather than\n * waiting for the automatic flush interval or batch size threshold.\n *\n * @returns Promise that resolves when all pending scores have been sent\n *\n * @example\n * ```typescript\n * langfuse.score.create({ name: \"quality\", value: 0.8 });\n * await langfuse.score.flush(); // Ensures the score is sent immediately\n * ```\n */\n public async flush() {\n return this.flushPromise ?? this.handleFlush();\n }\n\n /**\n * Gracefully shuts down the score manager by flushing all pending scores.\n *\n * This method should be called before your application exits to ensure\n * all score data is sent to Langfuse.\n *\n * @returns Promise that resolves when shutdown is complete\n *\n * @example\n * ```typescript\n * // Before application exit\n * await langfuse.score.shutdown();\n * ```\n */\n public async shutdown() {\n await this.flush();\n }\n}\n","import { Evaluator } from \"./types.js\";\n\n/**\n * Converts an AutoEvals evaluator to a Langfuse-compatible evaluator function.\n *\n * This adapter function bridges the gap between AutoEvals library evaluators\n * and Langfuse experiment evaluators, handling parameter mapping and result\n * formatting automatically.\n *\n * AutoEvals evaluators expect `input`, `output`, and `expected` parameters,\n * while Langfuse evaluators use `input`, `output`, and `expectedOutput`.\n * This function handles the parameter name mapping.\n *\n * @template E - Type of the AutoEvals evaluator function\n * @param autoevalEvaluator - The AutoEvals evaluator function to convert\n * @param params - Optional additional parameters to pass to the AutoEvals evaluator\n * @returns A Langfuse-compatible evaluator function\n *\n * @example Basic usage with AutoEvals\n * ```typescript\n * import { Factuality, Levenshtein } from 'autoevals';\n * import { createEvaluatorFromAutoevals } from '@langfuse/client';\n *\n * const factualityEvaluator = createEvaluatorFromAutoevals(Factuality);\n * const levenshteinEvaluator = createEvaluatorFromAutoevals(Levenshtein);\n *\n * await langfuse.experiment.run({\n * name: \"AutoEvals Integration Test\",\n * data: myDataset,\n * task: myTask,\n * evaluators: [factualityEvaluator, levenshteinEvaluator]\n * });\n * ```\n *\n * @example Using with additional parameters\n * ```typescript\n * import { Factuality } from 'autoevals';\n *\n * const factualityEvaluator = createEvaluatorFromAutoevals(\n * Factuality,\n * { model: 'gpt-4o' } // Additional params for AutoEvals\n * );\n *\n * await langfuse.experiment.run({\n * name: \"Factuality Test\",\n * data: myDataset,\n * task: myTask,\n * evaluators: [factualityEvaluator]\n * });\n * ```\n *\n * @see {@link https://github.com/braintrustdata/autoevals} AutoEvals library documentation\n * @see {@link Evaluator} for Langfuse evaluator specifications\n *\n * @public\n * @since 4.0.0\n */\nexport function createEvaluatorFromAutoevals<E extends CallableFunction>(\n autoevalEvaluator: E,\n params?: Params<E>,\n): Evaluator {\n const langfuseEvaluator: Evaluator = async (langfuseEvaluatorParams) => {\n const score = await autoevalEvaluator({\n ...(params ?? {}),\n input: langfuseEvaluatorParams.input,\n output: langfuseEvaluatorParams.output,\n expected: langfuseEvaluatorParams.expectedOutput,\n });\n\n return {\n name: score.name,\n value: score.score ?? 0,\n metadata: score.metadata,\n };\n };\n\n return langfuseEvaluator;\n}\n\n/**\n * Utility type to extract parameter types from AutoEvals evaluator functions.\n *\n * This type helper extracts the parameter type from an AutoEvals evaluator\n * and omits the standard parameters (input, output, expected) that are\n * handled by the adapter, leaving only the additional configuration parameters.\n *\n * @template E - The AutoEvals evaluator function type\n * @internal\n */\ntype Params<E> = Parameters<\n E extends (...args: any[]) => any ? E : never\n>[0] extends infer P\n ? Omit<P, \"input\" | \"output\" | \"expected\">\n : never;\n"],"mappings":"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","names":["LangfuseAPIClient","getGlobalLogger","getEnv","output","params","getGlobalLogger","obj","getGlobalLogger","getGlobalLogger","ChatMessageType","getGlobalLogger","getGlobalLogger","trace","getGlobalLogger","getEnv","LangfuseAPIClient"]}
|
|
1
|
+
{"version":3,"sources":["../src/LangfuseClient.ts","../src/dataset/index.ts","../src/experiment/ExperimentManager.ts","../src/media/index.ts","../src/prompt/promptManager.ts","../src/prompt/promptCache.ts","../src/prompt/promptClients.ts","../src/prompt/types.ts","../src/score/index.ts","../src/experiment/adapters.ts"],"sourcesContent":["import {\n LangfuseAPIClient,\n LANGFUSE_SDK_VERSION,\n getGlobalLogger,\n getEnv,\n} from \"@langfuse/core\";\n\nimport { DatasetManager } from \"./dataset/index.js\";\nimport { ExperimentManager } from \"./experiment/ExperimentManager.js\";\nimport { MediaManager } from \"./media/index.js\";\nimport { PromptManager } from \"./prompt/index.js\";\nimport { ScoreManager } from \"./score/index.js\";\n\n/**\n * Configuration parameters for initializing a LangfuseClient instance.\n *\n * @public\n */\nexport interface LangfuseClientParams {\n /**\n * Public API key for authentication with Langfuse.\n * Can also be provided via LANGFUSE_PUBLIC_KEY environment variable.\n */\n publicKey?: string;\n\n /**\n * Secret API key for authentication with Langfuse.\n * Can also be provided via LANGFUSE_SECRET_KEY environment variable.\n */\n secretKey?: string;\n\n /**\n * Base URL of the Langfuse instance to connect to.\n * Can also be provided via LANGFUSE_BASE_URL environment variable.\n *\n * @defaultValue \"https://cloud.langfuse.com\"\n */\n baseUrl?: string;\n\n /**\n * Request timeout in seconds.\n * Can also be provided via LANGFUSE_TIMEOUT environment variable.\n *\n * @defaultValue 5\n */\n timeout?: number;\n\n /**\n * Additional HTTP headers to include with API requests.\n */\n additionalHeaders?: Record<string, string>;\n}\n\n/**\n * Main client for interacting with the Langfuse API.\n *\n * The LangfuseClient provides access to all Langfuse functionality including:\n * - Prompt management and retrieval\n * - Dataset operations\n * - Score creation and management\n * - Media upload and handling\n * - Direct API access for advanced use cases\n *\n * @example\n * ```typescript\n * // Initialize with explicit credentials\n * const langfuse = new LangfuseClient({\n * publicKey: \"pk_...\",\n * secretKey: \"sk_...\",\n * baseUrl: \"https://cloud.langfuse.com\"\n * });\n *\n * // Or use environment variables\n * const langfuse = new LangfuseClient();\n *\n * // Use the client\n * const prompt = await langfuse.prompt.get(\"my-prompt\");\n * const compiledPrompt = prompt.compile({ variable: \"value\" });\n * ```\n *\n * @public\n */\nexport class LangfuseClient {\n /**\n * Direct access to the underlying Langfuse API client.\n * Use this for advanced API operations not covered by the high-level managers.\n */\n public api: LangfuseAPIClient;\n\n /**\n * Manager for prompt operations including creation, retrieval, and caching.\n */\n public prompt: PromptManager;\n\n /**\n * Manager for dataset operations including retrieval and item linking.\n */\n public dataset: DatasetManager;\n\n /**\n * Manager for score creation and batch processing.\n */\n public score: ScoreManager;\n\n /**\n * Manager for media upload and reference resolution.\n */\n public media: MediaManager;\n\n /**\n * Manager for running experiments on datasets and data items.\n *\n * The experiment manager provides comprehensive functionality for:\n * - Running tasks on datasets or custom data arrays\n * - Evaluating outputs with custom or pre-built evaluators\n * - Tracking experiment runs with automatic tracing\n * - Generating formatted result summaries\n * - Integrating with AutoEvals library evaluators\n *\n * @example Basic experiment execution\n * ```typescript\n * const langfuse = new LangfuseClient();\n *\n * const result = await langfuse.experiment.run({\n * name: \"Model Evaluation\",\n * description: \"Testing model performance on Q&A tasks\",\n * data: [\n * { input: \"What is 2+2?\", expectedOutput: \"4\" },\n * { input: \"What is the capital of France?\", expectedOutput: \"Paris\" }\n * ],\n * task: async ({ input }) => {\n * // Your model/task implementation\n * const response = await myModel.generate(input);\n * return response;\n * },\n * evaluators: [\n * async ({ output, expectedOutput }) => ({\n * name: \"exact_match\",\n * value: output.trim().toLowerCase() === expectedOutput.toLowerCase() ? 1 : 0\n * })\n * ]\n * });\n *\n * console.log(await result.format());\n * ```\n *\n * @example Using with datasets\n * ```typescript\n * const dataset = await langfuse.dataset.get(\"my-test-dataset\");\n * const result = await dataset.runExperiment({\n * name: \"Production Readiness Test\",\n * task: myTask,\n * evaluators: [accuracyEvaluator, latencyEvaluator],\n * runEvaluators: [overallQualityEvaluator]\n * });\n * ```\n *\n * @see {@link ExperimentManager} for detailed API documentation\n * @see {@link ExperimentParams} for configuration options\n * @see {@link ExperimentResult} for result structure\n * @public\n * @since 4.0.0\n */\n public experiment: ExperimentManager;\n\n private baseUrl: string;\n private projectId: string | null = null;\n\n /**\n * @deprecated Use prompt.get instead\n */\n public getPrompt: typeof PromptManager.prototype.get;\n /**\n * @deprecated Use prompt.create instead\n */\n public createPrompt: typeof PromptManager.prototype.create;\n /**\n * @deprecated Use prompt.update instead\n */\n public updatePrompt: typeof PromptManager.prototype.update;\n /**\n * @deprecated Use dataset.get instead\n */\n public getDataset: typeof DatasetManager.prototype.get;\n /**\n * @deprecated Use api.trace.get instead\n */\n public fetchTrace: typeof LangfuseAPIClient.prototype.trace.get;\n /**\n * @deprecated Use api.trace.list instead\n */\n public fetchTraces: typeof LangfuseAPIClient.prototype.trace.list;\n /**\n * @deprecated Use api.observations.get instead\n */\n public fetchObservation: typeof LangfuseAPIClient.prototype.observations.get;\n /**\n * @deprecated Use api.observations.list instead\n */\n public fetchObservations: typeof LangfuseAPIClient.prototype.observations.getMany;\n /**\n * @deprecated Use api.sessions.get instead\n */\n public fetchSessions: typeof LangfuseAPIClient.prototype.sessions.get;\n /**\n * @deprecated Use api.datasets.getRun instead\n */\n public getDatasetRun: typeof LangfuseAPIClient.prototype.datasets.getRun;\n /**\n * @deprecated Use api.datasets.getRuns instead\n */\n public getDatasetRuns: typeof LangfuseAPIClient.prototype.datasets.getRuns;\n /**\n * @deprecated Use api.datasets.create instead\n */\n public createDataset: typeof LangfuseAPIClient.prototype.datasets.create;\n /**\n * @deprecated Use api.datasetItems.get instead\n */\n public getDatasetItem: typeof LangfuseAPIClient.prototype.datasetItems.get;\n /**\n * @deprecated Use api.datasetItems.create instead\n */\n public createDatasetItem: typeof LangfuseAPIClient.prototype.datasetItems.create;\n /**\n * @deprecated Use api.media.get instead\n */\n public fetchMedia: typeof LangfuseAPIClient.prototype.media.get;\n /**\n * @deprecated Use media.resolveReferences instead\n */\n public resolveMediaReferences: typeof MediaManager.prototype.resolveReferences;\n\n /**\n * Creates a new LangfuseClient instance.\n *\n * @param params - Configuration parameters. If not provided, will use environment variables.\n *\n * @throws Will log warnings if required credentials are not provided\n *\n * @example\n * ```typescript\n * // With explicit configuration\n * const client = new LangfuseClient({\n * publicKey: \"pk_...\",\n * secretKey: \"sk_...\",\n * baseUrl: \"https://your-instance.langfuse.com\"\n * });\n *\n * // Using environment variables\n * const client = new LangfuseClient();\n * ```\n */\n constructor(params?: LangfuseClientParams) {\n const logger = getGlobalLogger();\n\n const publicKey = params?.publicKey ?? getEnv(\"LANGFUSE_PUBLIC_KEY\");\n const secretKey = params?.secretKey ?? getEnv(\"LANGFUSE_SECRET_KEY\");\n this.baseUrl =\n params?.baseUrl ??\n getEnv(\"LANGFUSE_BASE_URL\") ??\n getEnv(\"LANGFUSE_BASEURL\") ?? // legacy v2\n \"https://cloud.langfuse.com\";\n\n if (!publicKey) {\n logger.warn(\n \"No public key provided in constructor or as LANGFUSE_PUBLIC_KEY env var. Client operations will fail.\",\n );\n }\n if (!secretKey) {\n logger.warn(\n \"No secret key provided in constructor or as LANGFUSE_SECRET_KEY env var. Client operations will fail.\",\n );\n }\n const timeoutSeconds =\n params?.timeout ?? Number(getEnv(\"LANGFUSE_TIMEOUT\") ?? 5);\n\n this.api = new LangfuseAPIClient({\n baseUrl: this.baseUrl,\n username: publicKey,\n password: secretKey,\n xLangfusePublicKey: publicKey,\n xLangfuseSdkVersion: LANGFUSE_SDK_VERSION,\n xLangfuseSdkName: \"javascript\",\n environment: \"\", // noop as baseUrl is set\n headers: params?.additionalHeaders,\n });\n\n logger.debug(\"Initialized LangfuseClient with params:\", {\n publicKey,\n baseUrl: this.baseUrl,\n timeoutSeconds,\n });\n\n this.prompt = new PromptManager({ apiClient: this.api });\n this.dataset = new DatasetManager({ langfuseClient: this });\n this.score = new ScoreManager({ apiClient: this.api });\n this.media = new MediaManager({ apiClient: this.api });\n this.experiment = new ExperimentManager({ langfuseClient: this });\n\n // Keep v3 compat by exposing old interface\n this.getPrompt = this.prompt.get.bind(this.prompt); // keep correct this context for cache access\n this.createPrompt = this.prompt.create.bind(this.prompt);\n this.updatePrompt = this.prompt.update.bind(this.prompt);\n this.getDataset = this.dataset.get;\n this.fetchTrace = this.api.trace.get;\n this.fetchTraces = this.api.trace.list;\n this.fetchObservation = this.api.observations.get;\n this.fetchObservations = this.api.observations.getMany;\n this.fetchSessions = this.api.sessions.get;\n this.getDatasetRun = this.api.datasets.getRun;\n this.getDatasetRuns = this.api.datasets.getRuns;\n this.createDataset = this.api.datasets.create;\n this.getDatasetItem = this.api.datasetItems.get;\n this.createDatasetItem = this.api.datasetItems.create;\n this.fetchMedia = this.api.media.get;\n this.resolveMediaReferences = this.media.resolveReferences;\n }\n\n /**\n * Flushes any pending score events to the Langfuse API.\n *\n * This method ensures all queued scores are sent immediately rather than\n * waiting for the automatic flush interval or batch size threshold.\n *\n * @returns Promise that resolves when all pending scores have been sent\n *\n * @example\n * ```typescript\n * langfuse.score.create({ name: \"quality\", value: 0.8 });\n * await langfuse.flush(); // Ensures the score is sent immediately\n * ```\n */\n public async flush() {\n return this.score.flush();\n }\n\n /**\n * Gracefully shuts down the client by flushing all pending data.\n *\n * This method should be called before your application exits to ensure\n * all data is sent to Langfuse.\n *\n * @returns Promise that resolves when shutdown is complete\n *\n * @example\n * ```typescript\n * // Before application exit\n * await langfuse.shutdown();\n * ```\n */\n public async shutdown() {\n return this.score.shutdown();\n }\n\n /**\n * Generates a URL to view a specific trace in the Langfuse UI.\n *\n * @param traceId - The ID of the trace to generate a URL for\n * @returns Promise that resolves to the trace URL\n *\n * @example\n * ```typescript\n * const traceId = \"trace-123\";\n * const url = await langfuse.getTraceUrl(traceId);\n * console.log(`View trace at: ${url}`);\n * ```\n */\n public async getTraceUrl(traceId: string) {\n let projectId = this.projectId;\n\n if (!projectId) {\n projectId = (await this.api.projects.get()).data[0].id;\n this.projectId = projectId;\n }\n\n const traceUrl = `${this.baseUrl}/project/${projectId}/traces/${traceId}`;\n\n return traceUrl;\n }\n}\n","import { Dataset, DatasetRunItem, DatasetItem } from \"@langfuse/core\";\nimport { Span } from \"@opentelemetry/api\";\n\nimport { ExperimentResult, ExperimentParams } from \"../experiment/types.js\";\nimport { LangfuseClient } from \"../LangfuseClient.js\";\n\n/**\n * Function type for running experiments on Langfuse datasets.\n *\n * This function type is attached to fetched datasets to enable convenient\n * experiment execution directly on dataset objects.\n *\n * @param params - Experiment parameters excluding data (since data comes from the dataset)\n * @returns Promise resolving to experiment results\n *\n * @example\n * ```typescript\n * const dataset = await langfuse.dataset.get(\"my-dataset\");\n * const result = await dataset.runExperiment({\n * name: \"Model Evaluation\",\n * runName: \"Model Evaluation Run 1\", // optional\n * task: myTask,\n * evaluators: [myEvaluator]\n * });\n * ```\n *\n * @public\n * @since 4.0.0\n */\nexport type RunExperimentOnDataset = (\n params: Omit<ExperimentParams<any, any, Record<string, any>>, \"data\">,\n) => Promise<ExperimentResult<any, any, Record<string, any>>>;\n\n/**\n * Enhanced dataset object with additional methods for linking and experiments.\n *\n * This type extends the base Dataset with functionality for:\n * - Linking dataset items to traces/observations\n * - Running experiments directly on the dataset\n *\n * @example Working with a fetched dataset\n * ```typescript\n * const dataset = await langfuse.dataset.get(\"my-evaluation-dataset\");\n *\n * // Access dataset metadata\n * console.log(dataset.name, dataset.description);\n *\n * // Work with individual items\n * for (const item of dataset.items) {\n * console.log(item.input, item.expectedOutput);\n *\n * // Link item to a trace\n * await item.link(myObservation, \"experiment-run-1\");\n * }\n *\n * // Run experiments on the entire dataset\n * const result = await dataset.runExperiment({\n * name: \"Model Comparison\",\n * task: myTask,\n * evaluators: [accuracyEvaluator]\n * });\n * ```\n *\n * @public\n * @since 4.0.0\n */\nexport type FetchedDataset = Dataset & {\n /** Dataset items with additional linking functionality */\n items: (DatasetItem & { link: LinkDatasetItemFunction })[];\n /** Function to run experiments directly on this dataset */\n runExperiment: RunExperimentOnDataset;\n};\n\n/**\n * Function type for linking dataset items to OpenTelemetry spans.\n *\n * This function creates a connection between a dataset item and a trace/observation,\n * enabling tracking of which dataset items were used in which experiments or runs.\n * This is essential for creating dataset runs and tracking experiment lineage.\n *\n * @param obj - Object containing the OpenTelemetry span to link to\n * @param obj.otelSpan - The OpenTelemetry span from a Langfuse observation\n * @param runName - Name of the experiment run for grouping related items\n * @param runArgs - Optional configuration for the dataset run\n * @param runArgs.description - Description of the experiment run\n * @param runArgs.metadata - Additional metadata to attach to the run\n * @returns Promise that resolves to the created dataset run item\n *\n * @example Basic linking\n * ```typescript\n * const dataset = await langfuse.dataset.get(\"my-dataset\");\n * const span = startObservation(\"my-task\", { input: \"test\" });\n * span.update({ output: \"result\" });\n * span.end();\n *\n * // Link the dataset item to this execution\n * await dataset.items[0].link(\n * { otelSpan: span.otelSpan },\n * \"experiment-run-1\"\n * );\n * ```\n *\n * @example Linking with metadata\n * ```typescript\n * await dataset.items[0].link(\n * { otelSpan: span.otelSpan },\n * \"model-comparison-v2\",\n * {\n * description: \"Comparing GPT-4 vs Claude performance\",\n * metadata: {\n * modelVersion: \"gpt-4-1106-preview\",\n * temperature: 0.7,\n * timestamp: new Date().toISOString()\n * }\n * }\n * );\n * ```\n *\n * @see {@link https://langfuse.com/docs/datasets} Langfuse datasets documentation\n * @public\n * @since 4.0.0\n */\nexport type LinkDatasetItemFunction = (\n obj: { otelSpan: Span },\n runName: string,\n runArgs?: {\n /** Description of the dataset run */\n description?: string;\n /** Additional metadata for the dataset run */\n metadata?: any;\n },\n) => Promise<DatasetRunItem>;\n\n/**\n * Manager for dataset operations in Langfuse.\n *\n * Provides methods to retrieve datasets and their items, with automatic\n * pagination handling and convenient linking functionality for experiments.\n *\n * @public\n */\nexport class DatasetManager {\n private langfuseClient: LangfuseClient;\n\n /**\n * Creates a new DatasetManager instance.\n *\n * @param params - Configuration object containing the API client\n * @internal\n */\n constructor(params: { langfuseClient: LangfuseClient }) {\n this.langfuseClient = params.langfuseClient;\n }\n\n /**\n * Retrieves a dataset by name with all its items and experiment functionality.\n *\n * This method fetches a dataset and all its associated items, with support\n * for automatic pagination to handle large datasets efficiently. The returned\n * dataset object includes enhanced functionality for linking items to traces\n * and running experiments directly on the dataset.\n *\n * @param name - The name of the dataset to retrieve\n * @param options - Optional configuration for data fetching\n * @param options.fetchItemsPageSize - Number of items to fetch per page (default: 50)\n * @returns Promise resolving to enhanced dataset with items, linking, and experiment capabilities\n *\n * @example Basic dataset retrieval\n * ```typescript\n * const dataset = await langfuse.dataset.get(\"my-evaluation-dataset\");\n * console.log(`Dataset ${dataset.name} has ${dataset.items.length} items`);\n *\n * // Access dataset properties\n * console.log(dataset.description);\n * console.log(dataset.metadata);\n * ```\n *\n * @example Working with dataset items\n * ```typescript\n * const dataset = await langfuse.dataset.get(\"qa-dataset\");\n *\n * for (const item of dataset.items) {\n * console.log(\"Question:\", item.input);\n * console.log(\"Expected Answer:\", item.expectedOutput);\n *\n * // Each item has a link function for connecting to traces\n * // await item.link(span, \"experiment-name\");\n * }\n * ```\n *\n * @example Running experiments on datasets\n * ```typescript\n * const dataset = await langfuse.dataset.get(\"benchmark-dataset\");\n *\n * const result = await dataset.runExperiment({\n * name: \"GPT-4 Benchmark\",\n * runName: \"GPT-4 Benchmark v1.2\", // optional exact run name\n * description: \"Evaluating GPT-4 on our benchmark tasks\",\n * task: async ({ input }) => {\n * const response = await openai.chat.completions.create({\n * model: \"gpt-4\",\n * messages: [{ role: \"user\", content: input }]\n * });\n * return response.choices[0].message.content;\n * },\n * evaluators: [\n * async ({ output, expectedOutput }) => ({\n * name: \"exact_match\",\n * value: output === expectedOutput ? 1 : 0\n * })\n * ]\n * });\n *\n * console.log(await result.format());\n * ```\n *\n * @example Handling large datasets\n * ```typescript\n * // For very large datasets, use smaller page sizes\n * const largeDataset = await langfuse.dataset.get(\n * \"large-dataset\",\n * { fetchItemsPageSize: 100 }\n * );\n * ```\n *\n * @throws {Error} If the dataset does not exist or cannot be accessed\n * @see {@link FetchedDataset} for the complete return type specification\n * @see {@link RunExperimentOnDataset} for experiment execution details\n * @public\n * @since 4.0.0\n */\n async get(\n name: string,\n options?: {\n fetchItemsPageSize: number;\n },\n ): Promise<FetchedDataset> {\n const dataset = await this.langfuseClient.api.datasets.get(name);\n const items: DatasetItem[] = [];\n\n let page = 1;\n\n while (true) {\n const itemsResponse = await this.langfuseClient.api.datasetItems.list({\n datasetName: name,\n limit: options?.fetchItemsPageSize ?? 50,\n page,\n });\n\n items.push(...itemsResponse.data);\n\n if (itemsResponse.meta.totalPages <= page) {\n break;\n }\n\n page++;\n }\n\n const itemsWithLinkMethod = items.map((item) => ({\n ...item,\n link: this.createDatasetItemLinkFunction(item),\n }));\n\n const runExperiment: RunExperimentOnDataset = (params) => {\n return this.langfuseClient.experiment.run({\n data: items,\n ...params,\n });\n };\n\n const returnDataset = {\n ...dataset,\n items: itemsWithLinkMethod,\n runExperiment,\n };\n\n return returnDataset;\n }\n\n /**\n * Creates a link function for a specific dataset item.\n *\n * @param item - The dataset item to create a link function for\n * @returns A function that can link the item to OpenTelemetry spans\n * @internal\n */\n private createDatasetItemLinkFunction(\n item: DatasetItem,\n ): LinkDatasetItemFunction {\n const linkFunction = async (\n obj: { otelSpan: Span },\n runName: string,\n runArgs?: {\n description?: string;\n metadata?: any;\n },\n ): Promise<DatasetRunItem> => {\n return await this.langfuseClient.api.datasetRunItems.create({\n runName,\n datasetItemId: item.id,\n traceId: obj.otelSpan.spanContext().traceId,\n runDescription: runArgs?.description,\n metadata: runArgs?.metadata,\n });\n };\n\n return linkFunction;\n }\n}\n","import {\n DatasetItem,\n getGlobalLogger,\n propagateAttributes,\n serializeValue,\n createExperimentId,\n createExperimentItemId,\n LangfuseOtelSpanAttributes,\n LANGFUSE_SDK_EXPERIMENT_ENVIRONMENT,\n} from \"@langfuse/core\";\nimport { startActiveObservation } from \"@langfuse/tracing\";\nimport { ProxyTracerProvider, trace } from \"@opentelemetry/api\";\n\nimport { LangfuseClient } from \"../LangfuseClient.js\";\n\nimport {\n ExperimentParams,\n ExperimentResult,\n ExperimentTask,\n ExperimentItem,\n ExperimentItemResult,\n Evaluator,\n Evaluation,\n} from \"./types.js\";\n\n/**\n * Manages the execution and evaluation of experiments on datasets.\n *\n * The ExperimentManager provides a comprehensive framework for running experiments\n * that test models or tasks against datasets, with support for automatic evaluation,\n * scoring.\n *\n * @example Basic experiment usage\n * ```typescript\n * const langfuse = new LangfuseClient();\n *\n * const result = await langfuse.experiment.run({\n * name: \"Capital Cities Test\",\n * description: \"Testing model knowledge of world capitals\",\n * data: [\n * { input: \"France\", expectedOutput: \"Paris\" },\n * { input: \"Germany\", expectedOutput: \"Berlin\" }\n * ],\n * task: async ({ input }) => {\n * const response = await openai.chat.completions.create({\n * model: \"gpt-4\",\n * messages: [{ role: \"user\", content: `What is the capital of ${input}?` }]\n * });\n * return response.choices[0].message.content;\n * },\n * evaluators: [\n * async ({ input, output, expectedOutput }) => ({\n * name: \"exact_match\",\n * value: output === expectedOutput ? 1 : 0\n * })\n * ]\n * });\n *\n * console.log(await result.format());\n * ```\n *\n * @example Using with Langfuse datasets\n * ```typescript\n * const dataset = await langfuse.dataset.get(\"my-dataset\");\n *\n * const result = await dataset.runExperiment({\n * name: \"Model Comparison\",\n * task: myTask,\n * evaluators: [myEvaluator],\n * runEvaluators: [averageScoreEvaluator]\n * });\n * ```\n *\n * @public\n */\nexport class ExperimentManager {\n private langfuseClient: LangfuseClient;\n\n /**\n * Creates a new ExperimentManager instance.\n *\n * @param params - Configuration object\n * @param params.langfuseClient - The Langfuse client instance for API communication\n * @internal\n */\n constructor(params: { langfuseClient: LangfuseClient }) {\n this.langfuseClient = params.langfuseClient;\n }\n\n /**\n * Gets the global logger instance for experiment-related logging.\n *\n * @returns The global logger instance\n * @internal\n */\n get logger() {\n return getGlobalLogger();\n }\n\n /**\n * Executes an experiment by running a task on each data item and evaluating the results.\n *\n * This method orchestrates the complete experiment lifecycle:\n * 1. Executes the task function on each data item with proper tracing\n * 2. Runs item-level evaluators on each task output\n * 3. Executes run-level evaluators on the complete result set\n * 4. Links results to dataset runs (for Langfuse datasets)\n * 5. Stores all scores and traces in Langfuse\n *\n * @param config - The experiment configuration\n * @param config.name - Human-readable name for the experiment\n * @param config.runName - Optional exact name for the experiment run (defaults to name + timestamp)\n * @param config.description - Optional description of the experiment's purpose\n * @param config.metadata - Optional metadata to attach to the experiment run\n * @param config.data - Array of data items to process (ExperimentItem[] or DatasetItem[])\n * @param config.task - Function that processes each data item and returns output\n * @param config.evaluators - Optional array of functions to evaluate each item's output\n * @param config.runEvaluators - Optional array of functions to evaluate the entire run\n * @param config.maxConcurrency - Maximum number of concurrent task executions (default: 50)\n *\n * @returns Promise that resolves to experiment results including:\n * - runName: The experiment run name (either provided or generated)\n * - itemResults: Results for each processed data item\n * - runEvaluations: Results from run-level evaluators\n * - datasetRunId: ID of the dataset run (if using Langfuse datasets)\n * - format: Function to format results for display\n *\n * @throws {Error} When task execution fails and cannot be handled gracefully\n * @throws {Error} When required evaluators fail critically\n *\n * @example Simple experiment\n * ```typescript\n * const result = await langfuse.experiment.run({\n * name: \"Translation Quality Test\",\n * data: [\n * { input: \"Hello world\", expectedOutput: \"Hola mundo\" },\n * { input: \"Good morning\", expectedOutput: \"Buenos días\" }\n * ],\n * task: async ({ input }) => translateText(input, 'es'),\n * evaluators: [\n * async ({ output, expectedOutput }) => ({\n * name: \"bleu_score\",\n * value: calculateBleuScore(output, expectedOutput)\n * })\n * ]\n * });\n * ```\n *\n * @example Experiment with concurrency control\n * ```typescript\n * const result = await langfuse.experiment.run({\n * name: \"Large Scale Evaluation\",\n * data: largeBatchOfItems,\n * task: expensiveModelCall,\n * maxConcurrency: 5, // Process max 5 items simultaneously\n * evaluators: [myEvaluator],\n * runEvaluators: [\n * async ({ itemResults }) => ({\n * name: \"average_score\",\n * value: itemResults.reduce((acc, r) => acc + r.evaluations[0].value, 0) / itemResults.length\n * })\n * ]\n * });\n * ```\n *\n * @see {@link ExperimentParams} for detailed parameter documentation\n * @see {@link ExperimentResult} for detailed return value documentation\n * @see {@link Evaluator} for evaluator function specifications\n * @see {@link RunEvaluator} for run evaluator function specifications\n *\n * @public\n */\n async run<\n Input = any,\n ExpectedOutput = any,\n Metadata extends Record<string, any> = Record<string, any>,\n >(\n config: ExperimentParams<Input, ExpectedOutput, Metadata>,\n ): Promise<ExperimentResult<Input, ExpectedOutput, Metadata>> {\n const {\n data,\n evaluators,\n task,\n name,\n runName: providedRunName,\n description,\n metadata,\n maxConcurrency: batchSize = 50,\n runEvaluators,\n } = config;\n\n const runName = this.createExperimentRunName({\n name,\n runName: providedRunName,\n });\n\n if (!this.isOtelRegistered()) {\n this.logger.warn(\n \"OpenTelemetry has not been set up. Traces will not be sent to Langfuse.See our docs on how to set up OpenTelemetry: https://langfuse.com/docs/observability/sdk/typescript/setup#tracing-setup\",\n );\n }\n\n const itemResults: ExperimentItemResult<Input, ExpectedOutput, Metadata>[] =\n [];\n\n for (let i = 0; i < data.length; i += batchSize) {\n const batch = data.slice(i, i + batchSize);\n\n const promises: Promise<\n ExperimentItemResult<Input, ExpectedOutput, Metadata>\n >[] = batch.map(async (item) => {\n return this.runItem({\n item,\n evaluators,\n task,\n experimentName: name,\n experimentRunName: runName,\n experimentDescription: description,\n experimentMetadata: metadata,\n });\n });\n\n const settledResults = await Promise.allSettled(promises);\n const results = settledResults.reduce(\n (acc, settledResult) => {\n if (settledResult.status === \"fulfilled\") {\n acc.push(settledResult.value);\n } else {\n const errorMessage =\n settledResult.reason instanceof Error\n ? settledResult.reason.message\n : String(settledResult.reason);\n this.logger.error(\n `Task failed with error: ${errorMessage}. Skipping item.`,\n );\n }\n return acc;\n },\n [] as ExperimentItemResult<Input, ExpectedOutput, Metadata>[],\n );\n\n itemResults.push(...results);\n }\n\n // Get dataset run URL\n const datasetRunId =\n itemResults.length > 0 ? itemResults[0].datasetRunId : undefined;\n\n let datasetRunUrl = undefined;\n if (datasetRunId && data.length > 0 && \"datasetId\" in data[0]) {\n const datasetId = data[0].datasetId;\n const projectUrl = (await this.langfuseClient.getTraceUrl(\"mock\")).split(\n \"/traces\",\n )[0];\n\n datasetRunUrl = `${projectUrl}/datasets/${datasetId}/runs/${datasetRunId}`;\n }\n\n // Execute run evaluators\n let runEvaluations: Evaluation[] = [];\n if (runEvaluators && runEvaluators?.length > 0) {\n const promises = runEvaluators.map(async (runEvaluator) => {\n return runEvaluator({ itemResults })\n .then((result) => {\n // Handle both single evaluation and array of evaluations\n return Array.isArray(result) ? result : [result];\n })\n .catch((err) => {\n this.logger.error(\"Run evaluator failed with error \", err);\n\n throw err;\n });\n });\n\n runEvaluations = (await Promise.allSettled(promises)).reduce(\n (acc, settledPromise) => {\n if (settledPromise.status === \"fulfilled\") {\n acc.push(...settledPromise.value);\n }\n\n return acc;\n },\n [] as Evaluation[],\n );\n\n if (datasetRunId) {\n runEvaluations.forEach((runEval) =>\n this.langfuseClient.score.create({ datasetRunId, ...runEval }),\n );\n }\n }\n\n await this.langfuseClient.score.flush();\n\n return {\n runName,\n itemResults,\n datasetRunId,\n datasetRunUrl,\n runEvaluations,\n format: async (options?: { includeItemResults?: boolean }) =>\n await this.prettyPrintResults({\n datasetRunUrl,\n itemResults,\n originalData: data,\n runEvaluations,\n name: config.name,\n runName,\n description: config.description,\n includeItemResults: options?.includeItemResults ?? false,\n }),\n };\n }\n\n /**\n * Executes the task and evaluators for a single data item.\n *\n * This method handles the complete processing pipeline for one data item:\n * 1. Executes the task within a traced observation span\n * 2. Links the result to a dataset run (if applicable)\n * 3. Runs all item-level evaluators on the output\n * 4. Stores evaluation scores in Langfuse\n * 5. Handles errors gracefully by continuing with remaining evaluators\n *\n * @param params - Parameters for item execution\n * @param params.experimentName - Name of the parent experiment\n * @param params.experimentRunName - Run name for the parent experiment\n * @param params.experimentDescription - Description of the parent experiment\n * @param params.experimentMetadata - Metadata for the parent experiment\n * @param params.item - The data item to process\n * @param params.task - The task function to execute\n * @param params.evaluators - Optional evaluators to run on the output\n *\n * @returns Promise resolving to the item result with output, evaluations, and trace info\n *\n * @throws {Error} When task execution fails (propagated from task function)\n *\n * @internal\n */\n private async runItem<\n Input = any,\n ExpectedOutput = any,\n Metadata extends Record<string, any> = Record<string, any>,\n >(params: {\n experimentName: ExperimentParams<Input, ExpectedOutput, Metadata>[\"name\"];\n experimentRunName: string;\n experimentDescription: ExperimentParams<\n Input,\n ExpectedOutput,\n Metadata\n >[\"description\"];\n experimentMetadata: ExperimentParams<\n Input,\n ExpectedOutput,\n Metadata\n >[\"metadata\"];\n item: ExperimentParams<Input, ExpectedOutput, Metadata>[\"data\"][0];\n task: ExperimentTask<Input, ExpectedOutput, Metadata>;\n evaluators?: Evaluator<Input, ExpectedOutput, Metadata>[];\n }): Promise<ExperimentItemResult<Input, ExpectedOutput, Metadata>> {\n const { item, evaluators = [], task, experimentMetadata } = params;\n\n const { output, traceId, observationId, datasetRunId } =\n await startActiveObservation(\"experiment-item-run\", async (span) => {\n // Extract experiment data\n const input = item.input;\n const expectedOutput = item.expectedOutput;\n const itemMetadata = item.metadata;\n const datasetId = \"datasetId\" in item ? item.datasetId : undefined;\n const datasetItemId = \"id\" in item ? item.id : undefined;\n const traceId = span.traceId;\n const observationId = span.id;\n\n // Validate input is present\n if (input === undefined) {\n throw new Error(\"Experiment item is missing input. Skipping item.\");\n }\n\n let datasetRunId: string | undefined = undefined;\n\n if (datasetItemId) {\n try {\n const result = await this.langfuseClient.api.datasetRunItems.create(\n {\n runName: params.experimentRunName,\n runDescription: params.experimentDescription,\n metadata: params.experimentMetadata,\n datasetItemId,\n traceId,\n observationId,\n },\n );\n\n datasetRunId = result.datasetRunId;\n } catch (err) {\n this.logger.error(\"Linking dataset run item failed\", err);\n }\n }\n\n // Generate IDs\n const experimentItemId =\n datasetItemId || (await createExperimentItemId(input));\n const experimentId = datasetRunId || (await createExperimentId());\n\n // Set non-propagated experiment attributes directly on root span\n const rootSpanAttributes: Record<string, string> = {\n [LangfuseOtelSpanAttributes.ENVIRONMENT]:\n LANGFUSE_SDK_EXPERIMENT_ENVIRONMENT,\n };\n if (params.experimentDescription) {\n rootSpanAttributes[\n LangfuseOtelSpanAttributes.EXPERIMENT_DESCRIPTION\n ] = params.experimentDescription;\n }\n\n if (expectedOutput !== undefined) {\n const serialized = serializeValue(expectedOutput);\n if (serialized) {\n rootSpanAttributes[\n LangfuseOtelSpanAttributes.EXPERIMENT_ITEM_EXPECTED_OUTPUT\n ] = serialized;\n }\n }\n\n span.otelSpan.setAttributes(rootSpanAttributes);\n\n // Propagate experiment context to all child spans\n const output = await propagateAttributes(\n {\n _internalExperiment: {\n experimentId,\n experimentName: params.experimentRunName,\n experimentMetadata: serializeValue(experimentMetadata),\n experimentDatasetId: datasetId,\n experimentItemId,\n experimentItemMetadata: serializeValue(itemMetadata),\n experimentItemRootObservationId: span.id,\n },\n },\n async () => await task(item),\n );\n\n span.update({\n input,\n output,\n metadata: {\n experiment_name: params.experimentName,\n experiment_run_name: params.experimentRunName,\n ...experimentMetadata,\n ...(itemMetadata ?? {}),\n ...(datasetId && datasetItemId\n ? {\n dataset_id: datasetId,\n dataset_item_id: datasetItemId,\n }\n : {}),\n },\n });\n\n return {\n output,\n traceId,\n observationId,\n datasetRunId,\n };\n });\n\n const evalPromises: Promise<Evaluation[]>[] = evaluators.map(\n async (evaluator) => {\n const params = {\n input: item.input as any,\n expectedOutput: item.expectedOutput as any,\n output,\n };\n\n return evaluator(params)\n .then((result) => {\n // Handle both single evaluation and array of evaluations\n return Array.isArray(result) ? result : [result];\n })\n .catch((err) => {\n this.logger.error(\n `Evaluator '${evaluator.name}' failed for params \\n\\n${JSON.stringify(params)}\\n\\n with error: ${err}`,\n );\n\n throw err;\n });\n },\n );\n\n const evals = (await Promise.allSettled(evalPromises)).reduce(\n (acc, promiseResult) => {\n if (promiseResult.status === \"fulfilled\") {\n acc.push(...promiseResult.value.flat());\n }\n\n return acc;\n },\n [] as Evaluation[],\n );\n\n for (const ev of evals) {\n this.langfuseClient.score.create({\n traceId,\n observationId,\n ...ev,\n });\n }\n\n return {\n output,\n evaluations: evals,\n traceId,\n datasetRunId,\n item,\n };\n }\n\n /**\n * Formats experiment results into a human-readable string representation.\n *\n * Creates a comprehensive, nicely formatted summary of the experiment including:\n * - Individual item results with inputs, outputs, expected values, and scores\n * - Dataset item and trace links (when available)\n * - Experiment overview with aggregate statistics\n * - Average scores across all evaluations\n * - Run-level evaluation results\n * - Links to dataset runs in the Langfuse UI\n *\n * @param params - Formatting parameters\n * @param params.datasetRunUrl - Optional URL to the dataset run in Langfuse UI\n * @param params.itemResults - Results from processing each data item\n * @param params.originalData - The original input data items\n * @param params.runEvaluations - Results from run-level evaluators\n * @param params.name - Name of the experiment\n * @param params.description - Optional description of the experiment\n * @param params.includeItemResults - Whether to include individual item details (default: false)\n *\n * @returns Promise resolving to formatted string representation\n *\n * @example Output format\n * ```\n * 1. Item 1:\n * Input: What is the capital of France?\n * Expected: Paris\n * Actual: Paris\n * Scores:\n * • exact_match: 1.000\n * • similarity: 0.95\n * 💭 Very close match with expected output\n *\n * Dataset Item:\n * https://cloud.langfuse.com/project/123/datasets/456/items/789\n *\n * Trace:\n * https://cloud.langfuse.com/project/123/traces/abc123\n *\n * ──────────────────────────────────────────────────\n * 📊 Translation Quality Test - Testing model accuracy\n * 2 items\n * Evaluations:\n * • exact_match\n * • similarity\n *\n * Average Scores:\n * • exact_match: 0.850\n * • similarity: 0.923\n *\n * Run Evaluations:\n * • overall_quality: 0.887\n * 💭 Good performance with room for improvement\n *\n * 🔗 Dataset Run:\n * https://cloud.langfuse.com/project/123/datasets/456/runs/def456\n * ```\n *\n * @internal\n */\n private async prettyPrintResults<\n Input = any,\n ExpectedOutput = any,\n Metadata extends Record<string, any> = Record<string, any>,\n >(params: {\n datasetRunUrl?: string;\n itemResults: ExperimentItemResult<Input, ExpectedOutput, Metadata>[];\n originalData:\n | ExperimentItem<Input, ExpectedOutput, Metadata>[]\n | DatasetItem[];\n runEvaluations: Evaluation[];\n name: string;\n runName: string;\n description?: string;\n includeItemResults?: boolean;\n }): Promise<string> {\n const {\n itemResults,\n originalData,\n runEvaluations,\n name,\n runName,\n description,\n includeItemResults = false,\n } = params;\n\n if (itemResults.length === 0) {\n return \"No experiment results to display.\";\n }\n\n let output = \"\";\n\n // Individual results\n if (includeItemResults) {\n for (let index = 0; index < itemResults.length; index++) {\n const result = itemResults[index];\n const originalItem = originalData[index];\n\n output += `\\n${index + 1}. Item ${index + 1}:\\n`;\n\n // Input, expected, and actual on separate lines\n if (originalItem?.input !== undefined) {\n output += ` Input: ${this.formatValue(originalItem.input)}\\n`;\n }\n\n const expectedOutput =\n originalItem?.expectedOutput ?? result.expectedOutput ?? null;\n output += ` Expected: ${expectedOutput !== null ? this.formatValue(expectedOutput) : \"null\"}\\n`;\n output += ` Actual: ${this.formatValue(result.output)}\\n`;\n\n // Scores on separate lines\n if (result.evaluations.length > 0) {\n output += ` Scores:\\n`;\n result.evaluations.forEach((evaluation) => {\n const score =\n typeof evaluation.value === \"number\"\n ? evaluation.value.toFixed(3)\n : evaluation.value;\n output += ` • ${evaluation.name}: ${score}`;\n if (evaluation.comment) {\n output += `\\n 💭 ${evaluation.comment}`;\n }\n output += \"\\n\";\n });\n }\n\n // Dataset item link on separate line\n if (\n originalItem &&\n \"id\" in originalItem &&\n \"datasetId\" in originalItem\n ) {\n const projectUrl = (\n await this.langfuseClient.getTraceUrl(\"mock\")\n ).split(\"/traces\")[0];\n const datasetItemUrl = `${projectUrl}/datasets/${originalItem.datasetId}/items/${originalItem.id}`;\n output += `\\n Dataset Item:\\n ${datasetItemUrl}\\n`;\n }\n\n // Trace link on separate line\n if (result.traceId) {\n const traceUrl = await this.langfuseClient.getTraceUrl(\n result.traceId,\n );\n output += `\\n Trace:\\n ${traceUrl}\\n`;\n }\n }\n } else {\n output += `Individual Results: Hidden (${itemResults.length} items)\\n`;\n output += \"💡 Call format({ includeItemResults: true }) to view them\\n\";\n }\n\n // Experiment Overview\n const totalItems = itemResults.length;\n const evaluationNames = new Set(\n itemResults.flatMap((r) => r.evaluations.map((e) => e.name)),\n );\n\n output += `\\n${\"─\".repeat(50)}\\n`;\n output += `🧪 Experiment: ${name}`;\n output += `\\n📋 Run name: ${runName}`;\n if (description) {\n output += ` - ${description}`;\n }\n\n output += `\\n${totalItems} items`;\n\n if (evaluationNames.size > 0) {\n output += `\\nEvaluations:`;\n Array.from(evaluationNames).forEach((evalName) => {\n output += `\\n • ${evalName}`;\n });\n output += \"\\n\";\n }\n\n // Average scores in bulleted list\n if (evaluationNames.size > 0) {\n output += `\\nAverage Scores:`;\n for (const evalName of evaluationNames) {\n const scores = itemResults\n .flatMap((r) => r.evaluations)\n .filter((e) => e.name === evalName && typeof e.value === \"number\")\n .map((e) => e.value as number);\n\n if (scores.length > 0) {\n const avg = scores.reduce((a, b) => a + b, 0) / scores.length;\n output += `\\n • ${evalName}: ${avg.toFixed(3)}`;\n }\n }\n output += \"\\n\";\n }\n\n // Run evaluations\n if (runEvaluations.length > 0) {\n output += `\\nRun Evaluations:`;\n runEvaluations.forEach((runEval) => {\n const score =\n typeof runEval.value === \"number\"\n ? runEval.value.toFixed(3)\n : runEval.value;\n output += `\\n • ${runEval.name}: ${score}`;\n if (runEval.comment) {\n output += `\\n 💭 ${runEval.comment}`;\n }\n });\n output += \"\\n\";\n }\n\n if (params.datasetRunUrl) {\n output += `\\n🔗 Dataset Run:\\n ${params.datasetRunUrl}`;\n }\n\n return output;\n }\n\n /**\n * Formats a value for display in pretty-printed output.\n *\n * Handles different value types appropriately:\n * - Strings: Truncates long strings to 50 characters with \"...\"\n * - Objects/Arrays: Converts to JSON string representation\n * - Primitives: Uses toString() representation\n *\n * @param value - The value to format\n * @returns Formatted string representation suitable for display\n *\n * @internal\n */\n private formatValue(value: any): string {\n if (typeof value === \"string\") {\n return value.length > 50 ? `${value.substring(0, 47)}...` : value;\n }\n return JSON.stringify(value);\n }\n\n private isOtelRegistered(): boolean {\n let tracerProvider = trace.getTracerProvider();\n\n if (tracerProvider instanceof ProxyTracerProvider) {\n tracerProvider = tracerProvider.getDelegate();\n }\n\n return tracerProvider.constructor.name !== \"NoopTracerProvider\";\n }\n\n /**\n * Creates an experiment run name based on provided parameters.\n *\n * If runName is provided, returns it directly. Otherwise, generates\n * a name by combining the experiment name with an ISO timestamp.\n *\n * @param params - Parameters for run name creation\n * @param params.name - The experiment name\n * @param params.runName - Optional provided run name\n * @returns The final run name to use\n *\n * @internal\n */\n private createExperimentRunName(params: {\n name: string;\n runName?: string;\n }): string {\n if (params.runName) {\n return params.runName;\n }\n\n const isoTimestamp = new Date().toISOString();\n return `${params.name} - ${isoTimestamp}`;\n }\n}\n","import {\n LangfuseAPIClient,\n ParsedMediaReference,\n MediaContentType,\n getGlobalLogger,\n bytesToBase64,\n} from \"@langfuse/core\";\n\n/**\n * Parameters for resolving media references in objects.\n *\n * @template T - The type of the object being processed\n * @public\n */\nexport type LangfuseMediaResolveMediaReferencesParams<T> = {\n /** The object to process for media references */\n obj: T;\n /** The format to resolve media references to (currently only \"base64DataUri\" is supported) */\n resolveWith: \"base64DataUri\";\n /** Maximum depth to traverse when processing nested objects (default: 10) */\n maxDepth?: number;\n};\n\n/**\n * Manager for media operations in Langfuse.\n *\n * Provides methods to resolve media references in objects by replacing\n * them with actual media content (e.g., base64 data URIs).\n *\n * @public\n */\nexport class MediaManager {\n private apiClient: LangfuseAPIClient;\n\n /**\n * Creates a new MediaManager instance.\n *\n * @param params - Configuration object containing the API client\n * @internal\n */\n constructor(params: { apiClient: LangfuseAPIClient }) {\n this.apiClient = params.apiClient;\n }\n\n /**\n * Replaces media reference strings in an object with base64 data URIs.\n *\n * This method recursively traverses an object looking for media reference strings\n * in the format \"@@@langfuseMedia:...@@@\". When found, it fetches the actual media\n * content from Langfuse and replaces the reference string with a base64 data URI.\n *\n * If fetching media content fails for a reference string, a warning is logged\n * and the reference string is left unchanged.\n *\n * @param params - Configuration object\n * @returns A deep copy of the input object with media references resolved\n *\n * @example\n * ```typescript\n * const obj = {\n * image: \"@@@langfuseMedia:type=image/jpeg|id=123|source=bytes@@@\",\n * nested: {\n * pdf: \"@@@langfuseMedia:type=application/pdf|id=456|source=bytes@@@\"\n * }\n * };\n *\n * const result = await langfuse.media.resolveReferences({\n * obj,\n * resolveWith: \"base64DataUri\"\n * });\n *\n * // Result:\n * // {\n * // image: \"data:image/jpeg;base64,/9j/4AAQSkZJRg...\",\n * // nested: {\n * // pdf: \"data:application/pdf;base64,JVBERi0xLjcK...\"\n * // }\n * // }\n * ```\n */\n public async resolveReferences<T>(\n params: LangfuseMediaResolveMediaReferencesParams<T>,\n ): Promise<T> {\n const { obj, maxDepth = 10 } = params;\n\n const traverse = async <T>(obj: T, depth: number): Promise<T> => {\n if (depth > maxDepth) {\n return obj;\n }\n\n // Handle string with potential media references\n if (typeof obj === \"string\") {\n const regex = /@@@langfuseMedia:.+?@@@/g;\n const referenceStringMatches = obj.match(regex);\n if (!referenceStringMatches) {\n return obj;\n }\n\n let result = obj;\n const referenceStringToMediaContentMap = new Map<string, string>();\n\n await Promise.all(\n referenceStringMatches.map(async (referenceString) => {\n try {\n const parsedMediaReference =\n MediaManager.parseReferenceString(referenceString);\n const mediaData = await this.apiClient.media.get(\n parsedMediaReference.mediaId,\n );\n const mediaContent = await fetch(mediaData.url, {\n method: \"GET\",\n headers: {},\n });\n if (mediaContent.status !== 200) {\n throw new Error(\"Failed to fetch media content\");\n }\n\n const uint8Content = new Uint8Array(\n await mediaContent.arrayBuffer(),\n );\n\n const base64MediaContent = bytesToBase64(uint8Content);\n const base64DataUri = `data:${mediaData.contentType};base64,${base64MediaContent}`;\n\n referenceStringToMediaContentMap.set(\n referenceString,\n base64DataUri,\n );\n } catch (error) {\n getGlobalLogger().warn(\n \"Error fetching media content for reference string\",\n referenceString,\n error,\n );\n }\n }),\n );\n\n for (const [\n referenceString,\n base64MediaContent,\n ] of referenceStringToMediaContentMap.entries()) {\n result = result.replaceAll(referenceString, base64MediaContent) as T &\n string;\n }\n\n return result;\n }\n\n // Handle arrays\n if (Array.isArray(obj)) {\n return Promise.all(\n obj.map(async (item) => await traverse(item, depth + 1)),\n ) as Promise<T>;\n }\n\n // Handle objects\n if (typeof obj === \"object\" && obj !== null) {\n return Object.fromEntries(\n await Promise.all(\n Object.entries(obj).map(async ([key, value]) => [\n key,\n await traverse(value, depth + 1),\n ]),\n ),\n );\n }\n\n return obj;\n };\n\n return traverse(obj, 0);\n }\n\n /**\n * Parses a media reference string into a ParsedMediaReference.\n *\n * Example reference string:\n * \"@@@langfuseMedia:type=image/jpeg|id=some-uuid|source=base64DataUri@@@\"\n *\n * @param referenceString - The reference string to parse.\n * @returns An object with the mediaId, source, and contentType.\n *\n * @throws Error if the reference string is invalid or missing required fields.\n */\n public static parseReferenceString(\n referenceString: string,\n ): ParsedMediaReference {\n const prefix = \"@@@langfuseMedia:\";\n const suffix = \"@@@\";\n\n if (!referenceString.startsWith(prefix)) {\n throw new Error(\n \"Reference string does not start with '@@@langfuseMedia:type='\",\n );\n }\n\n if (!referenceString.endsWith(suffix)) {\n throw new Error(\"Reference string does not end with '@@@'\");\n }\n\n const content = referenceString.slice(prefix.length, -suffix.length);\n\n const pairs = content.split(\"|\");\n const parsedData: { [key: string]: string } = {};\n\n for (const pair of pairs) {\n const [key, value] = pair.split(\"=\", 2);\n parsedData[key] = value;\n }\n\n if (\n !(\"type\" in parsedData && \"id\" in parsedData && \"source\" in parsedData)\n ) {\n throw new Error(\"Missing required fields in reference string\");\n }\n\n return {\n mediaId: parsedData[\"id\"],\n source: parsedData[\"source\"],\n contentType: parsedData[\"type\"] as MediaContentType,\n };\n }\n}\n","import {\n CreatePromptRequest,\n getGlobalLogger,\n LangfuseAPIClient,\n PlaceholderMessage,\n Prompt,\n ChatMessage,\n} from \"@langfuse/core\";\n\nimport { LangfusePromptCache } from \"./promptCache.js\";\nimport {\n ChatPromptClient,\n TextPromptClient,\n LangfusePromptClient,\n} from \"./promptClients.js\";\nimport {\n ChatMessageType,\n CreateChatPromptBodyWithPlaceholders,\n} from \"./types.js\";\n\n/**\n * Manager for prompt operations in Langfuse.\n *\n * Provides methods to create, retrieve, and manage prompts with built-in caching\n * for optimal performance. Supports both text and chat prompts with variable\n * substitution and placeholder functionality.\n *\n * @public\n */\nexport class PromptManager {\n private cache: LangfusePromptCache;\n private apiClient: LangfuseAPIClient;\n\n /**\n * Creates a new PromptManager instance.\n *\n * @param params - Configuration object containing the API client\n * @internal\n */\n constructor(params: { apiClient: LangfuseAPIClient }) {\n const { apiClient } = params;\n\n this.apiClient = apiClient;\n this.cache = new LangfusePromptCache();\n }\n\n get logger() {\n return getGlobalLogger();\n }\n\n /**\n * Creates a new prompt in Langfuse.\n *\n * @param body - The prompt data to create (chat prompt)\n * @returns Promise that resolves to a ChatPromptClient\n */\n async create(\n body: CreateChatPromptBodyWithPlaceholders,\n ): Promise<ChatPromptClient>;\n\n /**\n * Creates a new prompt in Langfuse.\n *\n * @param body - The prompt data to create (text prompt)\n * @returns Promise that resolves to a TextPromptClient\n */\n async create(\n body: Omit<CreatePromptRequest.Text, \"type\"> & { type?: \"text\" },\n ): Promise<TextPromptClient>;\n\n /**\n * Creates a new prompt in Langfuse.\n *\n * @param body - The prompt data to create (chat prompt)\n * @returns Promise that resolves to a ChatPromptClient\n */\n async create(body: CreatePromptRequest.Chat): Promise<ChatPromptClient>;\n\n /**\n * Creates a new prompt in Langfuse.\n *\n * Supports both text and chat prompts. Chat prompts can include placeholders\n * for dynamic content insertion.\n *\n * @param body - The prompt data to create\n * @returns Promise that resolves to the appropriate prompt client\n *\n * @example\n * ```typescript\n * // Create a text prompt\n * const textPrompt = await langfuse.prompt.create({\n * name: \"greeting\",\n * prompt: \"Hello {{name}}!\",\n * type: \"text\"\n * });\n *\n * // Create a chat prompt\n * const chatPrompt = await langfuse.prompt.create({\n * name: \"conversation\",\n * type: \"chat\",\n * prompt: [\n * { role: \"system\", content: \"You are a helpful assistant.\" },\n * { role: \"user\", content: \"{{user_message}}\" }\n * ]\n * });\n * ```\n */\n async create(\n body:\n | CreatePromptRequest.Chat\n | (Omit<CreatePromptRequest.Text, \"type\"> & { type?: \"text\" })\n | CreateChatPromptBodyWithPlaceholders,\n ): Promise<LangfusePromptClient> {\n const requestBody: CreatePromptRequest =\n body.type === \"chat\"\n ? {\n ...body,\n prompt: body.prompt.map((item) => {\n if (\"type\" in item && item.type === ChatMessageType.Placeholder) {\n return {\n type: ChatMessageType.Placeholder,\n name: (item as PlaceholderMessage).name,\n };\n } else {\n // Handle regular ChatMessage (without type field) from API\n return { type: ChatMessageType.ChatMessage, ...item };\n }\n }),\n }\n : {\n ...body,\n type: body.type ?? \"text\",\n };\n\n const promptResponse = await this.apiClient.prompts.create(requestBody);\n\n if (promptResponse.type === \"chat\") {\n return new ChatPromptClient(promptResponse);\n }\n\n return new TextPromptClient(promptResponse);\n }\n\n /**\n * Updates the labels of an existing prompt version.\n *\n * @param params - Update parameters\n * @param params.name - Name of the prompt to update\n * @param params.version - Version number of the prompt to update\n * @param params.newLabels - New labels to apply to the prompt version\n *\n * @returns Promise that resolves to the updated prompt\n *\n * @example\n * ```typescript\n * const updatedPrompt = await langfuse.prompt.update({\n * name: \"my-prompt\",\n * version: 1,\n * newLabels: [\"production\", \"v2\"]\n * });\n * ```\n */\n async update(params: {\n name: string;\n version: number;\n newLabels: string[];\n }): Promise<Prompt> {\n const { name, version, newLabels } = params;\n\n const newPrompt = await this.apiClient.promptVersion.update(name, version, {\n newLabels,\n });\n\n this.cache.invalidate(name);\n\n return newPrompt;\n }\n\n /**\n * Retrieves a text prompt by name.\n *\n * @param name - Name of the prompt to retrieve\n * @param options - Optional retrieval configuration\n * @returns Promise that resolves to a TextPromptClient\n */\n async get(\n name: string,\n options?: {\n /** Specific version to retrieve (defaults to latest) */\n version?: number;\n /** Label to filter by */\n label?: string;\n /** Cache TTL in seconds (0 to disable caching) */\n cacheTtlSeconds?: number;\n /** Fallback text content if prompt fetch fails */\n fallback?: string;\n /** Maximum retry attempts for failed requests */\n maxRetries?: number;\n /** Specify text prompt type */\n type?: \"text\";\n /** Request timeout in milliseconds */\n fetchTimeoutMs?: number;\n },\n ): Promise<TextPromptClient>;\n\n /**\n * Retrieves a chat prompt by name.\n *\n * @param name - Name of the prompt to retrieve\n * @param options - Optional retrieval configuration\n * @returns Promise that resolves to a ChatPromptClient\n */\n async get(\n name: string,\n options?: {\n /** Specific version to retrieve (defaults to latest) */\n version?: number;\n /** Label to filter by */\n label?: string;\n /** Cache TTL in seconds (0 to disable caching) */\n cacheTtlSeconds?: number;\n /** Fallback chat messages if prompt fetch fails */\n fallback?: ChatMessage[];\n /** Maximum retry attempts for failed requests */\n maxRetries?: number;\n /** Specify chat prompt type */\n type: \"chat\";\n /** Request timeout in milliseconds */\n fetchTimeoutMs?: number;\n },\n ): Promise<ChatPromptClient>;\n\n /**\n * Retrieves a prompt by name with intelligent caching.\n *\n * This method implements sophisticated caching behavior:\n * - Fresh prompts are returned immediately from cache\n * - Expired prompts are returned from cache while being refreshed in background\n * - Cache misses trigger immediate fetch with optional fallback support\n *\n * @param name - Name of the prompt to retrieve\n * @param options - Optional retrieval configuration\n * @returns Promise that resolves to the appropriate prompt client\n *\n * @example\n * ```typescript\n * // Get latest version with caching\n * const prompt = await langfuse.prompt.get(\"my-prompt\");\n *\n * // Get specific version\n * const v2Prompt = await langfuse.prompt.get(\"my-prompt\", {\n * version: 2\n * });\n *\n * // Get with label filter\n * const prodPrompt = await langfuse.prompt.get(\"my-prompt\", {\n * label: \"production\"\n * });\n *\n * // Get with fallback\n * const promptWithFallback = await langfuse.prompt.get(\"my-prompt\", {\n * type: \"text\",\n * fallback: \"Hello {{name}}!\"\n * });\n * ```\n */\n async get(\n name: string,\n options?: {\n /** Specific version to retrieve (defaults to latest) */\n version?: number;\n /** Label to filter by */\n label?: string;\n /** Cache TTL in seconds (0 to disable caching) */\n cacheTtlSeconds?: number;\n /** Fallback content if prompt fetch fails */\n fallback?: ChatMessage[] | string;\n /** Maximum retry attempts for failed requests */\n maxRetries?: number;\n /** Prompt type (auto-detected if not specified) */\n type?: \"chat\" | \"text\";\n /** Request timeout in milliseconds */\n fetchTimeoutMs?: number;\n },\n ): Promise<LangfusePromptClient> {\n const cacheKey = this.cache.createKey({\n name,\n label: options?.label,\n });\n const cachedPrompt = this.cache.getIncludingExpired(cacheKey);\n if (!cachedPrompt || options?.cacheTtlSeconds === 0) {\n try {\n return await this.fetchPromptAndUpdateCache({\n name,\n version: options?.version,\n label: options?.label,\n cacheTtlSeconds: options?.cacheTtlSeconds,\n maxRetries: options?.maxRetries,\n fetchTimeoutMs: options?.fetchTimeoutMs,\n });\n } catch (err) {\n if (options?.fallback) {\n const sharedFallbackParams = {\n name,\n version: options?.version ?? 0,\n labels: options.label ? [options.label] : [],\n cacheTtlSeconds: options?.cacheTtlSeconds,\n config: {},\n tags: [],\n };\n\n if (options.type === \"chat\") {\n return new ChatPromptClient(\n {\n ...sharedFallbackParams,\n type: \"chat\",\n prompt: (options.fallback as ChatMessage[]).map((msg) => ({\n type: ChatMessageType.ChatMessage,\n ...msg,\n })),\n },\n true,\n );\n } else {\n return new TextPromptClient(\n {\n ...sharedFallbackParams,\n type: \"text\",\n prompt: options.fallback as string,\n },\n true,\n );\n }\n }\n\n throw err;\n }\n }\n\n if (cachedPrompt.isExpired) {\n // If the cache is not currently being refreshed, start refreshing it and register the promise in the cache\n if (!this.cache.isRefreshing(cacheKey)) {\n const refreshPromptPromise = this.fetchPromptAndUpdateCache({\n name,\n version: options?.version,\n label: options?.label,\n cacheTtlSeconds: options?.cacheTtlSeconds,\n maxRetries: options?.maxRetries,\n fetchTimeoutMs: options?.fetchTimeoutMs,\n }).catch(() => {\n this.logger.warn(\n `Failed to refresh prompt cache '${cacheKey}', stale cache will be used until next refresh succeeds.`,\n );\n });\n this.cache.addRefreshingPromise(cacheKey, refreshPromptPromise);\n }\n\n return cachedPrompt.value;\n }\n\n return cachedPrompt.value;\n }\n\n private async fetchPromptAndUpdateCache(params: {\n name: string;\n version?: number;\n cacheTtlSeconds?: number;\n label?: string;\n maxRetries?: number;\n fetchTimeoutMs?: number;\n }): Promise<LangfusePromptClient> {\n const cacheKey = this.cache.createKey(params);\n\n try {\n const {\n name,\n version,\n cacheTtlSeconds,\n label,\n maxRetries,\n fetchTimeoutMs,\n } = params;\n\n const data = await this.apiClient.prompts.get(\n name,\n {\n version,\n label,\n },\n {\n maxRetries,\n timeoutInSeconds: fetchTimeoutMs ? fetchTimeoutMs / 1_000 : undefined,\n },\n );\n\n let prompt: LangfusePromptClient;\n if (data.type === \"chat\") {\n prompt = new ChatPromptClient(data);\n } else {\n prompt = new TextPromptClient(data);\n }\n\n this.cache.set(cacheKey, prompt, cacheTtlSeconds);\n\n return prompt;\n } catch (error) {\n this.logger.error(`Error fetching prompt '${cacheKey}':`, error);\n\n throw error;\n }\n }\n}\n","import { getGlobalLogger } from \"@langfuse/core\";\n\nimport type { LangfusePromptClient } from \"./promptClients.js\";\n\nexport const DEFAULT_PROMPT_CACHE_TTL_SECONDS = 60;\n\nclass LangfusePromptCacheItem {\n private _expiry: number;\n\n constructor(\n public value: LangfusePromptClient,\n ttlSeconds: number,\n ) {\n this._expiry = Date.now() + ttlSeconds * 1000;\n }\n\n get isExpired(): boolean {\n return Date.now() > this._expiry;\n }\n}\nexport class LangfusePromptCache {\n private _cache: Map<string, LangfusePromptCacheItem>;\n private _defaultTtlSeconds: number;\n private _refreshingKeys: Map<string, Promise<void>>;\n\n constructor() {\n this._cache = new Map<string, LangfusePromptCacheItem>();\n this._defaultTtlSeconds = DEFAULT_PROMPT_CACHE_TTL_SECONDS;\n this._refreshingKeys = new Map<string, Promise<void>>();\n }\n\n public getIncludingExpired(key: string): LangfusePromptCacheItem | null {\n return this._cache.get(key) ?? null;\n }\n\n public createKey(params: {\n name: string;\n version?: number;\n label?: string;\n }): string {\n const { name, version, label } = params;\n const parts = [name];\n\n if (version !== undefined) {\n parts.push(\"version:\" + version.toString());\n } else if (label !== undefined) {\n parts.push(\"label:\" + label);\n } else {\n parts.push(\"label:production\");\n }\n\n return parts.join(\"-\");\n }\n\n public set(\n key: string,\n value: LangfusePromptClient,\n ttlSeconds?: number,\n ): void {\n const effectiveTtlSeconds = ttlSeconds ?? this._defaultTtlSeconds;\n this._cache.set(\n key,\n new LangfusePromptCacheItem(value, effectiveTtlSeconds),\n );\n }\n\n public addRefreshingPromise(key: string, promise: Promise<any>): void {\n this._refreshingKeys.set(key, promise);\n promise\n .then(() => {\n this._refreshingKeys.delete(key);\n })\n .catch(() => {\n this._refreshingKeys.delete(key);\n });\n }\n\n public isRefreshing(key: string): boolean {\n return this._refreshingKeys.has(key);\n }\n\n public invalidate(promptName: string): void {\n getGlobalLogger().debug(\n \"Invalidating cache keys for\",\n promptName,\n this._cache.keys(),\n );\n\n for (const key of this._cache.keys()) {\n if (key.startsWith(promptName)) {\n this._cache.delete(key);\n }\n }\n }\n}\n","import {\n Prompt,\n ChatMessage,\n BasePrompt,\n ChatMessageWithPlaceholders,\n} from \"@langfuse/core\";\nimport mustache from \"mustache\";\n\nimport {\n ChatMessageOrPlaceholder,\n ChatMessageType,\n LangchainMessagesPlaceholder,\n} from \"./types.js\";\n\nmustache.escape = function (text) {\n return text;\n};\n\n/**\n * Base class for all prompt clients.\n *\n * @internal\n */\nabstract class BasePromptClient {\n /** The name of the prompt */\n public readonly name: string;\n /** The version number of the prompt */\n public readonly version: number;\n /** Configuration object associated with the prompt */\n public readonly config: unknown;\n /** Labels associated with the prompt */\n public readonly labels: string[];\n /** Tags associated with the prompt */\n public readonly tags: string[];\n /** Whether this prompt client is using fallback content */\n public readonly isFallback: boolean;\n /** The type of prompt (\"text\" or \"chat\") */\n public readonly type: \"text\" | \"chat\";\n /** Optional commit message for the prompt version */\n public readonly commitMessage: string | null | undefined;\n\n /**\n * Creates a new BasePromptClient instance.\n *\n * @param prompt - The base prompt data\n * @param isFallback - Whether this is fallback content\n * @param type - The prompt type\n * @internal\n */\n constructor(prompt: BasePrompt, isFallback = false, type: \"text\" | \"chat\") {\n this.name = prompt.name;\n this.version = prompt.version;\n this.config = prompt.config;\n this.labels = prompt.labels;\n this.tags = prompt.tags;\n this.isFallback = isFallback;\n this.type = type;\n this.commitMessage = prompt.commitMessage;\n }\n\n /** Gets the raw prompt content */\n abstract get prompt(): string | ChatMessageWithPlaceholders[];\n\n /** Sets the raw prompt content */\n abstract set prompt(value: string | ChatMessageWithPlaceholders[]);\n\n /**\n * Compiles the prompt by substituting variables and resolving placeholders.\n *\n * @param variables - Key-value pairs for variable substitution\n * @param placeholders - Key-value pairs for placeholder resolution\n * @returns The compiled prompt content\n */\n abstract compile(\n variables?: Record<string, string>,\n placeholders?: Record<string, any>,\n ): string | ChatMessage[] | (ChatMessageOrPlaceholder | any)[];\n\n /**\n * Converts the prompt to a format compatible with LangChain.\n *\n * @param options - Options for conversion\n * @param options.placeholders - Placeholders to resolve during conversion\n * @returns The prompt in LangChain-compatible format\n */\n public abstract getLangchainPrompt(options?: {\n placeholders?: Record<string, any>;\n }):\n | string\n | ChatMessage[]\n | ChatMessageOrPlaceholder[]\n | (ChatMessage | LangchainMessagesPlaceholder | any)[];\n\n protected _transformToLangchainVariables(content: string): string {\n const jsonEscapedContent = this.escapeJsonForLangchain(content);\n\n return jsonEscapedContent.replace(/\\{\\{(\\w+)\\}\\}/g, \"{$1}\");\n }\n\n /**\n * Escapes every curly brace that is part of a JSON object by doubling it.\n *\n * A curly brace is considered “JSON-related” when, after skipping any immediate\n * whitespace, the next non-whitespace character is a single (') or double (\") quote.\n *\n * Braces that are already doubled (e.g. `{{variable}}` placeholders) are left untouched.\n *\n * @param text - Input string that may contain JSON snippets.\n * @returns The string with JSON-related braces doubled.\n */\n protected escapeJsonForLangchain(text: string): string {\n const out: string[] = []; // collected characters\n const stack: boolean[] = []; // true = “this { belongs to JSON”, false = normal “{”\n let i = 0;\n const n = text.length;\n\n while (i < n) {\n const ch = text[i];\n\n // ---------- opening brace ----------\n if (ch === \"{\") {\n // leave existing “{{ …” untouched\n if (i + 1 < n && text[i + 1] === \"{\") {\n out.push(\"{{\");\n i += 2;\n continue;\n }\n\n // look ahead to find the next non-space character\n let j = i + 1;\n while (j < n && /\\s/.test(text[j])) {\n j++;\n }\n\n const isJson = j < n && (text[j] === \"'\" || text[j] === '\"');\n out.push(isJson ? \"{{\" : \"{\");\n stack.push(isJson); // remember how this “{” was treated\n i += 1;\n continue;\n }\n\n // ---------- closing brace ----------\n if (ch === \"}\") {\n // leave existing “… }}” untouched\n if (i + 1 < n && text[i + 1] === \"}\") {\n out.push(\"}}\");\n i += 2;\n continue;\n }\n\n const isJson = stack.pop() ?? false;\n out.push(isJson ? \"}}\" : \"}\");\n i += 1;\n continue;\n }\n\n // ---------- any other character ----------\n out.push(ch);\n i += 1;\n }\n\n return out.join(\"\");\n }\n\n /**\n * Serializes the prompt client to JSON.\n *\n * @returns JSON string representation of the prompt\n */\n public abstract toJSON(): string;\n}\n\n/**\n * Client for working with text-based prompts.\n *\n * Provides methods to compile text prompts with variable substitution\n * and convert them to LangChain-compatible formats.\n *\n * @public\n */\nexport class TextPromptClient extends BasePromptClient {\n /** The original prompt response from the API */\n public readonly promptResponse: Prompt.Text;\n /** The text content of the prompt */\n public readonly prompt: string;\n\n /**\n * Creates a new TextPromptClient instance.\n *\n * @param prompt - The text prompt data\n * @param isFallback - Whether this is fallback content\n */\n constructor(prompt: Prompt.Text, isFallback = false) {\n super(prompt, isFallback, \"text\");\n this.promptResponse = prompt;\n this.prompt = prompt.prompt;\n }\n\n /**\n * Compiles the text prompt by substituting variables.\n *\n * Uses Mustache templating to replace {{variable}} placeholders with provided values.\n *\n * @param variables - Key-value pairs for variable substitution\n * @param _placeholders - Ignored for text prompts\n * @returns The compiled text with variables substituted\n *\n * @example\n * ```typescript\n * const prompt = await langfuse.prompt.get(\"greeting\", { type: \"text\" });\n * const compiled = prompt.compile({ name: \"Alice\" });\n * // If prompt is \"Hello {{name}}!\", result is \"Hello Alice!\"\n * ```\n */\n compile(\n variables?: Record<string, string>,\n _placeholders?: Record<string, any>,\n ): string {\n return mustache.render(this.promptResponse.prompt, variables ?? {});\n }\n\n /**\n * Converts the prompt to LangChain PromptTemplate format.\n *\n * Transforms Mustache-style {{variable}} syntax to LangChain's {variable} format.\n *\n * @param _options - Ignored for text prompts\n * @returns The prompt string compatible with LangChain PromptTemplate\n *\n * @example\n * ```typescript\n * const prompt = await langfuse.prompt.get(\"greeting\", { type: \"text\" });\n * const langchainFormat = prompt.getLangchainPrompt();\n * // Transforms \"Hello {{name}}!\" to \"Hello {name}!\"\n * ```\n */\n public getLangchainPrompt(_options?: {\n placeholders?: Record<string, any>;\n }): string {\n return this._transformToLangchainVariables(this.prompt);\n }\n\n public toJSON(): string {\n return JSON.stringify({\n name: this.name,\n prompt: this.prompt,\n version: this.version,\n isFallback: this.isFallback,\n tags: this.tags,\n labels: this.labels,\n type: this.type,\n config: this.config,\n });\n }\n}\n\n/**\n * Client for working with chat-based prompts.\n *\n * Provides methods to compile chat prompts with variable substitution and\n * placeholder resolution, and convert them to LangChain-compatible formats.\n *\n * @public\n */\nexport class ChatPromptClient extends BasePromptClient {\n /** The original prompt response from the API */\n public readonly promptResponse: Prompt.Chat;\n /** The chat messages that make up the prompt */\n public readonly prompt: ChatMessageWithPlaceholders[];\n\n /**\n * Creates a new ChatPromptClient instance.\n *\n * @param prompt - The chat prompt data\n * @param isFallback - Whether this is fallback content\n */\n constructor(prompt: Prompt.Chat, isFallback = false) {\n const normalizedPrompt = ChatPromptClient.normalizePrompt(prompt.prompt);\n const typedPrompt: Prompt.Chat = {\n ...prompt,\n prompt: normalizedPrompt,\n };\n\n super(typedPrompt, isFallback, \"chat\");\n this.promptResponse = typedPrompt;\n this.prompt = normalizedPrompt;\n }\n\n private static normalizePrompt(\n prompt: ChatMessage[] | ChatMessageWithPlaceholders[],\n ): ChatMessageWithPlaceholders[] {\n // Convert ChatMessages to ChatMessageWithPlaceholders for backward compatibility\n return prompt.map((item): ChatMessageWithPlaceholders => {\n if (\"type\" in item) {\n // Already has type field (new format)\n return item as ChatMessageWithPlaceholders;\n } else {\n // Plain ChatMessage (legacy format) - add type field\n return {\n type: ChatMessageType.ChatMessage,\n ...item,\n } as ChatMessageWithPlaceholders;\n }\n });\n }\n\n /**\n * Compiles the chat prompt by replacing placeholders and variables.\n *\n * First resolves placeholders with provided values, then applies variable substitution\n * to message content using Mustache templating. Unresolved placeholders remain\n * as placeholder objects in the output.\n *\n * @param variables - Key-value pairs for Mustache variable substitution in message content\n * @param placeholders - Key-value pairs where keys are placeholder names and values are ChatMessage arrays\n * @returns Array of ChatMessage objects and unresolved placeholder objects\n *\n * @example\n * ```typescript\n * const prompt = await langfuse.prompt.get(\"conversation\", { type: \"chat\" });\n * const compiled = prompt.compile(\n * { user_name: \"Alice\" },\n * { examples: [{ role: \"user\", content: \"Hello\" }, { role: \"assistant\", content: \"Hi!\" }] }\n * );\n * ```\n */\n compile(\n variables?: Record<string, string>,\n placeholders?: Record<string, any>,\n ): (ChatMessageOrPlaceholder | any)[] {\n const messagesWithPlaceholdersReplaced: (ChatMessageOrPlaceholder | any)[] =\n [];\n const placeholderValues = placeholders ?? {};\n\n for (const item of this.prompt) {\n if (\"type\" in item && item.type === ChatMessageType.Placeholder) {\n const placeholderValue = placeholderValues[item.name];\n if (\n Array.isArray(placeholderValue) &&\n placeholderValue.length > 0 &&\n placeholderValue.every(\n (msg) =>\n typeof msg === \"object\" && \"role\" in msg && \"content\" in msg,\n )\n ) {\n messagesWithPlaceholdersReplaced.push(\n ...(placeholderValue as ChatMessage[]),\n );\n } else if (\n Array.isArray(placeholderValue) &&\n placeholderValue.length === 0\n ) {\n // Empty array provided - skip placeholder (don't include it)\n } else if (placeholderValue !== undefined) {\n // Non-standard placeholder value format, just stringfiy\n messagesWithPlaceholdersReplaced.push(\n JSON.stringify(placeholderValue),\n );\n } else {\n // Keep unresolved placeholder in the output\n messagesWithPlaceholdersReplaced.push(\n item as { type: ChatMessageType.Placeholder } & typeof item,\n );\n }\n } else if (\n \"role\" in item &&\n \"content\" in item &&\n item.type === ChatMessageType.ChatMessage\n ) {\n messagesWithPlaceholdersReplaced.push({\n role: item.role,\n content: item.content,\n });\n }\n }\n\n return messagesWithPlaceholdersReplaced.map((item) => {\n if (\n typeof item === \"object\" &&\n item !== null &&\n \"role\" in item &&\n \"content\" in item\n ) {\n return {\n ...item,\n content: mustache.render(item.content, variables ?? {}),\n };\n } else {\n // Return placeholder or stringified value as-is\n return item;\n }\n });\n }\n\n /**\n * Converts the prompt to LangChain ChatPromptTemplate format.\n *\n * Resolves placeholders with provided values and converts unresolved ones\n * to LangChain MessagesPlaceholder objects. Transforms variables from\n * {{var}} to {var} format without rendering them.\n *\n * @param options - Configuration object\n * @param options.placeholders - Key-value pairs for placeholder resolution\n * @returns Array of ChatMessage objects and LangChain MessagesPlaceholder objects\n *\n * @example\n * ```typescript\n * const prompt = await langfuse.prompt.get(\"conversation\", { type: \"chat\" });\n * const langchainFormat = prompt.getLangchainPrompt({\n * placeholders: { examples: [{ role: \"user\", content: \"Hello\" }] }\n * });\n * ```\n */\n public getLangchainPrompt(options?: {\n placeholders?: Record<string, any>;\n }): (ChatMessage | LangchainMessagesPlaceholder | any)[] {\n const messagesWithPlaceholdersReplaced: (\n | ChatMessage\n | LangchainMessagesPlaceholder\n | any\n )[] = [];\n const placeholderValues = options?.placeholders ?? {};\n\n for (const item of this.prompt) {\n if (\"type\" in item && item.type === ChatMessageType.Placeholder) {\n const placeholderValue = placeholderValues[item.name];\n if (\n Array.isArray(placeholderValue) &&\n placeholderValue.length > 0 &&\n placeholderValue.every(\n (msg) =>\n typeof msg === \"object\" && \"role\" in msg && \"content\" in msg,\n )\n ) {\n // Complete placeholder fill-in, replace with it\n messagesWithPlaceholdersReplaced.push(\n ...(placeholderValue as ChatMessage[]).map((msg) => {\n return {\n role: msg.role,\n content: this._transformToLangchainVariables(msg.content),\n };\n }),\n );\n } else if (\n Array.isArray(placeholderValue) &&\n placeholderValue.length === 0\n ) {\n // Skip empty array placeholder\n } else if (placeholderValue !== undefined) {\n // Non-standard placeholder value, just stringify and add directly\n messagesWithPlaceholdersReplaced.push(\n JSON.stringify(placeholderValue),\n );\n } else {\n // Convert unresolved placeholder to Langchain MessagesPlaceholder format\n // see: https://js.langchain.com/docs/concepts/prompt_templates/#messagesplaceholder\n // we convert it to the format without using the class explicitly. Therefore, we\n // don't have to import langchain as a dependency.\n messagesWithPlaceholdersReplaced.push([\n \"placeholder\",\n `{${item.name}}`,\n ]);\n }\n } else if (\n \"role\" in item &&\n \"content\" in item &&\n item.type === ChatMessageType.ChatMessage\n ) {\n messagesWithPlaceholdersReplaced.push({\n role: item.role,\n content: this._transformToLangchainVariables(item.content),\n });\n }\n }\n\n return messagesWithPlaceholdersReplaced;\n }\n\n public toJSON(): string {\n return JSON.stringify({\n name: this.name,\n prompt: this.promptResponse.prompt.map((item) => {\n if (\"type\" in item && item.type === ChatMessageType.ChatMessage) {\n const { type: _, ...messageWithoutType } = item;\n return messageWithoutType;\n }\n return item;\n }),\n version: this.version,\n isFallback: this.isFallback,\n tags: this.tags,\n labels: this.labels,\n type: this.type,\n config: this.config,\n });\n }\n}\n\n/**\n * Union type representing either a text or chat prompt client.\n *\n * @public\n */\nexport type LangfusePromptClient = TextPromptClient | ChatPromptClient;\n","import {\n ChatMessage,\n PlaceholderMessage,\n ChatMessageWithPlaceholders,\n CreatePromptRequest,\n} from \"@langfuse/core\";\n\n/**\n * Enumeration of chat message types in Langfuse prompts.\n *\n * @public\n */\nexport enum ChatMessageType {\n /** Regular chat message with role and content */\n ChatMessage = \"chatmessage\",\n /** Placeholder for dynamic content insertion */\n Placeholder = \"placeholder\",\n}\n\n/**\n * Union type representing either a chat message or a placeholder.\n *\n * Used in compiled prompts where placeholders may remain unresolved.\n *\n * @public\n */\nexport type ChatMessageOrPlaceholder =\n | ChatMessage\n | ({ type: ChatMessageType.Placeholder } & PlaceholderMessage);\n\n/**\n * Represents a LangChain MessagesPlaceholder object.\n *\n * Used when converting Langfuse prompts to LangChain format,\n * unresolved placeholders become LangChain MessagesPlaceholder objects.\n *\n * @public\n */\nexport type LangchainMessagesPlaceholder = {\n /** Name of the variable that will provide the messages */\n variableName: string;\n /** Whether the placeholder is optional (defaults to false) */\n optional?: boolean;\n};\n\n/**\n * Type for creating chat prompts that support both regular messages and placeholders.\n *\n * Extends the standard chat prompt creation request to allow mixed content types.\n *\n * @public\n */\nexport type CreateChatPromptBodyWithPlaceholders = {\n /** Specifies this is a chat prompt */\n type: \"chat\";\n} & Omit<CreatePromptRequest.Chat, \"type\" | \"prompt\"> & {\n /** Array of chat messages and/or placeholders */\n prompt: (ChatMessage | ChatMessageWithPlaceholders)[];\n };\n","import {\n LangfuseAPIClient,\n IngestionEvent,\n getEnv,\n generateUUID,\n ScoreBody,\n getGlobalLogger,\n safeSetTimeout,\n IngestionResponse,\n} from \"@langfuse/core\";\nimport { Span, trace } from \"@opentelemetry/api\";\n\nconst MAX_QUEUE_SIZE = 100_000; // prevent memory leaks\nconst MAX_BATCH_SIZE = 100;\n\n/**\n * Manager for creating and batching score events in Langfuse.\n *\n * The ScoreManager handles automatic batching and flushing of score events\n * to optimize API usage. Scores are automatically sent when the queue reaches\n * a certain size or after a time interval.\n *\n * @public\n */\nexport class ScoreManager {\n private apiClient: LangfuseAPIClient;\n private eventQueue: IngestionEvent[] = [];\n private flushPromise: Promise<void> | null = null;\n private flushTimer: any = null;\n private flushAtCount: number;\n private flushIntervalSeconds: number;\n\n /**\n * Creates a new ScoreManager instance.\n *\n * @param params - Configuration object containing the API client\n * @internal\n */\n constructor(params: { apiClient: LangfuseAPIClient }) {\n this.apiClient = params.apiClient;\n\n const envFlushAtCount = getEnv(\"LANGFUSE_FLUSH_AT\");\n const envFlushIntervalSeconds = getEnv(\"LANGFUSE_FLUSH_INTERVAL\");\n\n this.flushAtCount = envFlushAtCount ? Number(envFlushAtCount) : 10;\n this.flushIntervalSeconds = envFlushIntervalSeconds\n ? Number(envFlushIntervalSeconds)\n : 1;\n }\n\n get logger() {\n return getGlobalLogger();\n }\n\n /**\n * Creates a new score event and adds it to the processing queue.\n *\n * Scores are queued and sent in batches for efficiency. The score will be\n * automatically sent when the queue reaches the flush threshold or after\n * the flush interval expires.\n *\n * @param data - The score data to create\n *\n * @example\n * ```typescript\n * langfuse.score.create({\n * name: \"quality\",\n * value: 0.85,\n * traceId: \"trace-123\",\n * comment: \"High quality response\"\n * });\n * ```\n */\n public create(data: ScoreBody): void {\n const scoreData: ScoreBody = {\n ...data,\n id: data.id ?? generateUUID(),\n environment: data.environment ?? getEnv(\"LANGFUSE_TRACING_ENVIRONMENT\"),\n };\n\n const scoreIngestionEvent: IngestionEvent = {\n id: generateUUID(),\n type: \"score-create\",\n timestamp: new Date().toISOString(),\n body: scoreData,\n };\n\n if (this.eventQueue.length >= MAX_QUEUE_SIZE) {\n this.logger.error(\n `Score queue is at max size ${MAX_QUEUE_SIZE}. Dropping score.`,\n );\n return;\n }\n\n this.eventQueue.push(scoreIngestionEvent);\n this.logger.debug(\n \"Added score event to queue:\\n\",\n JSON.stringify(scoreIngestionEvent, null, 2),\n );\n\n if (this.eventQueue.length >= this.flushAtCount) {\n this.flushPromise = this.flush();\n } else if (!this.flushTimer) {\n this.flushTimer = safeSetTimeout(() => {\n this.flushPromise = this.flush();\n }, this.flushIntervalSeconds * 1_000);\n }\n }\n\n /**\n * Creates a score for a specific observation using its OpenTelemetry span.\n *\n * This method automatically extracts the trace ID and observation ID from\n * the provided span context.\n *\n * @param observation - Object containing the OpenTelemetry span\n * @param data - Score data (traceId and observationId will be auto-populated)\n *\n * @example\n * ```typescript\n * import { startSpan } from '@langfuse/tracing';\n *\n * const span = startSpan({ name: \"my-operation\" });\n * langfuse.score.observation(\n * { otelSpan: span },\n * { name: \"accuracy\", value: 0.92 }\n * );\n * ```\n */\n public observation(\n observation: { otelSpan: Span },\n data: Omit<\n ScoreBody,\n \"traceId\" | \"sessionId\" | \"observationId\" | \"datasetRunId\"\n >,\n ) {\n const { spanId, traceId } = observation.otelSpan.spanContext();\n\n this.create({\n ...data,\n traceId,\n observationId: spanId,\n });\n }\n\n /**\n * Creates a score for a trace using an OpenTelemetry span.\n *\n * This method automatically extracts the trace ID from the provided\n * span context and creates a trace-level score.\n *\n * @param observation - Object containing the OpenTelemetry span\n * @param data - Score data (traceId will be auto-populated)\n *\n * @example\n * ```typescript\n * import { startSpan } from '@langfuse/tracing';\n *\n * const span = startSpan({ name: \"my-operation\" });\n * langfuse.score.trace(\n * { otelSpan: span },\n * { name: \"overall_quality\", value: 0.88 }\n * );\n * ```\n */\n public trace(\n observation: { otelSpan: Span },\n data: Omit<\n ScoreBody,\n \"traceId\" | \"sessionId\" | \"observationId\" | \"datasetRunId\"\n >,\n ) {\n const { traceId } = observation.otelSpan.spanContext();\n\n this.create({\n ...data,\n traceId,\n });\n }\n\n /**\n * Creates a score for the currently active observation.\n *\n * This method automatically detects the active OpenTelemetry span and\n * creates an observation-level score. If no active span is found,\n * a warning is logged and the operation is skipped.\n *\n * @param data - Score data (traceId and observationId will be auto-populated)\n *\n * @example\n * ```typescript\n * import { startActiveSpan } from '@langfuse/tracing';\n *\n * startActiveSpan({ name: \"my-operation\" }, (span) => {\n * // Inside the active span\n * langfuse.score.activeObservation({\n * name: \"relevance\",\n * value: 0.95\n * });\n * });\n * ```\n */\n public activeObservation(\n data: Omit<\n ScoreBody,\n \"traceId\" | \"sessionId\" | \"observationId\" | \"datasetRunId\"\n >,\n ) {\n const currentOtelSpan = trace.getActiveSpan();\n if (!currentOtelSpan) {\n this.logger.warn(\"No active span in context to score.\");\n\n return;\n }\n\n const { spanId, traceId } = currentOtelSpan.spanContext();\n\n this.create({\n ...data,\n traceId,\n observationId: spanId,\n });\n }\n\n /**\n * Creates a score for the currently active trace.\n *\n * This method automatically detects the active OpenTelemetry span and\n * creates a trace-level score. If no active span is found,\n * a warning is logged and the operation is skipped.\n *\n * @param data - Score data (traceId will be auto-populated)\n *\n * @example\n * ```typescript\n * import { startActiveSpan } from '@langfuse/tracing';\n *\n * startActiveSpan({ name: \"my-operation\" }, (span) => {\n * // Inside the active span\n * langfuse.score.activeTrace({\n * name: \"user_satisfaction\",\n * value: 4,\n * comment: \"User rated 4 out of 5 stars\"\n * });\n * });\n * ```\n */\n public activeTrace(\n data: Omit<\n ScoreBody,\n \"traceId\" | \"sessionId\" | \"observationId\" | \"datasetRunId\"\n >,\n ) {\n const currentOtelSpan = trace.getActiveSpan();\n if (!currentOtelSpan) {\n this.logger.warn(\"No active span in context to score trace.\");\n\n return;\n }\n\n const { traceId } = currentOtelSpan.spanContext();\n\n this.create({\n ...data,\n traceId,\n });\n }\n\n private async handleFlush() {\n try {\n if (this.flushTimer) {\n clearTimeout(this.flushTimer);\n this.flushTimer = null;\n }\n\n const promises: Promise<IngestionResponse | void>[] = [];\n\n while (this.eventQueue.length > 0) {\n const batch = this.eventQueue.splice(0, MAX_BATCH_SIZE);\n\n promises.push(\n this.apiClient.ingestion\n .batch({ batch })\n .then((res) => {\n if (res.errors?.length > 0) {\n this.logger.error(\"Error ingesting scores:\", res.errors);\n }\n })\n .catch((err) => {\n this.logger.error(\"Failed to export score batch:\", err);\n }),\n );\n }\n\n await Promise.all(promises);\n } catch (err) {\n this.logger.error(\"Error flushing Score Manager: \", err);\n } finally {\n this.flushPromise = null;\n }\n }\n\n /**\n * Flushes all pending score events to the Langfuse API.\n *\n * This method ensures all queued scores are sent immediately rather than\n * waiting for the automatic flush interval or batch size threshold.\n *\n * @returns Promise that resolves when all pending scores have been sent\n *\n * @example\n * ```typescript\n * langfuse.score.create({ name: \"quality\", value: 0.8 });\n * await langfuse.score.flush(); // Ensures the score is sent immediately\n * ```\n */\n public async flush() {\n return this.flushPromise ?? this.handleFlush();\n }\n\n /**\n * Gracefully shuts down the score manager by flushing all pending scores.\n *\n * This method should be called before your application exits to ensure\n * all score data is sent to Langfuse.\n *\n * @returns Promise that resolves when shutdown is complete\n *\n * @example\n * ```typescript\n * // Before application exit\n * await langfuse.score.shutdown();\n * ```\n */\n public async shutdown() {\n await this.flush();\n }\n}\n","import { Evaluator } from \"./types.js\";\n\n/**\n * Converts an AutoEvals evaluator to a Langfuse-compatible evaluator function.\n *\n * This adapter function bridges the gap between AutoEvals library evaluators\n * and Langfuse experiment evaluators, handling parameter mapping and result\n * formatting automatically.\n *\n * AutoEvals evaluators expect `input`, `output`, and `expected` parameters,\n * while Langfuse evaluators use `input`, `output`, and `expectedOutput`.\n * This function handles the parameter name mapping.\n *\n * @template E - Type of the AutoEvals evaluator function\n * @param autoevalEvaluator - The AutoEvals evaluator function to convert\n * @param params - Optional additional parameters to pass to the AutoEvals evaluator\n * @returns A Langfuse-compatible evaluator function\n *\n * @example Basic usage with AutoEvals\n * ```typescript\n * import { Factuality, Levenshtein } from 'autoevals';\n * import { createEvaluatorFromAutoevals } from '@langfuse/client';\n *\n * const factualityEvaluator = createEvaluatorFromAutoevals(Factuality);\n * const levenshteinEvaluator = createEvaluatorFromAutoevals(Levenshtein);\n *\n * await langfuse.experiment.run({\n * name: \"AutoEvals Integration Test\",\n * data: myDataset,\n * task: myTask,\n * evaluators: [factualityEvaluator, levenshteinEvaluator]\n * });\n * ```\n *\n * @example Using with additional parameters\n * ```typescript\n * import { Factuality } from 'autoevals';\n *\n * const factualityEvaluator = createEvaluatorFromAutoevals(\n * Factuality,\n * { model: 'gpt-4o' } // Additional params for AutoEvals\n * );\n *\n * await langfuse.experiment.run({\n * name: \"Factuality Test\",\n * data: myDataset,\n * task: myTask,\n * evaluators: [factualityEvaluator]\n * });\n * ```\n *\n * @see {@link https://github.com/braintrustdata/autoevals} AutoEvals library documentation\n * @see {@link Evaluator} for Langfuse evaluator specifications\n *\n * @public\n * @since 4.0.0\n */\nexport function createEvaluatorFromAutoevals<E extends CallableFunction>(\n autoevalEvaluator: E,\n params?: Params<E>,\n): Evaluator {\n const langfuseEvaluator: Evaluator = async (langfuseEvaluatorParams) => {\n const score = await autoevalEvaluator({\n ...(params ?? {}),\n input: langfuseEvaluatorParams.input,\n output: langfuseEvaluatorParams.output,\n expected: langfuseEvaluatorParams.expectedOutput,\n });\n\n return {\n name: score.name,\n value: score.score ?? 0,\n metadata: score.metadata,\n };\n };\n\n return langfuseEvaluator;\n}\n\n/**\n * Utility type to extract parameter types from AutoEvals evaluator functions.\n *\n * This type helper extracts the parameter type from an AutoEvals evaluator\n * and omits the standard parameters (input, output, expected) that are\n * handled by the adapter, leaving only the additional configuration parameters.\n *\n * @template E - The AutoEvals evaluator function type\n * @internal\n */\ntype Params<E> = Parameters<\n E extends (...args: any[]) => any ? E : never\n>[0] extends infer P\n ? Omit<P, \"input\" | \"output\" | \"expected\">\n : never;\n"],"mappings":"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","names":["LangfuseAPIClient","getGlobalLogger","getEnv","traceId","observationId","datasetRunId","output","params","getGlobalLogger","obj","getGlobalLogger","getGlobalLogger","ChatMessageType","getGlobalLogger","getGlobalLogger","trace","getGlobalLogger","getEnv","LangfuseAPIClient"]}
|