@lmnr-ai/lmnr 0.8.21 → 0.8.22
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/cli/worker/index.cjs +1 -1
- package/dist/cli/worker/index.mjs +1 -1
- package/dist/cli.cjs +1 -1
- package/dist/cli.mjs +1 -1
- package/dist/{decorators-CaTC0lJq.cjs → decorators-CKzMiD2H.cjs} +8 -2
- package/dist/{decorators-CaTC0lJq.cjs.map → decorators-CKzMiD2H.cjs.map} +1 -1
- package/dist/{decorators-BDFgXfgI.mjs → decorators-DrWG-1Po.mjs} +3 -3
- package/dist/{decorators-BDFgXfgI.mjs.map → decorators-DrWG-1Po.mjs.map} +1 -1
- package/dist/{dist-B5CmOQ4s.mjs → dist-BXwORFgR.mjs} +3 -3
- package/dist/{dist-B5CmOQ4s.mjs.map → dist-BXwORFgR.mjs.map} +1 -1
- package/dist/{dist-B5cKzRzq.cjs → dist-CzSNkDiM.cjs} +3 -3
- package/dist/{dist-B5cKzRzq.cjs.map → dist-CzSNkDiM.cjs.map} +1 -1
- package/dist/index.cjs +29 -18
- package/dist/index.cjs.map +1 -1
- package/dist/index.mjs +29 -18
- package/dist/index.mjs.map +1 -1
- package/package.json +3 -3
package/dist/index.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.mjs","names":["logger","SDK_VERSION","contextApi"],"sources":["../src/datasets.ts","../src/evaluations.ts","../src/opentelemetry-lib/instrumentation/aisdk/base-language-model.ts","../src/opentelemetry-lib/instrumentation/aisdk/v2.ts","../src/opentelemetry-lib/instrumentation/aisdk/v3.ts","../src/opentelemetry-lib/instrumentation/aisdk/index.ts","../src/opentelemetry-lib/instrumentation/mastra/types.ts","../src/opentelemetry-lib/instrumentation/mastra/utils.ts","../src/opentelemetry-lib/instrumentation/mastra/exporter.ts"],"sourcesContent":["import { LaminarClient } from '@lmnr-ai/client';\nimport { type StringUUID } from '@lmnr-ai/types';\n\nimport { Datapoint } from './evaluations';\n\nconst DEFAULT_FETCH_SIZE = 25;\n\nexport abstract class EvaluationDataset<D, T> {\n public async slice(start: number, end: number): Promise<Datapoint<D, T>[]> {\n const result = [];\n for (let i = Math.max(start, 0); i < Math.min(end, await this.size()); i++) {\n result.push(await this.get(i));\n }\n return result;\n }\n public abstract size(): Promise<number> | number;\n public abstract get(index: number): Promise<Datapoint<D, T>> | Datapoint<D, T>;\n}\n\nexport class LaminarDataset<D, T> extends EvaluationDataset<D, T> {\n private fetchedItems: Datapoint<D, T>[] = [];\n private len: number | null = null;\n private offset: number = 0;\n private fetchSize: number;\n private client: LaminarClient | undefined = undefined;\n\n public name: string | undefined;\n public id?: StringUUID;\n\n constructor(name?: string, options?: { id?: StringUUID; fetchSize?: number }) {\n super();\n if (!name && !options?.id) {\n throw new Error('Either name or id must be provided');\n }\n if (name && options?.id) {\n throw new Error('Only one of name or id must be provided');\n }\n this.name = name;\n this.id = options?.id;\n this.fetchSize = options?.fetchSize || DEFAULT_FETCH_SIZE;\n }\n\n public setClient(client: LaminarClient) {\n this.client = client;\n }\n\n private async fetchBatch() {\n if (!this.client) {\n throw new Error('Client not set');\n }\n const identifier = this.id ? { id: this.id } : { name: this.name! };\n const resp = await this.client.datasets.pull<D, T>({\n ...identifier,\n offset: this.offset,\n limit: this.fetchSize,\n });\n this.fetchedItems = this.fetchedItems.concat(resp.items);\n this.offset = this.fetchedItems.length;\n if (this.len === null) {\n this.len = resp.totalCount;\n }\n }\n\n public async size(): Promise<number> {\n if (this.len === null) {\n await this.fetchBatch();\n }\n return this.len!;\n }\n\n public async get(index: number): Promise<Datapoint<D, T>> {\n if (index >= this.fetchedItems.length) {\n await this.fetchBatch();\n }\n return this.fetchedItems[index];\n }\n\n /**\n * Push data from files to this dataset.\n *\n * @param {string | string[]} paths - Path(s) to files or directories containing data\n * @param {boolean} recursive - Whether to recursively read files in directories\n */\n public async push(paths: string | string[], recursive: boolean = false): Promise<void> {\n if (!this.client) {\n throw new Error('Client not set');\n }\n\n // Dynamic import to avoid circular dependency\n const { loadFromPaths } = await import('./cli/file-utils');\n\n const pathArray = Array.isArray(paths) ? paths : [paths];\n const data = await loadFromPaths<D, T>(pathArray, recursive);\n\n if (data.length === 0) {\n console.warn('No data to push. Skipping');\n return;\n }\n\n const identifier = this.id ? { id: this.id } : { name: this.name! };\n await this.client.datasets.push({\n points: data,\n ...identifier,\n });\n\n console.log(`Successfully pushed ${data.length} datapoints to dataset`);\n }\n}\n","import { LaminarClient } from \"@lmnr-ai/client\";\nimport { EvaluationDatapoint } from \"@lmnr-ai/types\";\nimport { trace } from \"@opentelemetry/api\";\nimport * as cliProgress from \"cli-progress\";\n\nimport { EvaluationDataset, LaminarDataset } from \"./datasets\";\nimport { observe } from \"./decorators\";\nimport { Laminar } from \"./laminar\";\nimport { InitializeOptions } from \"./opentelemetry-lib/interfaces\";\nimport {\n HUMAN_EVALUATOR_OPTIONS,\n SPAN_TYPE,\n} from \"./opentelemetry-lib/tracing/attributes\";\nimport { LaminarContextManager } from \"./opentelemetry-lib/tracing/context\";\nimport {\n getFrontendUrl,\n initializeLogger,\n loadEnv,\n newUUID,\n otelSpanIdToUUID,\n otelTraceIdToUUID,\n Semaphore,\n StringUUID,\n} from \"./utils\";\n\nloadEnv();\n\nconst DEFAULT_CONCURRENCY = 5;\nconst MAX_EXPORT_BATCH_SIZE = 64;\n\ndeclare global {\n var _evaluations: Evaluation<any, any, any>[] | undefined;\n // If true, then we need to set the evaluation globally without running it\n var _set_global_evaluation: boolean;\n}\n\nconst logger = initializeLogger();\n\nconst getEvaluationUrl = (\n projectId: string,\n evaluationId: string,\n baseUrl?: string,\n frontendPort?: number,\n): string => {\n const url = getFrontendUrl(baseUrl, frontendPort);\n return `${url}/project/${projectId}/evaluations/${evaluationId}`;\n};\n\nconst getAverageScores = <D, T, O>(\n results: EvaluationDatapoint<D, T, O>[],\n): Record<string, number> => {\n const perScoreValues: Record<string, number[]> = {};\n for (const result of results) {\n for (const key in result.scores) {\n const score = result.scores[key];\n if (perScoreValues[key] && score !== null) {\n perScoreValues[key].push(score);\n } else {\n perScoreValues[key] = score !== null ? [score] : [];\n }\n }\n }\n\n const averageScores: Record<string, number> = {};\n for (const key in perScoreValues) {\n averageScores[key] =\n perScoreValues[key].reduce((a, b) => a + b, 0) /\n perScoreValues[key].length;\n }\n\n return averageScores;\n};\n\n/**\n * Configuration for the Evaluator\n */\ninterface EvaluationConfig {\n /**\n * The number of data points to evaluate in parallel at a time. Defaults to 5.\n */\n concurrencyLimit?: number;\n /**\n * The project API key to use for the evaluation. If not provided,\n * the API key from the environment variable `LMNR_PROJECT_API_KEY` will be used.\n */\n projectApiKey?: string;\n /**\n * The base URL of the Laminar API. If not provided, the default is\n * `https://api.lmnr.ai`. Useful with self-hosted Laminar instances.\n * Do NOT include the port in the URL, use `httpPort` and `grpcPort` instead.\n */\n baseUrl?: string;\n /**\n * The base HTTP URL of the Laminar API. If not provided, the default is\n * `baseUrl`. Only use this if you want to proxy HTTP requests through a different host.\n */\n baseHttpUrl?: string;\n /**\n * The HTTP port of the Laminar API. If not provided, the default is 443.\n */\n httpPort?: number;\n /**\n * The gRPC port of the Laminar API. If not provided, the default is 8443.\n */\n grpcPort?: number;\n /**\n * Object with modules to instrument. If not provided, all\n * available modules are instrumented.\n * See {@link https://laminar.sh/docs/sdk/typescript/instrumentation#instrumentmodules}\n */\n instrumentModules?: InitializeOptions[\"instrumentModules\"];\n /**\n * If true, then the spans will not be batched.\n */\n traceDisableBatch?: boolean;\n /**\n * Timeout for trace export. Defaults to 30_000 (30 seconds), which is over\n * the default OTLP exporter timeout of 10_000 (10 seconds).\n */\n traceExportTimeoutMillis?: number;\n /**\n * Defines default log level for SDK and all instrumentations.\n */\n logLevel?: \"debug\" | \"info\" | \"warn\" | \"error\";\n\n /**\n * Maximum number of spans to export at a time. Defaults to 64.\n */\n traceExportBatchSize?: number;\n /**\n * The port for the Laminar , when running self-hosted. If not provided, the default is 5667.\n */\n frontendPort?: number;\n}\n\n/**\n * Datapoint is a single data point in the evaluation. `D` is the type of the input data,\n * `T` is the type of the target data.\n */\nexport type Datapoint<D, T> = {\n /**\n * input to the executor function. Must be json serializable. Required.\n */\n data: D;\n /**\n * input to the evaluator function (alongside the executor output).\n * Must be json serializable.\n */\n target?: T;\n /**\n * metadata to the evaluator function. Must be json serializable.\n */\n metadata?: Record<string, any>;\n /**\n * Optional ID of the datapoint (from dataset)\n */\n id?: StringUUID;\n /**\n * Optional creation timestamp (from dataset)\n */\n createdAt?: string;\n};\n\n/**\n * HumanEvaluator is a class to register a human evaluator.\n */\nexport class HumanEvaluator {\n public options?: { value: number; label: string }[];\n\n constructor(options?: { value: number; label: string }[]) {\n this.options = options;\n }\n}\n\nexport type EvaluatorFunctionReturn = number | Record<string, number>;\n\n/**\n * EvaluatorFunction is a function that takes the output of the executor, the\n * target, and the data, and returns a score. The score can be a single number or a record\n * of string keys and number values. The latter is useful for evaluating\n * multiple criteria in one go instead of running multiple evaluators.\n */\nexport type EvaluatorFunction<O, T, D = any> = (\n output: O,\n target?: T,\n data?: D,\n ...args: any[]\n) => EvaluatorFunctionReturn | Promise<EvaluatorFunctionReturn>;\n\ninterface EvaluationConstructorProps<D, T, O> {\n /**\n * List of data points to evaluate. `data` is the input to the executor function,\n * `target` is the input to the evaluator function.\n */\n data: Datapoint<D, T>[] | EvaluationDataset<D, T>;\n /**\n * The executor function. Takes the data point + any additional arguments\n * and returns the output to evaluate.\n */\n executor: (data: D, ...args: any[]) => O | Promise<O>;\n /**\n * Evaluator functions and names. Each evaluator function takes the output of\n * the executor, the target, and the data, and returns a score. The score can be a\n * single number or a dict of string keys and number values. If the score is a\n * single number, it will be named after the evaluator function. Evaluator\n * function names must contain only letters, digits, hyphens, underscores,\n * or spaces.\n */\n evaluators: Record<string, EvaluatorFunction<O, T, D> | HumanEvaluator>;\n /**\n * Name of the evaluation. If not provided, a random name will be assigned.\n */\n name?: string;\n /**\n * Optional group id of the evaluation. Only evaluations within the same\n * group_id can be visually compared. Defaults to \"default\".\n */\n groupName?: string;\n /**\n * Optional metadata to evaluation\n */\n metadata?: Record<string, any>;\n /**\n * Optional override configurations for the evaluator.\n */\n config?: EvaluationConfig;\n}\n\ninterface EvaluationRunResult {\n averageScores: Record<string, number>;\n projectId: string;\n evaluationId: string;\n url: string;\n errorMessage?: string;\n}\n\n/**\n * Reports the whole progress to the console.\n */\nclass EvaluationReporter {\n private cliProgress: cliProgress.SingleBar = new cliProgress.SingleBar(\n {},\n cliProgress.Presets.shades_classic,\n );\n private progressCounter: number = 0;\n public baseUrl: string;\n public frontendPort?: number;\n\n constructor(baseUrl?: string, frontendPort?: number) {\n this.baseUrl = baseUrl ?? \"https://api.lmnr.ai\";\n this.frontendPort = frontendPort;\n }\n\n public start({ length }: { length: number }) {\n this.cliProgress.start(length, 0);\n }\n\n public update(batchLength: number) {\n this.progressCounter += batchLength;\n this.cliProgress.update(this.progressCounter);\n }\n\n // Call either error or stop, not both\n public stopWithError(error: Error) {\n this.cliProgress.stop();\n process.stdout.write(`\\nError: ${error.message}\\n`);\n }\n\n // Call either error or stop, not both\n public stop({\n averageScores,\n projectId,\n evaluationId,\n }: {\n averageScores: Record<string, number>;\n projectId: string;\n evaluationId: string;\n }) {\n this.cliProgress.stop();\n const url = getEvaluationUrl(\n projectId,\n evaluationId,\n this.baseUrl,\n this.frontendPort,\n );\n process.stdout.write(\"\\n\");\n process.stdout.write(\"\\nAverage scores:\\n\");\n for (const key in averageScores) {\n process.stdout.write(`${key}: ${averageScores[key]}\\n`);\n }\n process.stdout.write(`\\nCheck results at ${url}\\n`);\n }\n}\n\nexport class Evaluation<D, T, O> {\n private isFinished: boolean = false;\n private progressReporter: EvaluationReporter;\n private data: Datapoint<D, T>[] | EvaluationDataset<D, T>;\n private executor: (data: D, ...args: any[]) => O | Promise<O>;\n private evaluators: Record<\n string,\n EvaluatorFunction<O, T, D> | HumanEvaluator\n >;\n private groupName?: string;\n private frontendPort?: number;\n private name?: string;\n private metadata?: Record<string, any>;\n private concurrencyLimit: number = DEFAULT_CONCURRENCY;\n private traceDisableBatch: boolean = false;\n private traceExportTimeoutMillis?: number;\n private traceExportBatchSize: number = MAX_EXPORT_BATCH_SIZE;\n private uploadPromises: Promise<any>[] = [];\n private client: LaminarClient;\n\n constructor({\n data,\n executor,\n evaluators,\n groupName,\n name,\n metadata,\n config,\n }: EvaluationConstructorProps<D, T, O>) {\n if (Object.keys(evaluators).length === 0) {\n throw new Error(\"No evaluators provided\");\n }\n\n const evaluatorNameRegex = /^[\\w\\s-]+$/;\n // Validate evaluator keys\n for (const key in evaluators) {\n if (!evaluatorNameRegex.test(key)) {\n throw new Error(\n `Invalid evaluator key: \"${key}\".` +\n \"Keys must only contain letters, digits, hyphens, underscores, or spaces.\",\n );\n }\n }\n\n this.frontendPort = config?.frontendPort;\n this.progressReporter = new EvaluationReporter(\n config?.baseUrl,\n config?.frontendPort,\n );\n this.data = data;\n this.executor = executor;\n this.evaluators = evaluators;\n this.groupName = groupName;\n this.metadata = metadata;\n this.name = name;\n\n if (config) {\n if (\n config.concurrencyLimit !== undefined &&\n config.concurrencyLimit < 1\n ) {\n logger.warn(\n `concurrencyLimit must be greater than 0. Setting to default of ${DEFAULT_CONCURRENCY}`,\n );\n this.concurrencyLimit = DEFAULT_CONCURRENCY;\n } else {\n this.concurrencyLimit = config.concurrencyLimit ?? DEFAULT_CONCURRENCY;\n }\n this.traceDisableBatch = config.traceDisableBatch ?? false;\n this.traceExportTimeoutMillis = config.traceExportTimeoutMillis;\n this.traceExportBatchSize =\n config.traceExportBatchSize ?? MAX_EXPORT_BATCH_SIZE;\n }\n\n if (Laminar.initialized()) {\n this.client = new LaminarClient({\n baseUrl: Laminar.getHttpUrl(),\n projectApiKey: Laminar.getProjectApiKey(),\n });\n if (\n config?.projectApiKey &&\n config.projectApiKey !== Laminar.getProjectApiKey()\n ) {\n logger.warn(\n \"Laminar was already initialized with a different project API key. \" +\n \"Ignoring the project API key from the evaluation config.\",\n );\n }\n return;\n }\n\n const key = config?.projectApiKey ?? process.env.LMNR_PROJECT_API_KEY;\n if (key === undefined) {\n throw new Error(\n \"Please initialize the Laminar object with your project API key \" +\n \"or set the LMNR_PROJECT_API_KEY environment variable\",\n );\n }\n\n const url =\n config?.baseUrl ?? process?.env?.LMNR_BASE_URL ?? \"https://api.lmnr.ai\";\n const httpUrl = config?.baseHttpUrl ?? url;\n const httpPort =\n config?.httpPort ??\n (httpUrl.match(/:\\d{1,5}$/g)\n ? parseInt(httpUrl.match(/:\\d{1,5}$/g)![0].slice(1))\n : 443);\n const urlWithoutSlash = httpUrl\n .replace(/\\/$/, \"\")\n .replace(/:\\d{1,5}$/g, \"\");\n const baseHttpUrl = `${urlWithoutSlash}:${httpPort}`;\n\n this.client = new LaminarClient({\n baseUrl: baseHttpUrl,\n projectApiKey: key,\n });\n\n Laminar.initialize({\n projectApiKey: config?.projectApiKey,\n baseUrl: url,\n baseHttpUrl,\n httpPort,\n grpcPort: config?.grpcPort,\n instrumentModules: config?.instrumentModules,\n disableBatch: this.traceDisableBatch,\n traceExportTimeoutMillis: this.traceExportTimeoutMillis,\n maxExportBatchSize: this.traceExportBatchSize,\n });\n }\n\n public async run(): Promise<EvaluationRunResult> {\n if (this.isFinished) {\n throw new Error(\"Evaluation is already finished\");\n }\n if (this.data instanceof LaminarDataset) {\n this.data.setClient(this.client);\n // Fetch dataset ID if not already set\n if (!this.data.id) {\n try {\n const datasets = await this.client.datasets.getDatasetByName(\n (this.data as any).name,\n );\n if (datasets.length > 0) {\n this.data.id = datasets[0].id;\n } else {\n logger.warn(`Dataset ${(this.data as any).name} not found`);\n }\n } catch (error) {\n // Backward compatibility with old Laminar API (self-hosted)\n logger.warn(\n `Error getting dataset ${this.data.name}: ` +\n `${error instanceof Error ? error.message : String(error)}`,\n );\n }\n }\n }\n\n let resultDatapoints: EvaluationDatapoint<D, T, O>[];\n try {\n const evaluation = await this.client.evals.init(\n this.name,\n this.groupName,\n this.metadata,\n );\n const url = getEvaluationUrl(\n evaluation.projectId,\n evaluation.id,\n this.progressReporter.baseUrl,\n this.frontendPort,\n );\n process.stdout.write(`\\nCheck results at ${url}\\n`);\n this.progressReporter.start({ length: await this.getLength() });\n\n resultDatapoints = await this.evaluateInBatches(evaluation.id);\n const averageScores = getAverageScores(resultDatapoints);\n if (this.uploadPromises.length > 0) {\n await Promise.all(this.uploadPromises);\n }\n this.progressReporter.stop({\n averageScores,\n projectId: evaluation.projectId,\n evaluationId: evaluation.id,\n });\n this.isFinished = true;\n\n await Laminar.shutdown();\n return {\n averageScores,\n projectId: evaluation.projectId,\n evaluationId: evaluation.id,\n url,\n };\n } catch (e) {\n this.progressReporter.stopWithError(e as Error);\n this.isFinished = true;\n return {\n averageScores: {},\n projectId: \"\",\n evaluationId: \"\",\n url: \"\",\n errorMessage: e instanceof Error ? e.message : String(e),\n };\n }\n }\n\n public async evaluateInBatches(\n evalId: StringUUID,\n ): Promise<EvaluationDatapoint<D, T, O>[]> {\n const semaphore = new Semaphore(this.concurrencyLimit);\n const tasks: Promise<any>[] = [];\n\n const evaluateTask = async (\n datapoint: Datapoint<D, T>,\n index: number,\n ): Promise<[number, EvaluationDatapoint<D, T, O>]> => {\n try {\n const result = await this.evaluateDatapoint(evalId, datapoint, index);\n this.progressReporter.update(1);\n return [index, result];\n } finally {\n semaphore.release();\n }\n };\n\n for (let i = 0; i < (await this.getLength()); i++) {\n await semaphore.acquire();\n const datapoint = Array.isArray(this.data)\n ? this.data[i]\n : await this.data.get(i);\n tasks.push(evaluateTask(datapoint, i));\n }\n const results = await Promise.all(tasks);\n\n // eslint-disable-next-line @typescript-eslint/no-unsafe-return\n return results.sort((a, b) => a[0] - b[0]).map(([, result]) => result);\n }\n\n private async evaluateDatapoint(\n evalId: StringUUID,\n datapoint: Datapoint<D, T>,\n index: number,\n ): Promise<EvaluationDatapoint<D, T, O>> {\n return observe(\n { name: \"evaluation\", traceType: \"EVALUATION\" },\n async () => {\n trace\n .getSpan(LaminarContextManager.getContext())!\n .setAttribute(SPAN_TYPE, \"EVALUATION\");\n const executorSpan = Laminar.startSpan({\n name: \"executor\",\n input: datapoint.data,\n });\n executorSpan.setAttribute(SPAN_TYPE, \"EXECUTOR\");\n const executorSpanId = otelSpanIdToUUID(\n executorSpan.spanContext().spanId,\n );\n const datapointId = newUUID();\n const partialDatapoint = {\n id: datapointId,\n data: datapoint.data,\n target: datapoint.target,\n metadata: datapoint.metadata,\n traceId: otelTraceIdToUUID(\n trace.getSpan(LaminarContextManager.getContext())!.spanContext()\n .traceId,\n ),\n executorSpanId,\n index,\n } as EvaluationDatapoint<D, T, O>;\n\n // Add dataset link if data is from LaminarDataset\n if (\n this.data instanceof LaminarDataset &&\n this.data.id &&\n datapoint.id &&\n datapoint.createdAt\n ) {\n partialDatapoint.datasetLink = {\n datasetId: this.data.id,\n datapointId: datapoint.id,\n createdAt: datapoint.createdAt,\n };\n }\n\n // first create the datapoint in the database and await\n await this.client.evals.saveDatapoints({\n evalId,\n datapoints: [partialDatapoint],\n groupName: this.groupName,\n });\n\n const output = await Laminar.withSpan(\n executorSpan,\n async () => {\n const result = await this.executor(datapoint.data);\n Laminar.setSpanOutput(result);\n return result;\n },\n true,\n );\n const target = datapoint.target;\n\n let scores: Record<string, number | null> = {};\n for (const [evaluatorName, evaluator] of Object.entries(\n this.evaluators,\n )) {\n const value = await observe(\n { name: evaluatorName },\n async (output: O, target?: T, data?: D) => {\n if (evaluator instanceof HumanEvaluator) {\n const activeSpan = trace.getSpan(\n LaminarContextManager.getContext(),\n );\n if (activeSpan) {\n activeSpan.setAttribute(SPAN_TYPE, \"HUMAN_EVALUATOR\");\n if (evaluator.options) {\n activeSpan.setAttribute(\n HUMAN_EVALUATOR_OPTIONS,\n JSON.stringify(evaluator.options),\n );\n }\n }\n return null;\n } else {\n const activeSpan = trace.getSpan(\n LaminarContextManager.getContext(),\n );\n if (activeSpan) {\n activeSpan.setAttribute(SPAN_TYPE, \"EVALUATOR\");\n }\n return await evaluator(output, target, data);\n }\n },\n output,\n datapoint.target,\n datapoint.data,\n );\n\n if (evaluator instanceof HumanEvaluator) {\n scores[evaluatorName] = null;\n continue;\n }\n\n if (typeof value === \"number\") {\n if (isNaN(value)) {\n throw new Error(`Evaluator ${evaluatorName} returned NaN`);\n }\n scores[evaluatorName] = value;\n } else if (value !== null) {\n scores = { ...scores, ...value };\n }\n }\n\n const resultDatapoint = {\n id: datapointId,\n executorOutput: output,\n data: datapoint.data,\n target,\n metadata: datapoint.metadata,\n scores,\n traceId: otelTraceIdToUUID(\n trace.getSpan(LaminarContextManager.getContext())!.spanContext()\n .traceId,\n ),\n executorSpanId,\n index,\n } as EvaluationDatapoint<D, T, O>;\n\n // Add dataset link if data is from LaminarDataset\n if (\n this.data instanceof LaminarDataset &&\n this.data.id &&\n datapoint.id &&\n datapoint.createdAt\n ) {\n resultDatapoint.datasetLink = {\n datasetId: this.data.id,\n datapointId: datapoint.id,\n createdAt: datapoint.createdAt,\n };\n }\n\n const uploadPromise = this.client.evals.saveDatapoints({\n evalId,\n datapoints: [resultDatapoint],\n groupName: this.groupName,\n });\n this.uploadPromises.push(uploadPromise);\n\n return resultDatapoint;\n },\n );\n }\n\n private async getLength(): Promise<number> {\n return this.data instanceof EvaluationDataset\n ? await this.data.size()\n : this.data.length;\n }\n\n public setFrontendPort(port: number) {\n this.frontendPort = port;\n this.progressReporter.frontendPort = port;\n }\n}\n\n/**\n * If added to the file which is called through lmnr eval command, then simply\n * registers the evaluation. Otherwise, returns a promise which resolves when\n * the evaluation is finished. If the evaluation has no async logic, then it\n * will be executed synchronously.\n *\n * @param props.data List of data points to evaluate. `data` is the input to the\n * executor function, `target` is the input to the evaluator function.\n * @param props.executor The executor function. Takes the data point + any\n * additional arguments and returns the output to evaluate.\n * @param props.evaluators Map from evaluator name to evaluator function. Each\n * evaluator function takes the output of the executor and the target data, and\n * returns.\n * @param props.name Optional name of the evaluation. Used to easily identify\n * the evaluation in the group.\n * @param props.metadata Optional metadata to evaluation\n * @param props.config Optional override configurations for the evaluator.\n */\nexport async function evaluate<D, T, O>({\n data,\n executor,\n evaluators,\n groupName,\n name,\n metadata,\n config,\n}: EvaluationConstructorProps<D, T, O>): Promise<\n EvaluationRunResult | undefined\n> {\n const evaluation = new Evaluation<D, T, O>({\n data,\n executor,\n evaluators,\n name,\n groupName,\n metadata,\n config,\n });\n if (globalThis._set_global_evaluation) {\n // TODO: if we load files concurrently, we need to use a mutex to protect\n // concurrent writes to globalThis._evaluations\n globalThis._evaluations = [...(globalThis._evaluations ?? []), evaluation];\n } else {\n return await evaluation.run();\n }\n}\n","import {\n type JSONSchema7,\n type LanguageModelV3,\n type LanguageModelV3CallOptions,\n type LanguageModelV3Content,\n type LanguageModelV3FinishReason,\n type LanguageModelV3FunctionTool,\n type LanguageModelV3Message,\n type LanguageModelV3ProviderTool,\n type LanguageModelV3ResponseMetadata,\n type LanguageModelV3Usage,\n type SharedV3Headers,\n type SharedV3ProviderMetadata,\n type SharedV3Warning,\n} from \"@ai-sdk/provider\";\nimport {\n type LanguageModelV2,\n type LanguageModelV2CallOptions,\n type LanguageModelV2CallWarning,\n type LanguageModelV2Content,\n type LanguageModelV2FinishReason,\n type LanguageModelV2FunctionTool,\n type LanguageModelV2Message,\n type LanguageModelV2ProviderDefinedTool,\n type LanguageModelV2ResponseMetadata,\n type LanguageModelV2Usage,\n type SharedV2Headers,\n type SharedV2ProviderMetadata,\n} from \"@ai-sdk/provider-v2\";\nimport type {\n CacheServerResponse,\n LanguageModelTextBlock,\n LanguageModelToolDefinitionOverride,\n} from '@lmnr-ai/types';\n\nimport { Laminar } from \"../../../laminar\";\nimport { stringifyPromptForTelemetry } from \"./utils\";\n\n/**\n * Base class for Laminar language model wrappers.\n * Implements shared caching and override logic for both V2 and V3 specifications.\n * Uses method overloads for type safety across versions.\n */\nexport abstract class BaseLaminarLanguageModel {\n protected readonly innerLanguageModel: LanguageModelV2 | LanguageModelV3;\n readonly provider: string;\n readonly modelId: string;\n readonly supportedUrls: PromiseLike<Record<string, RegExp[]>> | Record<string, RegExp[]>;\n\n private pathToCount: Record<string, number> = {};\n private pathToCurrentIndex: Record<string, number> = {};\n private overrides: Record<string, {\n system?: string | Array<{ type: 'text'; text: string }>;\n tools?: Array<{ name: string; description?: string; parameters?: Record<string, any> }>;\n }> = {};\n private initialized: boolean = false;\n\n constructor(languageModel: LanguageModelV2 | LanguageModelV3) {\n this.innerLanguageModel = languageModel;\n this.provider = languageModel.provider;\n this.modelId = languageModel.modelId;\n this.supportedUrls = languageModel.supportedUrls;\n }\n\n /**\n * Creates a version-specific usage object.\n * V2 and V3 have different usage structures, so this must be implemented by subclasses.\n */\n protected abstract createUsageObject(): LanguageModelV2Usage | LanguageModelV3Usage;\n\n /**\n * Creates a version-specific stream from cached response data.\n * V2 and V3 have different stream part types, so this must be implemented by subclasses.\n */\n protected abstract createStreamFromCachedResponse(\n content: Array<LanguageModelV2Content | LanguageModelV3Content>,\n finishReason: LanguageModelV2FinishReason | LanguageModelV3FinishReason,\n usage: LanguageModelV2Usage | LanguageModelV3Usage\n ): ReadableStream<any>;\n\n /**\n * Main generation method with caching support for V2\n */\n protected doGenerateWithCaching(\n options: LanguageModelV2CallOptions,\n doGenerateFn: (opts: LanguageModelV2CallOptions) => PromiseLike<{\n content: Array<LanguageModelV2Content>;\n finishReason: LanguageModelV2FinishReason;\n usage: LanguageModelV2Usage;\n providerMetadata?: SharedV2ProviderMetadata;\n request?: { body?: unknown };\n response?: LanguageModelV2ResponseMetadata & { headers?: SharedV2Headers; body?: unknown };\n warnings: Array<LanguageModelV2CallWarning>;\n }>\n ): PromiseLike<{\n content: Array<LanguageModelV2Content>;\n finishReason: LanguageModelV2FinishReason;\n usage: LanguageModelV2Usage;\n providerMetadata?: SharedV2ProviderMetadata;\n request?: { body?: unknown };\n response?: LanguageModelV2ResponseMetadata & { headers?: SharedV2Headers; body?: unknown };\n warnings: Array<LanguageModelV2CallWarning>;\n }>;\n\n /**\n * Main generation method with caching support for V3\n */\n protected doGenerateWithCaching(\n options: LanguageModelV3CallOptions,\n doGenerateFn: (opts: LanguageModelV3CallOptions) => PromiseLike<{\n content: Array<LanguageModelV3Content>;\n finishReason: LanguageModelV3FinishReason;\n usage: LanguageModelV3Usage;\n providerMetadata?: SharedV3ProviderMetadata;\n request?: { body?: unknown };\n response?: LanguageModelV3ResponseMetadata & { headers?: SharedV3Headers; body?: unknown };\n warnings: Array<SharedV3Warning>;\n }>\n ): PromiseLike<{\n content: Array<LanguageModelV3Content>;\n finishReason: LanguageModelV3FinishReason;\n usage: LanguageModelV3Usage;\n providerMetadata?: SharedV3ProviderMetadata;\n request?: { body?: unknown };\n response?: LanguageModelV3ResponseMetadata & { headers?: SharedV3Headers; body?: unknown };\n warnings: Array<SharedV3Warning>;\n }>;\n\n /**\n * Implementation of doGenerateWithCaching\n * Handles rollout session logic, path tracking, and cache lookups\n */\n protected doGenerateWithCaching(\n options: LanguageModelV2CallOptions | LanguageModelV3CallOptions,\n doGenerateFn: (opts: any) => PromiseLike<any>,\n ): PromiseLike<any> {\n return this.doGenerateOrStreamWithCaching(\n options,\n doGenerateFn,\n this.cachedDoGenerate.bind(this),\n );\n }\n\n /**\n * Main streaming method with caching support for V2\n */\n protected doStreamWithCaching(\n options: LanguageModelV2CallOptions,\n doStreamFn: (opts: LanguageModelV2CallOptions) => PromiseLike<{\n stream: ReadableStream<any>;\n request?: { body?: unknown };\n response?: { headers?: SharedV2Headers };\n }>\n ): PromiseLike<{\n stream: ReadableStream<any>;\n request?: { body?: unknown };\n response?: { headers?: SharedV2Headers };\n }>;\n\n /**\n * Main streaming method with caching support for V3\n */\n protected doStreamWithCaching(\n options: LanguageModelV3CallOptions,\n doStreamFn: (opts: LanguageModelV3CallOptions) => PromiseLike<{\n stream: ReadableStream<any>;\n request?: { body?: unknown };\n response?: { headers?: SharedV3Headers };\n }>\n ): PromiseLike<{\n stream: ReadableStream<any>;\n request?: { body?: unknown };\n response?: { headers?: SharedV3Headers };\n }>;\n\n /**\n * Implementation of doStreamWithCaching\n * Handles rollout session logic, path tracking, and cache lookups for streaming\n */\n protected doStreamWithCaching(\n options: LanguageModelV2CallOptions | LanguageModelV3CallOptions,\n doStreamFn: (opts: any) => PromiseLike<any>,\n ): PromiseLike<any> {\n return this.doGenerateOrStreamWithCaching(\n options,\n doStreamFn,\n this.cachedDoStream.bind(this),\n );\n }\n\n /**\n * Common implementation for both doGenerateWithCaching and doStreamWithCaching.\n * Handles rollout session logic, path tracking, cache lookups, and override application.\n */\n private doGenerateOrStreamWithCaching(\n options: LanguageModelV2CallOptions | LanguageModelV3CallOptions,\n originalFn: (opts: any) => PromiseLike<any>,\n cachedFn: (path: string, index: number) => Promise<\n {\n stream: ReadableStream<any>;\n } | {\n content: Array<LanguageModelV2Content | LanguageModelV3Content>;\n finishReason: LanguageModelV2FinishReason | LanguageModelV3FinishReason;\n usage: LanguageModelV2Usage | LanguageModelV3Usage;\n warnings: Array<LanguageModelV2CallWarning | SharedV3Warning>;\n } | undefined\n >,\n ): PromiseLike<any> {\n const sessionId = process.env.LMNR_ROLLOUT_SESSION_ID;\n if (sessionId) {\n return this.ensureInitialized().then(() => {\n const span = Laminar.getCurrentSpan();\n span?.setAttribute('lmnr.rollout.session_id', sessionId);\n const pathArray = Laminar.getLaminarSpanContext()?.spanPath;\n if (pathArray) {\n const path = pathArray.join('.');\n const currentIndex = this.pathToCurrentIndex[path] ?? 0;\n this.pathToCurrentIndex[path] = currentIndex + 1;\n\n const optionsWithOverrides = this.applyOverrides(path, options);\n // By this time, we are already inside the .doGenerate/.doStream span, and the\n // input attributes (ai.prompt.messages) and ai.prompt.tools have\n // already been set, and so we can override them here.\n span?.setAttribute(\n 'ai.prompt.messages',\n stringifyPromptForTelemetry(optionsWithOverrides.prompt),\n );\n if (optionsWithOverrides.tools && optionsWithOverrides.tools.length > 0) {\n span?.setAttribute(\n 'ai.prompt.tools',\n optionsWithOverrides.tools.map(tool => JSON.stringify(tool)),\n );\n }\n if (this.pathToCount[path] && currentIndex >= this.pathToCount[path]) {\n return originalFn(optionsWithOverrides);\n }\n return cachedFn(path, currentIndex).then(cachedResult => {\n if (cachedResult) {\n return cachedResult;\n }\n return originalFn(optionsWithOverrides);\n });\n }\n return originalFn(options);\n });\n }\n return originalFn(options);\n }\n\n /**\n * Ensures metadata is initialized by fetching (pathToCount, overrides) from cache server.\n * Only runs once per instance since each run event spawns a new worker process.\n */\n private async ensureInitialized(): Promise<void> {\n if (this.initialized) {\n return;\n }\n\n // Initialize by fetching metadata from cache server\n await this.fetchInitialMetadata();\n this.initialized = true;\n }\n\n /**\n * Fetches initial metadata (pathToCount, overrides) from cache server.\n */\n private async fetchInitialMetadata(): Promise<void> {\n const data = await this.fetchCachedSpan('', 0);\n if (data) {\n this.pathToCount = data.pathToCount;\n if (data.overrides) {\n this.overrides = data.overrides;\n }\n }\n }\n\n /**\n * Fetches and parses cached span data into content blocks, usage, and finish reason.\n * Updates pathToCount and overrides as a side effect.\n */\n private async fetchAndParseCachedSpan(\n path: string,\n index: number,\n ): Promise<undefined | {\n content: Array<LanguageModelV2Content | LanguageModelV3Content>;\n finishReason: LanguageModelV2FinishReason | LanguageModelV3FinishReason;\n usage: LanguageModelV2Usage | LanguageModelV3Usage;\n }> {\n const data = await this.fetchCachedSpan(path, index);\n if (!data) {\n return;\n }\n\n // Update pathToCount and overrides from response (dynamic)\n this.pathToCount = data.pathToCount;\n if (data.overrides) {\n this.overrides = data.overrides;\n }\n\n if (!data.span) {\n return;\n }\n\n // Parse output\n let parsedOutput: string | Record<string, any>[] = data.span.output;\n try {\n parsedOutput = JSON.parse(data.span.output);\n } catch {\n // Ignore - keep as string\n }\n\n const currentSpan = Laminar.getCurrentSpan();\n if (currentSpan) {\n currentSpan.setAttributes({\n 'lmnr.span.type': 'CACHED',\n 'lmnr.span.original_type': 'LLM',\n 'lmnr.rollout.session_id': process.env.LMNR_ROLLOUT_SESSION_ID,\n 'lmnr.rollout.path.count': index + 1,\n });\n }\n\n const content = this.convertToContentBlocks(parsedOutput);\n const usage = this.createUsageObject();\n const finishReason = data.span.attributes['ai.response.finishReason'] ?? 'stop';\n\n return { content, usage, finishReason };\n }\n\n private async cachedDoGenerate(path: string, index: number): Promise<undefined | {\n content: Array<LanguageModelV2Content | LanguageModelV3Content>;\n finishReason: LanguageModelV2FinishReason | LanguageModelV3FinishReason;\n usage: LanguageModelV2Usage | LanguageModelV3Usage;\n warnings: Array<LanguageModelV2CallWarning | SharedV3Warning>;\n }> {\n const parsed = await this.fetchAndParseCachedSpan(path, index);\n if (!parsed) {\n return;\n }\n\n return {\n ...parsed,\n warnings: [],\n };\n }\n\n private async cachedDoStream(path: string, index: number): Promise<undefined | {\n stream: ReadableStream<any>;\n }> {\n const parsed = await this.fetchAndParseCachedSpan(path, index);\n if (!parsed) {\n return;\n }\n\n // Create stream using version-specific implementation\n const stream = this.createStreamFromCachedResponse(\n parsed.content,\n parsed.finishReason,\n parsed.usage,\n );\n\n return { stream };\n }\n\n private applyOverrides(\n path: string,\n options: LanguageModelV2CallOptions | LanguageModelV3CallOptions,\n ): LanguageModelV2CallOptions | LanguageModelV3CallOptions {\n const pathOverride = this.overrides[path];\n if (!pathOverride) {\n return options;\n }\n\n const modifiedOptions = {\n ...options,\n } as LanguageModelV2CallOptions | LanguageModelV3CallOptions;\n\n // Apply system override\n const systemOverride = this.normalizeSystemOverride(pathOverride.system);\n if (systemOverride) {\n modifiedOptions.prompt =\n this.applySystemOverride(modifiedOptions.prompt, systemOverride) as any;\n }\n\n // Apply tool overrides\n if (pathOverride.tools) {\n modifiedOptions.tools =\n this.applyToolOverrides(modifiedOptions.tools, pathOverride.tools) as any;\n }\n\n return modifiedOptions;\n }\n\n /**\n * Converts output from span to content blocks compatible with both V2 and V3\n */\n private convertToContentBlocks(\n output: string | Record<string, any>[],\n ): Array<LanguageModelV3Content | LanguageModelV2Content> {\n if (typeof output === 'string') {\n return [{\n type: 'text',\n text: output,\n }];\n }\n\n const handleItem = (item: Record<string, any>): LanguageModelV3Content[] => {\n if (item.type === 'text') {\n return [{\n type: 'text',\n text: item.text ?? '',\n }];\n }\n if (['tool-call', 'tool_call'].includes(item.type)) {\n return [{\n type: 'tool-call',\n toolCallId: item.toolCallId ?? item.id,\n toolName: item.toolName ?? item.name,\n input: JSON.stringify(item.input ?? item.arguments),\n }];\n }\n if (item.type === 'reasoning') {\n return [{\n type: 'reasoning',\n text: item.text ?? '',\n }];\n }\n return [{\n type: 'text',\n text: JSON.stringify(item),\n }];\n };\n\n return output.flatMap(item => {\n if (item.role && item.content) {\n let parsedContent: Record<string, any>[] = item.content;\n try {\n parsedContent = JSON.parse(item.content);\n } catch {\n if (typeof item === 'string') {\n return [{\n type: 'text',\n text: item,\n }];\n }\n }\n return parsedContent.flatMap(handleItem);\n }\n return handleItem(item);\n });\n }\n\n /**\n * Fetches cached span data from the local rollout cache server\n */\n private async fetchCachedSpan(\n path: string,\n index: number,\n ): Promise<CacheServerResponse | undefined> {\n const serverUrl = process.env.LMNR_ROLLOUT_STATE_SERVER_ADDRESS;\n if (!serverUrl) {\n return;\n }\n\n try {\n const response = await fetch(`${serverUrl}/cached`, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify({ path, index }),\n });\n\n if (!response.ok) {\n // 404 means cache miss\n return;\n }\n\n return await response.json() as CacheServerResponse;\n } catch {\n // Network error or other issues - return undefined to fall back to original model\n return;\n }\n }\n\n /**\n * Normalizes system override to a string\n */\n private normalizeSystemOverride(\n system: string | LanguageModelTextBlock[] | undefined,\n ): string | undefined {\n if (!system) {\n return undefined;\n }\n\n if (typeof system === 'string') {\n return system;\n }\n\n // Join text blocks into a single string\n return system.map(block => block.text ?? '').join('\\n');\n }\n\n /**\n * Applies system message override to prompt\n */\n private applySystemOverride(\n prompt: Array<LanguageModelV2Message | LanguageModelV3Message>,\n systemOverride: string | undefined,\n ): Array<LanguageModelV2Message | LanguageModelV3Message> {\n if (!systemOverride) {\n return prompt;\n }\n\n // Filter out existing system messages\n const withoutSystem = prompt.filter(msg => msg.role !== 'system');\n\n // Add override as first message\n return [\n { role: 'system', content: systemOverride },\n ...withoutSystem,\n ];\n }\n\n /**\n * Applies tool overrides\n */\n private applyToolOverrides<\n F extends LanguageModelV2FunctionTool | LanguageModelV3FunctionTool,\n P extends LanguageModelV2ProviderDefinedTool | LanguageModelV3ProviderTool,\n >(\n tools: Array<F | P> | undefined,\n toolOverrides: LanguageModelToolDefinitionOverride[] | undefined,\n ): Array<F | P> | undefined {\n if (!toolOverrides || toolOverrides.length === 0) {\n return tools;\n }\n\n if (!tools || tools.length === 0) {\n // If no tools exist, create new tools from overrides that have inputSchema\n const newTools = [];\n for (const override of toolOverrides) {\n if (override.parameters) {\n newTools.push({\n type: 'function',\n name: override.name,\n description: override.description,\n inputSchema: override.parameters as JSONSchema7,\n });\n }\n }\n return newTools.length > 0 ? newTools as Array<F | P> : undefined;\n }\n\n const updatedTools = [...tools] as Array<F>;\n\n for (const override of toolOverrides) {\n const existingToolIndex = updatedTools.findIndex(\n tool => tool.type === 'function' && tool.name === override.name,\n );\n\n if (existingToolIndex !== -1) {\n const existingTool = updatedTools[existingToolIndex];\n // Merge with existing function tool (override takes priority)\n updatedTools[existingToolIndex] = {\n ...existingTool,\n description: override.description ?? existingTool.description,\n inputSchema: override.parameters\n ? (override.parameters as JSONSchema7)\n : (existingTool.inputSchema),\n };\n } else if (override.parameters) {\n // Add new tool if it has inputSchema\n updatedTools.push({\n type: 'function',\n name: override.name,\n description: override.description,\n inputSchema: override.parameters as JSONSchema7,\n } as F);\n }\n }\n\n return updatedTools;\n }\n}\n","import {\n type LanguageModelV2,\n type LanguageModelV2CallOptions,\n type LanguageModelV2CallWarning,\n type LanguageModelV2Content,\n type LanguageModelV2FinishReason,\n type LanguageModelV2ResponseMetadata,\n type LanguageModelV2StreamPart,\n type LanguageModelV2Usage,\n type SharedV2Headers,\n type SharedV2ProviderMetadata,\n} from \"@ai-sdk/provider-v2\";\n\nimport { BaseLaminarLanguageModel } from \"./base-language-model\";\n\nexport class LaminarLanguageModelV2 extends BaseLaminarLanguageModel implements LanguageModelV2 {\n readonly specificationVersion = 'v2';\n\n protected readonly innerLanguageModel: LanguageModelV2;\n\n constructor(languageModel: LanguageModelV2) {\n super(languageModel);\n this.innerLanguageModel = languageModel;\n }\n\n protected createUsageObject(): LanguageModelV2Usage {\n return {\n inputTokens: 0,\n outputTokens: 0,\n totalTokens: 0,\n };\n }\n\n doGenerate(options: LanguageModelV2CallOptions): PromiseLike<{\n content: Array<LanguageModelV2Content>;\n finishReason: LanguageModelV2FinishReason;\n usage: LanguageModelV2Usage;\n providerMetadata?: SharedV2ProviderMetadata;\n request?: {\n body?: unknown;\n };\n response?: LanguageModelV2ResponseMetadata & {\n headers?: SharedV2Headers;\n body?: unknown;\n };\n warnings: Array<LanguageModelV2CallWarning>;\n }> {\n return this.doGenerateWithCaching(\n options,\n (opts) => this.innerLanguageModel.doGenerate(opts),\n );\n }\n\n doStream(options: LanguageModelV2CallOptions): PromiseLike<{\n stream: ReadableStream<LanguageModelV2StreamPart>;\n request?: {\n body?: unknown;\n };\n response?: {\n headers?: SharedV2Headers;\n };\n }> {\n return this.doStreamWithCaching(\n options,\n (opts) => this.innerLanguageModel.doStream(opts),\n );\n }\n\n protected createStreamFromCachedResponse(\n content: Array<LanguageModelV2Content>,\n finishReason: LanguageModelV2FinishReason,\n usage: LanguageModelV2Usage,\n ): ReadableStream<LanguageModelV2StreamPart> {\n const parts: LanguageModelV2StreamPart[] = [];\n\n // Stream start\n parts.push({ type: 'stream-start', warnings: [] });\n\n // Process each content block\n let textIndex = 0;\n let toolIndex = 0;\n let reasoningIndex = 0;\n\n for (const block of content) {\n if (block.type === 'text') {\n const id = `text-${textIndex++}`;\n parts.push({ type: 'text-start', id });\n parts.push({ type: 'text-delta', id, delta: block.text });\n parts.push({ type: 'text-end', id });\n } else if (block.type === 'tool-call') {\n const id = `tool-${toolIndex++}`;\n parts.push({\n type: 'tool-input-start',\n id,\n toolName: block.toolName,\n });\n parts.push({\n type: 'tool-input-delta',\n id,\n delta: block.input,\n });\n parts.push({ type: 'tool-input-end', id });\n parts.push({\n type: 'tool-call',\n toolCallId: block.toolCallId,\n toolName: block.toolName,\n input: block.input,\n });\n } else if (block.type === 'reasoning') {\n const id = `reasoning-${reasoningIndex++}`;\n parts.push({ type: 'reasoning-start', id });\n parts.push({ type: 'reasoning-delta', id, delta: block.text });\n parts.push({ type: 'reasoning-end', id });\n }\n }\n\n // Finish event\n parts.push({ type: 'finish', usage, finishReason });\n\n // Create readable stream from the parts array\n return new ReadableStream({\n start(controller) {\n for (const part of parts) {\n controller.enqueue(part);\n }\n controller.close();\n },\n });\n }\n}\n","import {\n type LanguageModelV3,\n type LanguageModelV3CallOptions,\n type LanguageModelV3Content,\n type LanguageModelV3FinishReason,\n type LanguageModelV3ResponseMetadata,\n type LanguageModelV3StreamPart,\n type LanguageModelV3Usage,\n type SharedV3Headers,\n type SharedV3ProviderMetadata,\n type SharedV3Warning,\n} from \"@ai-sdk/provider\";\n\nimport { BaseLaminarLanguageModel } from \"./base-language-model\";\n\nexport class LaminarLanguageModelV3 extends BaseLaminarLanguageModel implements LanguageModelV3 {\n readonly specificationVersion = 'v3';\n\n protected readonly innerLanguageModel: LanguageModelV3;\n\n constructor(languageModel: LanguageModelV3) {\n super(languageModel);\n this.innerLanguageModel = languageModel;\n }\n\n protected createUsageObject(): LanguageModelV3Usage {\n return {\n inputTokens: {\n total: 0,\n noCache: 0,\n cacheRead: 0,\n cacheWrite: 0,\n },\n outputTokens: {\n total: 0,\n text: 0,\n reasoning: 0,\n },\n };\n }\n\n doGenerate(options: LanguageModelV3CallOptions): PromiseLike<{\n content: Array<LanguageModelV3Content>;\n finishReason: LanguageModelV3FinishReason;\n usage: LanguageModelV3Usage;\n providerMetadata?: SharedV3ProviderMetadata;\n request?: {\n body?: unknown;\n };\n response?: LanguageModelV3ResponseMetadata & {\n headers?: SharedV3Headers;\n body?: unknown;\n };\n warnings: Array<SharedV3Warning>;\n }> {\n return this.doGenerateWithCaching(\n options,\n (opts) => this.innerLanguageModel.doGenerate(opts),\n );\n }\n\n doStream(options: LanguageModelV3CallOptions): PromiseLike<{\n stream: ReadableStream<LanguageModelV3StreamPart>;\n request?: {\n body?: unknown;\n };\n response?: {\n headers?: SharedV3Headers;\n };\n }> {\n return this.doStreamWithCaching(\n options,\n (opts) => this.innerLanguageModel.doStream(opts),\n );\n }\n\n protected createStreamFromCachedResponse(\n content: Array<LanguageModelV3Content>,\n finishReason: LanguageModelV3FinishReason,\n usage: LanguageModelV3Usage,\n ): ReadableStream<LanguageModelV3StreamPart> {\n const parts: LanguageModelV3StreamPart[] = [];\n\n // Stream start\n parts.push({ type: 'stream-start', warnings: [] });\n\n // Process each content block\n let textIndex = 0;\n let toolIndex = 0;\n let reasoningIndex = 0;\n\n for (const block of content) {\n if (block.type === 'text') {\n const id = `text-${textIndex++}`;\n parts.push({ type: 'text-start', id });\n parts.push({ type: 'text-delta', id, delta: block.text });\n parts.push({ type: 'text-end', id });\n } else if (block.type === 'tool-call') {\n const id = `tool-${toolIndex++}`;\n parts.push({\n type: 'tool-input-start',\n id,\n toolName: block.toolName,\n });\n parts.push({\n type: 'tool-input-delta',\n id,\n delta: block.input,\n });\n parts.push({ type: 'tool-input-end', id });\n parts.push({\n type: 'tool-call',\n toolCallId: block.toolCallId,\n toolName: block.toolName,\n input: block.input,\n });\n } else if (block.type === 'reasoning') {\n const id = `reasoning-${reasoningIndex++}`;\n parts.push({ type: 'reasoning-start', id });\n parts.push({ type: 'reasoning-delta', id, delta: block.text });\n parts.push({ type: 'reasoning-end', id });\n }\n }\n\n // Finish event\n parts.push({ type: 'finish', usage, finishReason });\n\n // Create readable stream from the parts array\n return new ReadableStream({\n start(controller) {\n for (const part of parts) {\n controller.enqueue(part);\n }\n controller.close();\n },\n });\n }\n}\n","import { type LanguageModelV3 } from \"@ai-sdk/provider\";\nimport { type LanguageModelV2 } from \"@ai-sdk/provider-v2\";\nimport type * as AI from \"ai\";\n\nimport { getTracer } from \"../../tracing\";\nimport { LaminarLanguageModelV2 } from \"./v2\";\nimport { LaminarLanguageModelV3 } from \"./v3\";\n\nconst AI_FUNCTIONS = [\n 'generateText',\n 'generateObject',\n 'streamText',\n 'streamObject',\n 'embed',\n 'embedMany',\n];\n\nexport const wrapAISDK = (ai: typeof AI): typeof AI => {\n const wrapped: Record<string, any> = {};\n Object.entries(ai).forEach(([key, value]) => {\n if (typeof value === 'function' && AI_FUNCTIONS.includes(key)) {\n const originalFn = value as (...args: any[]) => any;\n wrapped[key] = (...args: any[]) => {\n if (args[0] && typeof args[0] === 'object') {\n const defaultTelemetry = {\n isEnabled: true,\n tracer: getTracer(),\n };\n args[0] = {\n ...args[0],\n experimental_telemetry: {\n ...defaultTelemetry,\n ...args[0].experimental_telemetry,\n },\n };\n }\n // eslint-disable-next-line @typescript-eslint/no-unsafe-return\n return originalFn(...args);\n };\n } else {\n wrapped[key] = value;\n }\n });\n return wrapped as typeof AI;\n};\n\nexport function wrapLanguageModel(languageModel: LanguageModelV3): LanguageModelV3;\nexport function wrapLanguageModel(languageModel: LanguageModelV2): LanguageModelV2;\nexport function wrapLanguageModel(\n languageModel: LanguageModelV2 | LanguageModelV3,\n): LanguageModelV2 | LanguageModelV3 {\n if (languageModel.specificationVersion === 'v3') {\n return new LaminarLanguageModelV3(languageModel);\n }\n return new LaminarLanguageModelV2(languageModel);\n}\n","// Narrow structural types for Mastra's observability contract. Declared\n// locally to avoid a hard runtime/peer dep on @mastra/core. Also includes\n// the slice of AI SDK message shape we serialize into `ai.prompt.messages`.\n\nexport type LaminarSpanType = \"LLM\" | \"TOOL\" | \"DEFAULT\";\n\n// Mirror of the subset of `@mastra/core`'s `SpanType` enum we care about.\n// Declared locally so the exporter has no peer dep on Mastra. Mastra may\n// emit other span types we don't branch on — `MastraExportedSpan.type` stays\n// `string` to keep forward-compat, but every comparison in the exporter\n// should use this constant so the set of consumed types is centralized.\n//\n// Defined as a `const` object (not a TS `enum`) so equality checks against\n// `MastraExportedSpan.type: string` don't trip `no-unsafe-enum-comparison`.\nexport const MastraSpanType = {\n MODEL_GENERATION: \"model_generation\",\n MODEL_STEP: \"model_step\",\n MODEL_CHUNK: \"model_chunk\",\n TOOL_CALL: \"tool_call\",\n MCP_TOOL_CALL: \"mcp_tool_call\",\n} as const;\nexport type MastraSpanType =\n (typeof MastraSpanType)[keyof typeof MastraSpanType];\n\nexport interface MastraUsageStats {\n inputTokens?: number;\n outputTokens?: number;\n inputDetails?: {\n cacheRead?: number;\n cacheWrite?: number;\n };\n outputDetails?: {\n reasoning?: number;\n };\n}\n\nexport interface MastraSpanErrorInfo {\n message: string;\n name?: string;\n stack?: string;\n details?: Record<string, unknown>;\n}\n\nexport interface MastraExportedSpan {\n id: string;\n traceId: string;\n parentSpanId?: string;\n name: string;\n type: string;\n startTime: Date;\n endTime?: Date;\n attributes?: Record<string, any>;\n metadata?: Record<string, any>;\n tags?: string[];\n input?: unknown;\n output?: unknown;\n errorInfo?: MastraSpanErrorInfo;\n isEvent: boolean;\n isRootSpan: boolean;\n}\n\nexport interface MastraTracingEvent {\n type: \"span_started\" | \"span_updated\" | \"span_ended\";\n exportedSpan: MastraExportedSpan;\n}\n\nexport interface MastraExporterOptions {\n /**\n * Flush the underlying span processor after every span end. Useful for\n * short-lived processes that exit before the batch processor would drain\n * on its own.\n */\n realtime?: boolean;\n /**\n * When true (default), reparent Mastra traces under the caller's active\n * OpenTelemetry span if one exists. This lets `observe()`-wrapped code that\n * calls a Mastra agent produce a single unified trace instead of two\n * disconnected ones (user's OTel trace + Mastra's own trace).\n *\n * Mastra does not propagate OTel context into its event bus, so without\n * this we'd emit under Mastra's self-assigned trace id with no parent.\n * Set to false to preserve Mastra's original trace id even when nested\n * inside `observe()`.\n */\n linkToActiveContext?: boolean;\n}\n\nexport interface AISdkContentPart {\n type: string;\n text?: string;\n toolCallId?: string;\n toolName?: string;\n input?: unknown;\n output?: unknown;\n}\n\nexport interface AISdkMessage {\n role: string;\n content: string | AISdkContentPart[];\n tool_call_id?: string;\n}\n","import { HrTime } from \"@opentelemetry/api\";\n\nimport { LaminarAttributes } from \"../../tracing/attributes\";\nimport {\n AISdkContentPart,\n AISdkMessage,\n LaminarSpanType,\n MastraExportedSpan,\n MastraSpanType,\n MastraUsageStats,\n} from \"./types\";\n\nexport const dateToHrTime = (date: Date): HrTime => {\n const ms = date.getTime();\n const seconds = Math.floor(ms / 1e3);\n const nanoseconds = (ms % 1e3) * 1e6;\n return [seconds, nanoseconds];\n};\n\nexport const normalizeProvider = (provider: string): string =>\n provider.split(\".\").shift()?.toLowerCase().trim() ||\n provider.toLowerCase().trim();\n\n// `model_step` is Laminar's atomic LLM call. `model_generation` wraps the\n// whole agent-to-LLM flow (which may include multiple steps + tool calls),\n// so it stays DEFAULT.\nexport const mapLaminarSpanType = (spanType: string): LaminarSpanType => {\n switch (spanType) {\n case MastraSpanType.MODEL_STEP:\n return \"LLM\";\n case MastraSpanType.TOOL_CALL:\n case MastraSpanType.MCP_TOOL_CALL:\n return \"TOOL\";\n default:\n return \"DEFAULT\";\n }\n};\n\nexport const serializeJSON = (value: unknown): string => {\n if (typeof value === \"string\") return value;\n try {\n return JSON.stringify(value);\n } catch {\n return \"[unserializable]\";\n }\n};\n\ntype AttrPrimitive = string | number | boolean;\nexport const toAttributeValue = (\n value: unknown,\n): AttrPrimitive | AttrPrimitive[] | undefined => {\n if (value === undefined || value === null) return undefined;\n if (\n typeof value === \"string\" ||\n typeof value === \"number\" ||\n typeof value === \"boolean\"\n ) {\n return value;\n }\n if (Array.isArray(value)) {\n if (value.every((v) => typeof v === \"string\")) return value;\n if (value.every((v) => typeof v === \"number\")) return value;\n if (value.every((v) => typeof v === \"boolean\")) return value;\n }\n return serializeJSON(value);\n};\n\nexport const extractMessages = (input: unknown): unknown[] | undefined => {\n if (!input) return undefined;\n if (Array.isArray(input)) return input as unknown[];\n if (typeof input === \"object\") {\n const asObj = input as Record<string, unknown>;\n if (Array.isArray(asObj.messages)) return asObj.messages as unknown[];\n if (Array.isArray(asObj.prompt)) return asObj.prompt as unknown[];\n if (Array.isArray(asObj.input)) return asObj.input as unknown[];\n }\n return undefined;\n};\n\n// `MODEL_GENERATION.input` is the messages array the user passed into\n// `agent.generate(...)`, forwarded by Mastra to the AI SDK unchanged. It's\n// already in AI SDK shape by construction — the AI SDK would have rejected\n// it before observability fired otherwise. Just coerce non-object items to\n// a string user message so `ai.prompt.messages` always serializes as a\n// well-formed message list.\nexport const normalizeInputMessages = (raw: unknown[]): AISdkMessage[] => {\n const out: AISdkMessage[] = [];\n for (const m of raw) {\n if (m == null) continue;\n if (typeof m === \"object\") {\n out.push(m as AISdkMessage);\n continue;\n }\n out.push({\n role: \"user\",\n content: typeof m === \"string\" ? m : serializeJSON(m),\n });\n }\n return out;\n};\n\n// Synthesizes the assistant turn cached on the surrounding MODEL_GENERATION\n// and replayed in the next step's `ai.prompt.messages`. Reasoning text is\n// intentionally omitted — providers don't re-send it in subsequent turns.\nexport const buildAssistantMessageFromStepOutput = (\n output: unknown,\n): AISdkMessage | null => {\n if (!output || typeof output !== \"object\") return null;\n const outObj = output as Record<string, unknown>;\n const parts: AISdkContentPart[] = [];\n if (typeof outObj.text === \"string\" && outObj.text.length > 0) {\n parts.push({ type: \"text\", text: outObj.text });\n }\n if (Array.isArray(outObj.toolCalls)) {\n for (const tc of outObj.toolCalls as Array<Record<string, unknown>>) {\n if (!tc) continue;\n parts.push({\n type: \"tool-call\",\n toolCallId: tc.toolCallId as string | undefined,\n toolName: tc.toolName as string | undefined,\n // `args` is the legacy AI SDK v4 name, `input` is v5+; keep both\n // until we drop v4 support.\n input: tc.input ?? tc.args,\n });\n }\n }\n if (parts.length === 0) return null;\n return { role: \"assistant\", content: parts };\n};\n\nexport const extractToolCallId = (\n span: MastraExportedSpan,\n): string | undefined => {\n const v = span.attributes?.toolCallId;\n return typeof v === \"string\" ? v : undefined;\n};\n\n// Mastra names tool spans like `tool: 'myToolId'` or, for MCP tools,\n// `mcp_tool: 'toolId' on 'serverName'` (see @mastra/core buildSpanName).\n// Strip that prefix + quotes (and the trailing ` on '<server>'` for MCP) so\n// both the TOOL span's `ai.toolCall.name` and the tool-result message's\n// `toolName` agree on the raw tool id.\nexport const cleanMastraToolName = (\n name: string | undefined,\n): string | undefined =>\n name?.replace(/^(?:mcp_tool|tool):\\s*'?(.*?)'?(?:\\s+on\\s+'[^']*')?$/, \"$1\");\n\n// Tool-call args are sometimes a string (already-serialized JSON), sometimes\n// an object — stringify the latter so `ai.response.toolCalls` is always a\n// JSON-string at the AI SDK boundary. Mirror `serializeJSON`'s guard so\n// unserializable inputs (BigInt, circular refs) degrade the arg payload\n// rather than bubbling up and causing the caller's outer catch to drop the\n// whole LLM span.\nexport const stringifyArgs = (raw: unknown): string | undefined => {\n if (raw === undefined) return undefined;\n if (typeof raw === \"string\") return raw;\n try {\n return JSON.stringify(raw);\n } catch {\n return \"[unserializable]\";\n }\n};\n\nexport const formatUsage = (\n usage?: MastraUsageStats,\n): Record<string, number> => {\n if (!usage) return {};\n const out: Record<string, number> = {};\n if (typeof usage.inputTokens === \"number\") {\n out[LaminarAttributes.INPUT_TOKEN_COUNT] = usage.inputTokens;\n }\n if (typeof usage.outputTokens === \"number\") {\n out[LaminarAttributes.OUTPUT_TOKEN_COUNT] = usage.outputTokens;\n }\n const total = (usage.inputTokens ?? 0) + (usage.outputTokens ?? 0);\n if (total > 0) {\n out[LaminarAttributes.TOTAL_TOKEN_COUNT] = total;\n }\n if (typeof usage.inputDetails?.cacheWrite === \"number\") {\n out[\"gen_ai.usage.cache_creation_input_tokens\"] =\n usage.inputDetails.cacheWrite;\n }\n if (typeof usage.inputDetails?.cacheRead === \"number\") {\n out[\"gen_ai.usage.cache_read_input_tokens\"] = usage.inputDetails.cacheRead;\n }\n if (typeof usage.outputDetails?.reasoning === \"number\") {\n out[\"gen_ai.usage.reasoning_tokens\"] = usage.outputDetails.reasoning;\n }\n return out;\n};\n","import {\n Context,\n context as contextApi,\n Span,\n SpanContext,\n SpanKind,\n SpanStatusCode,\n trace,\n TraceFlags,\n} from \"@opentelemetry/api\";\n\nimport { version as SDK_VERSION } from \"../../../../package.json\";\nimport {\n initializeLogger,\n normalizeOtelSpanId,\n normalizeOtelTraceId,\n otelSpanIdToUUID,\n} from \"../../../utils\";\nimport { getLangVersion } from \"../../../version\";\nimport {\n ASSOCIATION_PROPERTIES,\n LaminarAttributes,\n SESSION_ID,\n SPAN_IDS_PATH,\n SPAN_INPUT,\n SPAN_INSTRUMENTATION_SOURCE,\n SPAN_LANGUAGE_VERSION,\n SPAN_OUTPUT,\n SPAN_PATH,\n SPAN_SDK_VERSION,\n SPAN_TYPE,\n USER_ID,\n} from \"../../tracing/attributes\";\nimport { LaminarContextManager } from \"../../tracing/context\";\nimport { getSpanProcessor, getTracerProvider } from \"../../tracing/index\";\nimport {\n AISdkMessage,\n MastraExportedSpan,\n MastraExporterOptions,\n MastraSpanType,\n MastraTracingEvent,\n MastraUsageStats,\n} from \"./types\";\nimport {\n buildAssistantMessageFromStepOutput,\n cleanMastraToolName,\n dateToHrTime,\n extractMessages,\n extractToolCallId,\n formatUsage,\n mapLaminarSpanType,\n normalizeInputMessages,\n normalizeProvider,\n serializeJSON,\n stringifyArgs,\n toAttributeValue,\n} from \"./utils\";\n\nconst logger = initializeLogger();\n\ninterface TraceState {\n spanPathById: Map<string, string[]>;\n spanIdsPathById: Map<string, string[]>;\n activeSpanIds: Set<string>;\n // When the first event for this Mastra trace arrived inside an active OTel\n // span (e.g. a user's `observe()` wrapper), we rewrite outgoing spans onto\n // that OTel trace so the whole thing renders as a single trace. Stored\n // once, at first-event time, and reused for every span in the trace.\n otelTraceId?: string;\n otelRootParentSpanId?: string;\n // Outer span's SPAN_PATH / SPAN_IDS_PATH, captured from the active\n // LaminarSpan's attributes at first-event time. Prepended to every Mastra\n // span's path so Laminar's UI (which builds its hierarchy from\n // `lmnr.span.ids_path`, NOT OTel parent-span relationships) renders the\n // Mastra subtree nested under the outer `observe()` span rather than as a\n // sibling on the same trace.\n otelParentSpanPath?: string[];\n otelParentSpanIdsPath?: string[];\n}\n\ninterface ToolCallChild {\n // Tool-call identifier extracted from the tool_call span (attributes/input)\n // when available. Used to pair results with the step's declared toolCalls\n // by id — necessary when tools execute in parallel and finish out of\n // declaration order. Falls back to arrival-order pairing when absent.\n toolCallId?: string;\n toolName: string;\n input: unknown;\n output: unknown;\n}\n\ninterface GenerationState {\n // Messages as originally passed into the generation (system + user).\n baseMessages: AISdkMessage[];\n // Per-step conversation turn (assistant message + tool-result messages).\n // Assembled at step-end, keyed by stepIndex so we can replay history for\n // step N as [baseMessages, ...turns in order].\n turnsByStepIndex: Map<number, AISdkMessage[]>;\n // Ordered tool_call children per step — collected as they end (always\n // before their parent MODEL_STEP ends) so we can pair them by arrival\n // order with the step's declared `toolCalls` when the step itself ends.\n toolCallChildrenByStepIndex: Map<number, ToolCallChild[]>;\n // Accumulated reasoning text per step, built up from MODEL_CHUNK children\n // with `chunkType: \"reasoning\"`. Mastra drops reasoning from\n // `MODEL_STEP.output`/`attributes` entirely (see `#endStepSpan` in\n // `@mastra/observability/dist/index.js`), and `MODEL_GENERATION.output`\n // only carries a flat cross-step `reasoningText` string — neither preserves\n // per-step boundaries. Reasoning chunks DO, and they end before the parent\n // MODEL_STEP ends, so accumulating from them is the only way to surface\n // thinking tokens on the step's LLM span.\n reasoningTextByStepIndex: Map<number, string>;\n}\n\n/**\n * Bridges Mastra's ObservabilityExporter contract to Laminar's OTLP ingestion.\n *\n * Span-type mapping:\n * - `model_step` → LLM (atomic LLM call; rendered with message history).\n * - `tool_call`, `mcp_tool_call` → TOOL.\n * - `model_chunk` → dropped (per-delta, noise).\n * - everything else → DEFAULT.\n *\n * Tool-call reconstruction: Mastra's `extractStepInput` collapses prior tool\n * calls/results into empty user messages on MODEL_STEP inputs, so we\n * reconstruct the full conversation by accumulating (baseMessages → step0's\n * assistant message → tool-result message(s) → step1's assistant message →\n * …) using the parent MODEL_GENERATION's input and the children TOOL_CALL\n * spans paired by arrival order with each step's declared toolCalls.\n *\n * For LLM spans we emit `ai.prompt.messages` (stringified AI SDK messages\n * with tool-call / tool-result content parts) and `ai.response.text` +\n * `ai.response.toolCalls` so Laminar's backend parser threads them into\n * the LLM message history view.\n */\nexport class MastraExporter {\n public readonly name = \"laminar\";\n\n private readonly config: {\n realtime: boolean;\n linkToActiveContext: boolean;\n };\n\n private readonly traceMap: Map<string, TraceState> = new Map();\n private readonly generationStateById: Map<string, GenerationState> =\n new Map();\n private readonly generationAttrsById: Map<string, Record<string, unknown>> =\n new Map();\n private readonly generationIdByStepId: Map<string, string> = new Map();\n // Remember step index per step span so tool_call children can look up which\n // step they belong to (fan-in by arrival order).\n private readonly stepIndexBySpanId: Map<string, number> = new Map();\n // Live OTel spans created via `tracer.startSpan` at span_started time, held\n // until span_ended. Lookups by Mastra span id let us:\n // - apply attributes / status / exception on the live span at end-time;\n // - resolve a Mastra child's parent context to the *mutated* live span,\n // so the SDK threads parent linkage with our Mastra-derived ids.\n private readonly liveOtelSpanByMastraId: Map<string, Span> = new Map();\n\n private warnedNotInitialized = false;\n\n constructor(options: MastraExporterOptions = {}) {\n this.config = {\n realtime: options.realtime ?? false,\n linkToActiveContext: options.linkToActiveContext ?? true,\n };\n }\n\n // Mastra calls `init({ config: { serviceName } })` on registration. We rely\n // on Laminar's tracer provider (set by `Laminar.initialize()`) for span\n // creation and export, so this is a no-op kept only for API compatibility\n // with Mastra's exporter contract.\n // eslint-disable-next-line @typescript-eslint/no-unused-vars\n init(_options?: unknown): void {}\n\n async exportTracingEvent(event: MastraTracingEvent): Promise<void> {\n const span = event.exportedSpan;\n // Point-in-time event spans have no start/end pair and don't map cleanly\n // onto our span-tree model — drop them regardless of phase. The guard must\n // cover span_ended too: otherwise an isEvent span arriving on that phase\n // slips through to handleSpanEnded and gets exported as a real span.\n if (span.isEvent) return;\n\n // Always remember generation attributes so children can resolve them\n // regardless of event ordering. Merge (not overwrite) so a span_updated\n // event carrying only a delta (e.g. just `usage`) can't clobber keys\n // set earlier at span_started (e.g. `provider`, `model`). MODEL_STEP\n // children end before MODEL_GENERATION's span_ended restores the full\n // set, so applyLlmAttributes would otherwise read a partial snapshot.\n if (span.type === MastraSpanType.MODEL_GENERATION && span.attributes) {\n const existing = this.generationAttrsById.get(span.id);\n // Always spread into a fresh object (even on first insert): Mastra\n // owns `span.attributes` and could reuse or mutate it between events,\n // which would silently corrupt the stored snapshot children read at\n // `applyLlmAttributes` time.\n this.generationAttrsById.set(\n span.id,\n existing\n ? { ...existing, ...span.attributes }\n : { ...span.attributes },\n );\n }\n\n if (event.type === \"span_started\") {\n this.handleSpanStarted(span);\n return;\n }\n if (event.type !== \"span_ended\") return;\n await this.handleSpanEnded(span);\n }\n\n async onTracingEvent(event: MastraTracingEvent): Promise<void> {\n await this.exportTracingEvent(event);\n }\n\n async flush(): Promise<void> {\n // Flush Laminar's span processor — the only pipeline our spans flow\n // through. No-op when Laminar isn't initialized.\n const processor = getSpanProcessor();\n if (!processor) return;\n try {\n await processor.forceFlush();\n } catch (err) {\n logger.error(\n `[MastraExporter] forceFlush failed: ${err instanceof Error ? err.message : String(err)}`,\n );\n }\n }\n\n shutdown(): Promise<void> {\n // Don't tear down Laminar's pipeline here — it may still be needed by\n // other instrumentations. Just drop our per-trace bookkeeping.\n this.traceMap.clear();\n this.generationStateById.clear();\n this.generationAttrsById.clear();\n this.generationIdByStepId.clear();\n this.stepIndexBySpanId.clear();\n this.liveOtelSpanByMastraId.clear();\n return Promise.resolve();\n }\n\n private handleSpanStarted(span: MastraExportedSpan): void {\n if (span.type === MastraSpanType.MODEL_CHUNK) return;\n\n const traceState = this.getOrCreateTraceState(span.traceId);\n const parentId = span.parentSpanId;\n\n this.recordSpanPath(span, traceState);\n traceState.activeSpanIds.add(span.id);\n\n if (span.type === MastraSpanType.MODEL_GENERATION) {\n this.initGenerationState(span);\n } else if (span.type === MastraSpanType.MODEL_STEP && parentId) {\n this.generationIdByStepId.set(span.id, parentId);\n const stepIndex =\n typeof span.attributes?.stepIndex === \"number\"\n ? span.attributes.stepIndex\n : 0;\n this.stepIndexBySpanId.set(span.id, stepIndex);\n }\n\n this.startOtelSpan(span, traceState);\n }\n\n // Build `spanPath` / `spanIdsPath` for a Mastra span and store them on the\n // trace state. For Mastra roots we seed from the captured outer observe()\n // path so Laminar's UI nests the subtree underneath; for Mastra descendants\n // we reuse the parent Mastra span's stored path (already has the outer\n // path prepended).\n private recordSpanPath(\n span: MastraExportedSpan,\n traceState: TraceState,\n ): void {\n const parentId = span.parentSpanId;\n const parentPath = parentId\n ? traceState.spanPathById.get(parentId)\n : traceState.otelParentSpanPath;\n const parentIdsPath = parentId\n ? traceState.spanIdsPathById.get(parentId)\n : traceState.otelParentSpanIdsPath;\n\n // Derive the UUID from the normalized (16-hex-char) span id so this\n // path entry agrees with what `convertSpanToOtel` produces via\n // `normalizeOtelSpanId` → OTel span id. Mastra occasionally emits span ids\n // longer than 16 hex chars; passing the raw value here would truncate\n // differently from the exported id and Laminar's UI (which threads\n // nesting on `lmnr.span.ids_path`) would render the Mastra subtree flat.\n const spanUuid = otelSpanIdToUUID(normalizeOtelSpanId(span.id));\n const spanPath = parentPath ? [...parentPath, span.name] : [span.name];\n const spanIdsPath = parentIdsPath\n ? [...parentIdsPath, spanUuid]\n : [spanUuid];\n\n traceState.spanPathById.set(span.id, spanPath);\n traceState.spanIdsPathById.set(span.id, spanIdsPath);\n }\n\n private async handleSpanEnded(span: MastraExportedSpan): Promise<void> {\n if (span.type === MastraSpanType.MODEL_CHUNK) {\n // MODEL_CHUNK spans are streaming noise as far as trace output goes\n // (dropped below), but reasoning chunks carry the thinking text for\n // their parent MODEL_STEP — accumulate before dropping. Mastra emits\n // these as `attributes.chunkType: \"reasoning\"` with `output.text` =\n // the delta text accumulated by its own chunk-span transform, and\n // the chunk ends BEFORE its parent MODEL_STEP ends, so the reasoning\n // is available when we build the step's LLM attributes.\n this.captureReasoningChunk(span);\n return;\n }\n\n const traceState = this.getOrCreateTraceState(span.traceId);\n\n try {\n // Backfill path/state when SPAN_STARTED was missed (e.g. exporter\n // registered mid-trace, the global SDK was unrecording at start time).\n // Inside the try so a throw here still runs cleanup.\n if (!traceState.spanPathById.has(span.id)) {\n this.recordSpanPath(span, traceState);\n // Mirror handleSpanStarted: register the span as active so the\n // finally block's `activeSpanIds.delete` is balanced.\n traceState.activeSpanIds.add(span.id);\n }\n if (\n span.type === MastraSpanType.MODEL_GENERATION &&\n !this.generationStateById.has(span.id)\n ) {\n this.initGenerationState(span);\n }\n if (\n span.type === MastraSpanType.MODEL_STEP &&\n !this.generationIdByStepId.has(span.id) &&\n span.parentSpanId\n ) {\n this.generationIdByStepId.set(span.id, span.parentSpanId);\n }\n if (!this.liveOtelSpanByMastraId.has(span.id)) {\n // Span_ended without a prior span_started, or the SDK was a\n // NonRecording proxy at start time and we couldn't keep a live\n // reference. Create the live span now (zero-duration if Mastra\n // didn't carry startTime).\n this.startOtelSpan(span, traceState);\n }\n\n // Record state transitions BEFORE applying attrs; LLM attribute\n // construction reads the accumulated step turn history.\n this.updateGenerationStateOnSpanEnd(span);\n\n const otelSpan = this.liveOtelSpanByMastraId.get(span.id);\n if (!otelSpan) {\n // `Laminar.initialize()` wasn't called, so the tracer was a noop\n // and `startOtelSpan` couldn't keep a recording span. Skip silently\n // — `startOtelSpan` already logged a one-time warning.\n return;\n }\n\n this.applyEndAttributes(span, otelSpan, traceState);\n\n if (span.errorInfo) {\n otelSpan.setStatus({\n code: SpanStatusCode.ERROR,\n message: span.errorInfo.message,\n });\n otelSpan.recordException({\n name: span.errorInfo.name ?? \"Error\",\n message: span.errorInfo.message,\n stack: span.errorInfo.stack,\n });\n }\n\n const endTime = span.endTime\n ? dateToHrTime(span.endTime)\n : dateToHrTime(span.startTime);\n otelSpan.end(endTime);\n\n // `Span.end()` synchronously fanned out to every processor on\n // Laminar's tracer provider. No manual onEnd push needed.\n if (this.config.realtime) {\n await getSpanProcessor()?.forceFlush();\n }\n } catch (err) {\n logger.error(\n `[MastraExporter] failed to export span ${span.id}: ${\n err instanceof Error ? err.message : String(err)\n }`,\n );\n } finally {\n traceState.activeSpanIds.delete(span.id);\n if (traceState.activeSpanIds.size === 0) {\n this.traceMap.delete(span.traceId);\n }\n if (span.type === MastraSpanType.MODEL_GENERATION) {\n this.generationStateById.delete(span.id);\n this.generationAttrsById.delete(span.id);\n }\n if (span.type === MastraSpanType.MODEL_STEP) {\n this.generationIdByStepId.delete(span.id);\n this.stepIndexBySpanId.delete(span.id);\n }\n this.liveOtelSpanByMastraId.delete(span.id);\n }\n }\n\n private initGenerationState(span: MastraExportedSpan): void {\n const rawMessages = extractMessages(span.input);\n const baseMessages = rawMessages ? normalizeInputMessages(rawMessages) : [];\n this.generationStateById.set(span.id, {\n baseMessages,\n turnsByStepIndex: new Map(),\n toolCallChildrenByStepIndex: new Map(),\n reasoningTextByStepIndex: new Map(),\n });\n }\n\n // Accumulate reasoning text from a MODEL_CHUNK into its parent MODEL_STEP's\n // slot on the surrounding MODEL_GENERATION. Chunks' `parentSpanId` is the\n // step span's id, and the step span was registered in `stepIndexBySpanId` /\n // `generationIdByStepId` at span_started, so we can resolve the slot\n // without any extra bookkeeping.\n //\n // When a step produces multiple separate reasoning blocks (reasoning-start\n // / reasoning-end fires more than once inside one step — Mastra starts a\n // new chunk span each time), we concatenate their texts in arrival order\n // to mirror Mastra's own `#bufferedReasoning` behavior.\n private captureReasoningChunk(span: MastraExportedSpan): void {\n const attrs = span.attributes ?? {};\n if (attrs.chunkType !== \"reasoning\") return;\n const output = span.output;\n if (!output || typeof output !== \"object\") return;\n const text = (output as Record<string, unknown>).text;\n if (typeof text !== \"string\" || text.length === 0) return;\n\n const stepSpanId = span.parentSpanId;\n if (!stepSpanId) return;\n const generationId = this.generationIdByStepId.get(stepSpanId);\n if (!generationId) return;\n const gen = this.generationStateById.get(generationId);\n if (!gen) return;\n const stepIndex = this.stepIndexBySpanId.get(stepSpanId);\n if (stepIndex === undefined) return;\n\n const existing = gen.reasoningTextByStepIndex.get(stepIndex);\n gen.reasoningTextByStepIndex.set(\n stepIndex,\n existing ? existing + text : text,\n );\n }\n\n private updateGenerationStateOnSpanEnd(span: MastraExportedSpan): void {\n if (span.type === MastraSpanType.MODEL_STEP) {\n const generationId = this.generationIdByStepId.get(span.id);\n if (!generationId) return;\n const gen = this.generationStateById.get(generationId);\n if (!gen) return;\n const stepIndex =\n this.stepIndexBySpanId.get(span.id) ??\n (typeof span.attributes?.stepIndex === \"number\"\n ? span.attributes.stepIndex\n : 0);\n this.stepIndexBySpanId.set(span.id, stepIndex);\n\n // Pair the declared `toolCalls` with tool_call children. Prefer id-based\n // pairing when children carry a toolCallId (parallel tool execution can\n // finish out of declaration order, so array-index pairing would\n // mis-attribute outputs). Fall back to arrival-order pairing among\n // unclaimed children when ids aren't available on the child spans.\n const output = span.output as Record<string, unknown> | undefined;\n const declaredToolCalls = Array.isArray(output?.toolCalls)\n ? (output.toolCalls as Array<Record<string, unknown>>)\n : [];\n const children = gen.toolCallChildrenByStepIndex.get(stepIndex) ?? [];\n const childrenById = new Map<string, ToolCallChild>();\n for (const c of children) {\n if (c.toolCallId) childrenById.set(c.toolCallId, c);\n }\n // Track already-paired children so the arrival-order fallback can't\n // re-pick a child that was already claimed by an id-based match.\n const consumed = new Set<ToolCallChild>();\n\n const turnMessages: AISdkMessage[] = [];\n const assistantMsg = buildAssistantMessageFromStepOutput(output);\n if (assistantMsg) turnMessages.push(assistantMsg);\n\n for (const dec of declaredToolCalls) {\n const toolCallId =\n (dec?.toolCallId as string | undefined) ??\n (dec?.id as string | undefined);\n // Don't skip declarations with a missing id:\n // `buildAssistantMessageFromStepOutput` still emitted a `tool-call`\n // part for this declaration, so we must emit a matching tool-result\n // or subsequent steps will see a broken history (assistant calls a\n // tool, no result follows). Fall through to arrival-order pairing in\n // that case, using whatever id the child span carries (may be\n // undefined).\n let child = toolCallId ? childrenById.get(toolCallId) : undefined;\n if (child && consumed.has(child)) child = undefined;\n if (!child) {\n // Fallback: first unconsumed child in arrival order.\n child = children.find((c) => !consumed.has(c));\n }\n if (!child) continue;\n consumed.add(child);\n const resolvedToolCallId = toolCallId ?? child.toolCallId;\n const toolName =\n (dec?.toolName as string | undefined) ??\n (dec?.name as string | undefined) ??\n child.toolName;\n turnMessages.push({\n role: \"tool\",\n tool_call_id: resolvedToolCallId,\n content: [\n {\n type: \"tool-result\",\n toolCallId: resolvedToolCallId,\n toolName,\n output: child.output,\n },\n ],\n });\n }\n\n gen.turnsByStepIndex.set(stepIndex, turnMessages);\n } else if (\n span.type === MastraSpanType.TOOL_CALL ||\n span.type === MastraSpanType.MCP_TOOL_CALL\n ) {\n // Record the tool_call child under its step so the step-end handler\n // can pair it with the declared toolCalls. Tool spans always end\n // before their parent MODEL_STEP ends, so the parent's stepIndex is\n // still in `stepIndexBySpanId` here.\n const parentId = span.parentSpanId;\n if (!parentId) return;\n const generationId = this.generationIdByStepId.get(parentId);\n if (!generationId) return;\n const gen = this.generationStateById.get(generationId);\n if (!gen) return;\n const stepIndex = this.stepIndexBySpanId.get(parentId);\n if (stepIndex === undefined) return;\n const list = gen.toolCallChildrenByStepIndex.get(stepIndex) ?? [];\n list.push({\n toolCallId: extractToolCallId(span),\n toolName: cleanMastraToolName(span.name) || \"tool\",\n input: span.input,\n output: span.output,\n });\n gen.toolCallChildrenByStepIndex.set(stepIndex, list);\n }\n }\n\n private getOrCreateTraceState(traceId: string): TraceState {\n const existing = this.traceMap.get(traceId);\n if (existing) return existing;\n const created: TraceState = {\n spanPathById: new Map(),\n spanIdsPathById: new Map(),\n activeSpanIds: new Set(),\n };\n // Capture the caller's active span synchronously, at the moment the\n // first event for this Mastra trace arrives — Mastra invokes exporter\n // handlers within the originating async context (see\n // `@mastra/observability` routeToHandler), so the caller's wrapper\n // span is still active here. Any later reads would be outside that\n // context (Mastra's event bus schedules handlers).\n //\n // Two stacks to consult, in order:\n // 1. Laminar's own context manager — the only place spans started via\n // `Laminar.startActiveSpan()` are tracked. That helper writes onto\n // a separate AsyncLocalStorage instance and never touches OTel's\n // standard context, so step (2) alone wouldn't see them.\n // 2. OTel's standard active span — set by `observe()` via\n // `context.with(...)` and by any other OTel-aware instrumentation.\n if (this.config?.linkToActiveContext) {\n const activeSpan =\n trace.getSpan(LaminarContextManager.getContext()) ??\n trace.getActiveSpan();\n const ctx = activeSpan?.spanContext();\n if (ctx && ctx.traceId && ctx.spanId) {\n created.otelTraceId = normalizeOtelTraceId(ctx.traceId);\n created.otelRootParentSpanId = normalizeOtelSpanId(ctx.spanId);\n // Also capture SPAN_PATH / SPAN_IDS_PATH so we can prepend them to\n // every Mastra span's path. Laminar's backend/UI threads the span\n // tree on `lmnr.span.ids_path` — sharing the traceId alone isn't\n // enough to make the Mastra subtree render under `observe()`.\n const attrs = (\n activeSpan as unknown as { attributes?: Record<string, unknown> }\n ).attributes;\n if (attrs) {\n const parentPath = attrs[SPAN_PATH];\n if (\n Array.isArray(parentPath) &&\n parentPath.every((p) => typeof p === \"string\")\n ) {\n created.otelParentSpanPath = parentPath;\n }\n const parentIdsPath = attrs[SPAN_IDS_PATH];\n if (\n Array.isArray(parentIdsPath) &&\n parentIdsPath.every((p) => typeof p === \"string\")\n ) {\n created.otelParentSpanIdsPath = parentIdsPath;\n }\n }\n }\n }\n this.traceMap.set(traceId, created);\n return created;\n }\n\n // Create a real OTel span via Laminar's tracer, then mutate its ids /\n // parent references so they line up with Mastra's. We use a real span (not\n // a manually-constructed ReadableSpan) so the registered LaminarSpanProcessor\n // sees it on `onStart` and can attach association properties from the\n // active context.\n private startOtelSpan(\n span: MastraExportedSpan,\n traceState: TraceState,\n ): void {\n // `Laminar.initialize()` is expected to have set up the tracer provider.\n // If it wasn't called, `getTracerProvider()` falls back to the global\n // OTel API which is a NoopTracerProvider, and `tracer.startSpan` will\n // return a NonRecordingSpan — caught below.\n const tracer = getTracerProvider().getTracer(\n \"@lmnr-ai/lmnr\",\n SDK_VERSION,\n );\n const parentCtx = this.buildParentContext(span, traceState);\n\n const otelSpan = tracer.startSpan(\n span.name,\n {\n startTime: dateToHrTime(span.startTime),\n kind: SpanKind.INTERNAL,\n },\n parentCtx,\n );\n\n if (!otelSpan.isRecording()) {\n this.warnNotInitializedOnce();\n return;\n }\n\n // Capture the SDK-allocated span id before we overwrite it.\n // `LaminarSpanProcessor.onStart` already fired synchronously inside\n // `tracer.startSpan` and keyed its `_spanIdToPath` / `_spanIdLists`\n // entries under this id — without dropping them below, `onEnd` (which\n // reads the post-mutation Mastra id) would delete nothing and both maps\n // would grow unboundedly.\n const sdkAllocatedSpanId = otelSpan.spanContext().spanId;\n\n // Stamp Mastra-derived ids over the SDK's auto-generated ones. Mutates\n // the SpanContext object the SDK holds internally — `spanContext()`\n // returns the same reference each call.\n const mastraSpanId = normalizeOtelSpanId(span.id);\n const mastraTraceId =\n traceState.otelTraceId ?? normalizeOtelTraceId(span.traceId);\n Object.assign(otelSpan.spanContext(), {\n traceId: mastraTraceId,\n spanId: mastraSpanId,\n });\n\n // Drop the orphaned path entry the processor cached under the original\n // SDK-allocated id. Safe no-op if the id happened to match the Mastra id\n // (unlikely but possible).\n if (sdkAllocatedSpanId !== mastraSpanId) {\n getSpanProcessor()?.dropPathInfo(sdkAllocatedSpanId);\n }\n\n // Patch parent references. We set both `parentSpanContext` (SDK v2) and\n // `parentSpanId` (SDK v1 alias) so this works across SDK versions.\n const parentSpanIdNormalized = span.parentSpanId\n ? normalizeOtelSpanId(span.parentSpanId)\n : traceState.otelRootParentSpanId;\n if (parentSpanIdNormalized) {\n const parentSpanContext: SpanContext = {\n traceId: mastraTraceId,\n spanId: parentSpanIdNormalized,\n traceFlags: TraceFlags.SAMPLED,\n isRemote: false,\n };\n Object.assign(otelSpan, {\n parentSpanId: parentSpanIdNormalized,\n parentSpanContext,\n });\n } else {\n // Mastra root with no captured outer span — clear whatever parent the\n // SDK inferred from `parentCtx` so the exported span reads as a root.\n Object.assign(otelSpan, {\n parentSpanId: undefined,\n parentSpanContext: undefined,\n });\n }\n\n // Globally-registered processors (e.g. LaminarSpanProcessor) wrote\n // `lmnr.span.path` / `lmnr.span.ids_path` during their `onStart` hook\n // using the SDK's auto-generated span id — overwrite with our\n // Mastra-derived versions so Laminar's UI nests the subtree correctly.\n const spanPath = traceState.spanPathById.get(span.id);\n const spanIdsPath = traceState.spanIdsPathById.get(span.id);\n if (spanPath) otelSpan.setAttribute(SPAN_PATH, spanPath);\n if (spanIdsPath) otelSpan.setAttribute(SPAN_IDS_PATH, spanIdsPath);\n\n this.liveOtelSpanByMastraId.set(span.id, otelSpan);\n }\n\n private warnNotInitializedOnce(): void {\n if (this.warnedNotInitialized) return;\n this.warnedNotInitialized = true;\n logger.warn(\n \"[MastraExporter] Laminar tracer is not initialized — Mastra spans \" +\n \"will be dropped. Call `Laminar.initialize({ projectApiKey: ... })` \" +\n \"before constructing `MastraExporter`.\",\n );\n }\n\n // Pick the OTel context whose active span will become the new Mastra\n // span's parent. Resolution order:\n // 1. Mastra child? Use the *live* (mutated) parent span we already\n // created — its spanContext exposes the Mastra-derived parent ids.\n // 2. Mastra root? Use the outer `observe()` / `Laminar.startActiveSpan()`\n // span captured at first-event time (if linking is enabled).\n // 3. Otherwise the bare context (Laminar's first, OTel's as fallback).\n private buildParentContext(\n span: MastraExportedSpan,\n traceState: TraceState,\n ): Context {\n const baseCtx =\n LaminarContextManager.getContext() ?? contextApi.active();\n\n if (span.parentSpanId) {\n const parentLive = this.liveOtelSpanByMastraId.get(span.parentSpanId);\n if (parentLive) return trace.setSpan(baseCtx, parentLive);\n }\n\n if (traceState.otelTraceId && traceState.otelRootParentSpanId) {\n const wrapped = trace.wrapSpanContext({\n traceId: traceState.otelTraceId,\n spanId: traceState.otelRootParentSpanId,\n traceFlags: TraceFlags.SAMPLED,\n isRemote: false,\n });\n return trace.setSpan(baseCtx, wrapped);\n }\n\n return baseCtx;\n }\n\n private applyEndAttributes(\n span: MastraExportedSpan,\n otelSpan: Span,\n traceState: TraceState,\n ): void {\n const attributes = this.buildLaminarAttributes(span, traceState);\n // Path attrs were already written in `startOtelSpan`. Skip them on the\n // bulk re-apply both to avoid pointless setAttribute churn and to keep\n // the path values stable if some upstream processor mutated them.\n delete attributes[SPAN_PATH];\n delete attributes[SPAN_IDS_PATH];\n otelSpan.setAttributes(attributes);\n }\n\n private buildLaminarAttributes(\n span: MastraExportedSpan,\n traceState: TraceState,\n ): Record<string, any> {\n const attributes: Record<string, any> = {};\n\n const spanPath = traceState.spanPathById.get(span.id);\n const spanIdsPath = traceState.spanIdsPathById.get(span.id);\n if (spanPath) attributes[SPAN_PATH] = spanPath;\n if (spanIdsPath) attributes[SPAN_IDS_PATH] = spanIdsPath;\n\n const laminarSpanType = mapLaminarSpanType(span.type);\n attributes[SPAN_TYPE] = laminarSpanType;\n attributes[SPAN_INSTRUMENTATION_SOURCE] = \"javascript\";\n attributes[SPAN_SDK_VERSION] = SDK_VERSION;\n const langVersion = getLangVersion();\n if (langVersion) attributes[SPAN_LANGUAGE_VERSION] = langVersion;\n\n // Preserve the original Mastra span type so e.g. agent_run vs\n // model_generation (both DEFAULT) can be told apart downstream.\n attributes[\"lmnr.internal.mastra.span_type\"] = span.type;\n\n const metadata = span.metadata ?? {};\n const sessionId = metadata.sessionId ?? metadata.session_id;\n if (typeof sessionId === \"string\" && sessionId.length > 0) {\n attributes[SESSION_ID] = sessionId;\n }\n const userId = metadata.userId ?? metadata.user_id;\n if (typeof userId === \"string\" && userId.length > 0) {\n attributes[USER_ID] = userId;\n }\n for (const [key, value] of Object.entries(metadata)) {\n if (\n key === \"sessionId\" ||\n key === \"session_id\" ||\n key === \"userId\" ||\n key === \"user_id\" ||\n value === undefined ||\n value === null\n ) {\n continue;\n }\n const av = toAttributeValue(value);\n if (av !== undefined) {\n attributes[`${ASSOCIATION_PROPERTIES}.metadata.${key}`] = av;\n }\n }\n if (span.isRootSpan && span.tags?.length) {\n attributes[`${ASSOCIATION_PROPERTIES}.tags`] = span.tags;\n }\n\n if (laminarSpanType === \"LLM\") {\n this.applyLlmAttributes(span, attributes);\n } else if (laminarSpanType === \"TOOL\") {\n this.applyToolAttributes(span, attributes);\n } else {\n if (span.input !== undefined) {\n attributes[SPAN_INPUT] = serializeJSON(span.input);\n }\n if (span.output !== undefined) {\n attributes[SPAN_OUTPUT] = serializeJSON(span.output);\n }\n }\n\n return attributes;\n }\n\n private applyLlmAttributes(\n span: MastraExportedSpan,\n attributes: Record<string, any>,\n ): void {\n const generationId =\n this.generationIdByStepId.get(span.id) ?? span.parentSpanId;\n const gen = generationId\n ? this.generationStateById.get(generationId)\n : undefined;\n const generationAttrs = generationId\n ? this.generationAttrsById.get(generationId)\n : undefined;\n\n const stepAttrs = span.attributes ?? {};\n const stepIndex =\n this.stepIndexBySpanId.get(span.id) ??\n (typeof stepAttrs.stepIndex === \"number\" ? stepAttrs.stepIndex : 0);\n\n const provider = generationAttrs?.provider as string | undefined;\n const model = generationAttrs?.model as string | undefined;\n const responseModel = generationAttrs?.responseModel as string | undefined;\n if (provider)\n attributes[LaminarAttributes.PROVIDER] = normalizeProvider(provider);\n if (model) attributes[LaminarAttributes.REQUEST_MODEL] = model;\n if (responseModel)\n attributes[LaminarAttributes.RESPONSE_MODEL] = responseModel;\n else if (model) attributes[LaminarAttributes.RESPONSE_MODEL] = model;\n\n const usage = (stepAttrs.usage ?? generationAttrs?.usage) as\n | MastraUsageStats\n | undefined;\n Object.assign(attributes, formatUsage(usage));\n\n if (typeof stepAttrs.finishReason === \"string\") {\n attributes[\"gen_ai.response.finish_reason\"] = stepAttrs.finishReason;\n attributes[\"ai.response.finishReason\"] = stepAttrs.finishReason;\n }\n\n // Build the full message history for this step: base + every turn with\n // stepIndex < current stepIndex, flattened.\n const messages: AISdkMessage[] = gen ? [...gen.baseMessages] : [];\n if (gen) {\n for (let i = 0; i < stepIndex; i++) {\n const turn = gen.turnsByStepIndex.get(i);\n if (turn) messages.push(...turn);\n }\n }\n\n if (messages.length > 0) {\n attributes[\"ai.prompt.messages\"] = serializeJSON(messages);\n } else if (span.input !== undefined) {\n attributes[SPAN_INPUT] = serializeJSON(span.input);\n }\n\n const out = span.output;\n const reasoningText = gen?.reasoningTextByStepIndex.get(stepIndex);\n if (out && typeof out === \"object\") {\n const outObj = out as Record<string, unknown>;\n if (typeof outObj.text === \"string\" && outObj.text.length > 0) {\n attributes[\"ai.response.text\"] = outObj.text;\n }\n const normalizedToolCalls =\n Array.isArray(outObj.toolCalls) && outObj.toolCalls.length > 0\n ? (outObj.toolCalls as Array<Record<string, unknown>>).map((tc) => ({\n toolCallType: tc.toolCallType ?? \"function\",\n toolCallId: tc.toolCallId,\n toolName: tc.toolName,\n args: stringifyArgs(tc.args ?? tc.input),\n }))\n : [];\n if (normalizedToolCalls.length > 0) {\n attributes[\"ai.response.toolCalls\"] =\n JSON.stringify(normalizedToolCalls);\n }\n // Emit OTel GenAI semconv `gen_ai.output.messages` with a `thinking`\n // part when this step had reasoning chunks. Laminar's backend\n // preserves this attribute verbatim on `self.output` and the frontend\n // GenAI parser maps `thinking` → ModelMessage `reasoning`, rendered\n // with a \"Thinking\" label in the trace UI.\n if (typeof reasoningText === \"string\" && reasoningText.length > 0) {\n const parts: Array<Record<string, unknown>> = [\n { type: \"thinking\", content: reasoningText },\n ];\n if (typeof outObj.text === \"string\" && outObj.text.length > 0) {\n parts.push({ type: \"text\", content: outObj.text });\n }\n for (const tc of normalizedToolCalls) {\n let argsObj: unknown = tc.args;\n if (typeof argsObj === \"string\") {\n try {\n argsObj = JSON.parse(argsObj);\n } catch {\n // leave as string; backend still accepts it\n }\n }\n parts.push({\n type: \"tool_call\",\n id: tc.toolCallId,\n name: tc.toolName,\n arguments: argsObj,\n });\n }\n attributes[\"gen_ai.output.messages\"] = JSON.stringify([\n { role: \"assistant\", parts },\n ]);\n }\n } else if (out !== undefined) {\n attributes[SPAN_OUTPUT] = serializeJSON(out);\n }\n }\n\n private applyToolAttributes(\n span: MastraExportedSpan,\n attributes: Record<string, any>,\n ): void {\n if (span.input !== undefined) {\n attributes[SPAN_INPUT] = serializeJSON(span.input);\n }\n if (span.output !== undefined) {\n attributes[SPAN_OUTPUT] = serializeJSON(span.output);\n }\n const toolAttrs = span.attributes ?? {};\n // Strip the `tool: 'x'` prefix Mastra prepends so the displayed name is\n // just the tool id.\n const cleanedName = cleanMastraToolName(span.name) || span.name;\n if (cleanedName) attributes[\"ai.toolCall.name\"] = cleanedName;\n if (typeof toolAttrs.toolType === \"string\") {\n attributes[\"ai.toolCall.type\"] = toolAttrs.toolType;\n }\n if (typeof toolAttrs.mcpServer === \"string\") {\n attributes[\"mcp.server\"] = toolAttrs.mcpServer;\n }\n }\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.mjs","names":["logger","SDK_VERSION","contextApi"],"sources":["../src/datasets.ts","../src/evaluations.ts","../src/opentelemetry-lib/instrumentation/aisdk/base-language-model.ts","../src/opentelemetry-lib/instrumentation/aisdk/v2.ts","../src/opentelemetry-lib/instrumentation/aisdk/v3.ts","../src/opentelemetry-lib/instrumentation/aisdk/index.ts","../src/opentelemetry-lib/instrumentation/mastra/types.ts","../src/opentelemetry-lib/instrumentation/mastra/utils.ts","../src/opentelemetry-lib/instrumentation/mastra/exporter.ts"],"sourcesContent":["import { LaminarClient } from '@lmnr-ai/client';\nimport { type StringUUID } from '@lmnr-ai/types';\n\nimport { Datapoint } from './evaluations';\n\nconst DEFAULT_FETCH_SIZE = 25;\n\nexport abstract class EvaluationDataset<D, T> {\n public async slice(start: number, end: number): Promise<Datapoint<D, T>[]> {\n const result = [];\n for (let i = Math.max(start, 0); i < Math.min(end, await this.size()); i++) {\n result.push(await this.get(i));\n }\n return result;\n }\n public abstract size(): Promise<number> | number;\n public abstract get(index: number): Promise<Datapoint<D, T>> | Datapoint<D, T>;\n}\n\nexport class LaminarDataset<D, T> extends EvaluationDataset<D, T> {\n private fetchedItems: Datapoint<D, T>[] = [];\n private len: number | null = null;\n private offset: number = 0;\n private fetchSize: number;\n private client: LaminarClient | undefined = undefined;\n\n public name: string | undefined;\n public id?: StringUUID;\n\n constructor(name?: string, options?: { id?: StringUUID; fetchSize?: number }) {\n super();\n if (!name && !options?.id) {\n throw new Error('Either name or id must be provided');\n }\n if (name && options?.id) {\n throw new Error('Only one of name or id must be provided');\n }\n this.name = name;\n this.id = options?.id;\n this.fetchSize = options?.fetchSize || DEFAULT_FETCH_SIZE;\n }\n\n public setClient(client: LaminarClient) {\n this.client = client;\n }\n\n private async fetchBatch() {\n if (!this.client) {\n throw new Error('Client not set');\n }\n const identifier = this.id ? { id: this.id } : { name: this.name! };\n const resp = await this.client.datasets.pull<D, T>({\n ...identifier,\n offset: this.offset,\n limit: this.fetchSize,\n });\n this.fetchedItems = this.fetchedItems.concat(resp.items);\n this.offset = this.fetchedItems.length;\n if (this.len === null) {\n this.len = resp.totalCount;\n }\n }\n\n public async size(): Promise<number> {\n if (this.len === null) {\n await this.fetchBatch();\n }\n return this.len!;\n }\n\n public async get(index: number): Promise<Datapoint<D, T>> {\n if (index >= this.fetchedItems.length) {\n await this.fetchBatch();\n }\n return this.fetchedItems[index];\n }\n\n /**\n * Push data from files to this dataset.\n *\n * @param {string | string[]} paths - Path(s) to files or directories containing data\n * @param {boolean} recursive - Whether to recursively read files in directories\n */\n public async push(paths: string | string[], recursive: boolean = false): Promise<void> {\n if (!this.client) {\n throw new Error('Client not set');\n }\n\n // Dynamic import to avoid circular dependency\n const { loadFromPaths } = await import('./cli/file-utils');\n\n const pathArray = Array.isArray(paths) ? paths : [paths];\n const data = await loadFromPaths<D, T>(pathArray, recursive);\n\n if (data.length === 0) {\n console.warn('No data to push. Skipping');\n return;\n }\n\n const identifier = this.id ? { id: this.id } : { name: this.name! };\n await this.client.datasets.push({\n points: data,\n ...identifier,\n });\n\n console.log(`Successfully pushed ${data.length} datapoints to dataset`);\n }\n}\n","import { LaminarClient } from \"@lmnr-ai/client\";\nimport { EvaluationDatapoint } from \"@lmnr-ai/types\";\nimport { trace } from \"@opentelemetry/api\";\nimport * as cliProgress from \"cli-progress\";\n\nimport { EvaluationDataset, LaminarDataset } from \"./datasets\";\nimport { observe } from \"./decorators\";\nimport { Laminar } from \"./laminar\";\nimport { InitializeOptions } from \"./opentelemetry-lib/interfaces\";\nimport {\n HUMAN_EVALUATOR_OPTIONS,\n SPAN_TYPE,\n} from \"./opentelemetry-lib/tracing/attributes\";\nimport {\n ASSOCIATION_PROPERTIES_KEY,\n LaminarContextManager,\n} from \"./opentelemetry-lib/tracing/context\";\nimport {\n getFrontendUrl,\n initializeLogger,\n loadEnv,\n newUUID,\n otelSpanIdToUUID,\n otelTraceIdToUUID,\n Semaphore,\n StringUUID,\n} from \"./utils\";\n\nloadEnv();\n\nconst DEFAULT_CONCURRENCY = 5;\nconst MAX_EXPORT_BATCH_SIZE = 64;\n\ndeclare global {\n var _evaluations: Evaluation<any, any, any>[] | undefined;\n // If true, then we need to set the evaluation globally without running it\n var _set_global_evaluation: boolean;\n}\n\nconst logger = initializeLogger();\n\nconst getEvaluationUrl = (\n projectId: string,\n evaluationId: string,\n baseUrl?: string,\n frontendPort?: number,\n): string => {\n const url = getFrontendUrl(baseUrl, frontendPort);\n return `${url}/project/${projectId}/evaluations/${evaluationId}`;\n};\n\nconst getAverageScores = <D, T, O>(\n results: EvaluationDatapoint<D, T, O>[],\n): Record<string, number> => {\n const perScoreValues: Record<string, number[]> = {};\n for (const result of results) {\n for (const key in result.scores) {\n const score = result.scores[key];\n if (perScoreValues[key] && score !== null) {\n perScoreValues[key].push(score);\n } else {\n perScoreValues[key] = score !== null ? [score] : [];\n }\n }\n }\n\n const averageScores: Record<string, number> = {};\n for (const key in perScoreValues) {\n averageScores[key] =\n perScoreValues[key].reduce((a, b) => a + b, 0) /\n perScoreValues[key].length;\n }\n\n return averageScores;\n};\n\n/**\n * Configuration for the Evaluator\n */\ninterface EvaluationConfig {\n /**\n * The number of data points to evaluate in parallel at a time. Defaults to 5.\n */\n concurrencyLimit?: number;\n /**\n * The project API key to use for the evaluation. If not provided,\n * the API key from the environment variable `LMNR_PROJECT_API_KEY` will be used.\n */\n projectApiKey?: string;\n /**\n * The base URL of the Laminar API. If not provided, the default is\n * `https://api.lmnr.ai`. Useful with self-hosted Laminar instances.\n * Do NOT include the port in the URL, use `httpPort` and `grpcPort` instead.\n */\n baseUrl?: string;\n /**\n * The base HTTP URL of the Laminar API. If not provided, the default is\n * `baseUrl`. Only use this if you want to proxy HTTP requests through a different host.\n */\n baseHttpUrl?: string;\n /**\n * The HTTP port of the Laminar API. If not provided, the default is 443.\n */\n httpPort?: number;\n /**\n * The gRPC port of the Laminar API. If not provided, the default is 8443.\n */\n grpcPort?: number;\n /**\n * Object with modules to instrument. If not provided, all\n * available modules are instrumented.\n * See {@link https://laminar.sh/docs/sdk/typescript/instrumentation#instrumentmodules}\n */\n instrumentModules?: InitializeOptions[\"instrumentModules\"];\n /**\n * If true, then the spans will not be batched.\n */\n traceDisableBatch?: boolean;\n /**\n * Timeout for trace export. Defaults to 30_000 (30 seconds), which is over\n * the default OTLP exporter timeout of 10_000 (10 seconds).\n */\n traceExportTimeoutMillis?: number;\n /**\n * Defines default log level for SDK and all instrumentations.\n */\n logLevel?: \"debug\" | \"info\" | \"warn\" | \"error\";\n\n /**\n * Maximum number of spans to export at a time. Defaults to 64.\n */\n traceExportBatchSize?: number;\n /**\n * The port for the Laminar , when running self-hosted. If not provided, the default is 5667.\n */\n frontendPort?: number;\n}\n\n/**\n * Datapoint is a single data point in the evaluation. `D` is the type of the input data,\n * `T` is the type of the target data.\n */\nexport type Datapoint<D, T> = {\n /**\n * input to the executor function. Must be json serializable. Required.\n */\n data: D;\n /**\n * input to the evaluator function (alongside the executor output).\n * Must be json serializable.\n */\n target?: T;\n /**\n * metadata to the evaluator function. Must be json serializable.\n */\n metadata?: Record<string, any>;\n /**\n * Optional ID of the datapoint (from dataset)\n */\n id?: StringUUID;\n /**\n * Optional creation timestamp (from dataset)\n */\n createdAt?: string;\n};\n\n/**\n * HumanEvaluator is a class to register a human evaluator.\n */\nexport class HumanEvaluator {\n public options?: { value: number; label: string }[];\n\n constructor(options?: { value: number; label: string }[]) {\n this.options = options;\n }\n}\n\nexport type EvaluatorFunctionReturn = number | Record<string, number>;\n\n/**\n * EvaluatorFunction is a function that takes the output of the executor, the\n * target, and the data, and returns a score. The score can be a single number or a record\n * of string keys and number values. The latter is useful for evaluating\n * multiple criteria in one go instead of running multiple evaluators.\n */\nexport type EvaluatorFunction<O, T, D = any> = (\n output: O,\n target?: T,\n data?: D,\n ...args: any[]\n) => EvaluatorFunctionReturn | Promise<EvaluatorFunctionReturn>;\n\ninterface EvaluationConstructorProps<D, T, O> {\n /**\n * List of data points to evaluate. `data` is the input to the executor function,\n * `target` is the input to the evaluator function.\n */\n data: Datapoint<D, T>[] | EvaluationDataset<D, T>;\n /**\n * The executor function. Takes the data point + any additional arguments\n * and returns the output to evaluate.\n */\n executor: (data: D, ...args: any[]) => O | Promise<O>;\n /**\n * Evaluator functions and names. Each evaluator function takes the output of\n * the executor, the target, and the data, and returns a score. The score can be a\n * single number or a dict of string keys and number values. If the score is a\n * single number, it will be named after the evaluator function. Evaluator\n * function names must contain only letters, digits, hyphens, underscores,\n * or spaces.\n */\n evaluators: Record<string, EvaluatorFunction<O, T, D> | HumanEvaluator>;\n /**\n * Name of the evaluation. If not provided, a random name will be assigned.\n */\n name?: string;\n /**\n * Optional group id of the evaluation. Only evaluations within the same\n * group_id can be visually compared. Defaults to \"default\".\n */\n groupName?: string;\n /**\n * Optional metadata to evaluation\n */\n metadata?: Record<string, any>;\n /**\n * Optional override configurations for the evaluator.\n */\n config?: EvaluationConfig;\n}\n\ninterface EvaluationRunResult {\n averageScores: Record<string, number>;\n projectId: string;\n evaluationId: string;\n url: string;\n errorMessage?: string;\n}\n\n/**\n * Reports the whole progress to the console.\n */\nclass EvaluationReporter {\n private cliProgress: cliProgress.SingleBar = new cliProgress.SingleBar(\n {},\n cliProgress.Presets.shades_classic,\n );\n private progressCounter: number = 0;\n public baseUrl: string;\n public frontendPort?: number;\n\n constructor(baseUrl?: string, frontendPort?: number) {\n this.baseUrl = baseUrl ?? \"https://api.lmnr.ai\";\n this.frontendPort = frontendPort;\n }\n\n public start({ length }: { length: number }) {\n this.cliProgress.start(length, 0);\n }\n\n public update(batchLength: number) {\n this.progressCounter += batchLength;\n this.cliProgress.update(this.progressCounter);\n }\n\n // Call either error or stop, not both\n public stopWithError(error: Error) {\n this.cliProgress.stop();\n process.stdout.write(`\\nError: ${error.message}\\n`);\n }\n\n // Call either error or stop, not both\n public stop({\n averageScores,\n projectId,\n evaluationId,\n }: {\n averageScores: Record<string, number>;\n projectId: string;\n evaluationId: string;\n }) {\n this.cliProgress.stop();\n const url = getEvaluationUrl(\n projectId,\n evaluationId,\n this.baseUrl,\n this.frontendPort,\n );\n process.stdout.write(\"\\n\");\n process.stdout.write(\"\\nAverage scores:\\n\");\n for (const key in averageScores) {\n process.stdout.write(`${key}: ${averageScores[key]}\\n`);\n }\n process.stdout.write(`\\nCheck results at ${url}\\n`);\n }\n}\n\nexport class Evaluation<D, T, O> {\n private isFinished: boolean = false;\n private progressReporter: EvaluationReporter;\n private data: Datapoint<D, T>[] | EvaluationDataset<D, T>;\n private executor: (data: D, ...args: any[]) => O | Promise<O>;\n private evaluators: Record<\n string,\n EvaluatorFunction<O, T, D> | HumanEvaluator\n >;\n private groupName?: string;\n private frontendPort?: number;\n private name?: string;\n private metadata?: Record<string, any>;\n private concurrencyLimit: number = DEFAULT_CONCURRENCY;\n private traceDisableBatch: boolean = false;\n private traceExportTimeoutMillis?: number;\n private traceExportBatchSize: number = MAX_EXPORT_BATCH_SIZE;\n private uploadPromises: Promise<any>[] = [];\n private client: LaminarClient;\n\n constructor({\n data,\n executor,\n evaluators,\n groupName,\n name,\n metadata,\n config,\n }: EvaluationConstructorProps<D, T, O>) {\n if (Object.keys(evaluators).length === 0) {\n throw new Error(\"No evaluators provided\");\n }\n\n const evaluatorNameRegex = /^[\\w\\s-]+$/;\n // Validate evaluator keys\n for (const key in evaluators) {\n if (!evaluatorNameRegex.test(key)) {\n throw new Error(\n `Invalid evaluator key: \"${key}\".` +\n \"Keys must only contain letters, digits, hyphens, underscores, or spaces.\",\n );\n }\n }\n\n this.frontendPort = config?.frontendPort;\n this.progressReporter = new EvaluationReporter(\n config?.baseUrl,\n config?.frontendPort,\n );\n this.data = data;\n this.executor = executor;\n this.evaluators = evaluators;\n this.groupName = groupName;\n this.metadata = metadata;\n this.name = name;\n\n if (config) {\n if (\n config.concurrencyLimit !== undefined &&\n config.concurrencyLimit < 1\n ) {\n logger.warn(\n `concurrencyLimit must be greater than 0. Setting to default of ${DEFAULT_CONCURRENCY}`,\n );\n this.concurrencyLimit = DEFAULT_CONCURRENCY;\n } else {\n this.concurrencyLimit = config.concurrencyLimit ?? DEFAULT_CONCURRENCY;\n }\n this.traceDisableBatch = config.traceDisableBatch ?? false;\n this.traceExportTimeoutMillis = config.traceExportTimeoutMillis;\n this.traceExportBatchSize =\n config.traceExportBatchSize ?? MAX_EXPORT_BATCH_SIZE;\n }\n\n if (Laminar.initialized()) {\n this.client = new LaminarClient({\n baseUrl: Laminar.getHttpUrl(),\n projectApiKey: Laminar.getProjectApiKey(),\n });\n if (\n config?.projectApiKey &&\n config.projectApiKey !== Laminar.getProjectApiKey()\n ) {\n logger.warn(\n \"Laminar was already initialized with a different project API key. \" +\n \"Ignoring the project API key from the evaluation config.\",\n );\n }\n return;\n }\n\n const key = config?.projectApiKey ?? process.env.LMNR_PROJECT_API_KEY;\n if (key === undefined) {\n throw new Error(\n \"Please initialize the Laminar object with your project API key \" +\n \"or set the LMNR_PROJECT_API_KEY environment variable\",\n );\n }\n\n const url =\n config?.baseUrl ?? process?.env?.LMNR_BASE_URL ?? \"https://api.lmnr.ai\";\n const httpUrl = config?.baseHttpUrl ?? url;\n const httpPort =\n config?.httpPort ??\n (httpUrl.match(/:\\d{1,5}$/g)\n ? parseInt(httpUrl.match(/:\\d{1,5}$/g)![0].slice(1))\n : 443);\n const urlWithoutSlash = httpUrl\n .replace(/\\/$/, \"\")\n .replace(/:\\d{1,5}$/g, \"\");\n const baseHttpUrl = `${urlWithoutSlash}:${httpPort}`;\n\n this.client = new LaminarClient({\n baseUrl: baseHttpUrl,\n projectApiKey: key,\n });\n\n Laminar.initialize({\n projectApiKey: config?.projectApiKey,\n baseUrl: url,\n baseHttpUrl,\n httpPort,\n grpcPort: config?.grpcPort,\n instrumentModules: config?.instrumentModules,\n disableBatch: this.traceDisableBatch,\n traceExportTimeoutMillis: this.traceExportTimeoutMillis,\n maxExportBatchSize: this.traceExportBatchSize,\n });\n }\n\n public async run(): Promise<EvaluationRunResult> {\n if (this.isFinished) {\n throw new Error(\"Evaluation is already finished\");\n }\n if (this.data instanceof LaminarDataset) {\n this.data.setClient(this.client);\n // Fetch dataset ID if not already set\n if (!this.data.id) {\n try {\n const datasets = await this.client.datasets.getDatasetByName(\n (this.data as any).name,\n );\n if (datasets.length > 0) {\n this.data.id = datasets[0].id;\n } else {\n logger.warn(`Dataset ${(this.data as any).name} not found`);\n }\n } catch (error) {\n // Backward compatibility with old Laminar API (self-hosted)\n logger.warn(\n `Error getting dataset ${this.data.name}: ` +\n `${error instanceof Error ? error.message : String(error)}`,\n );\n }\n }\n }\n\n let resultDatapoints: EvaluationDatapoint<D, T, O>[];\n try {\n const evaluation = await this.client.evals.init(\n this.name,\n this.groupName,\n this.metadata,\n );\n const url = getEvaluationUrl(\n evaluation.projectId,\n evaluation.id,\n this.progressReporter.baseUrl,\n this.frontendPort,\n );\n process.stdout.write(`\\nCheck results at ${url}\\n`);\n this.progressReporter.start({ length: await this.getLength() });\n\n resultDatapoints = await this.evaluateInBatches(evaluation.id);\n const averageScores = getAverageScores(resultDatapoints);\n if (this.uploadPromises.length > 0) {\n await Promise.all(this.uploadPromises);\n }\n this.progressReporter.stop({\n averageScores,\n projectId: evaluation.projectId,\n evaluationId: evaluation.id,\n });\n this.isFinished = true;\n\n await Laminar.shutdown();\n return {\n averageScores,\n projectId: evaluation.projectId,\n evaluationId: evaluation.id,\n url,\n };\n } catch (e) {\n this.progressReporter.stopWithError(e as Error);\n this.isFinished = true;\n return {\n averageScores: {},\n projectId: \"\",\n evaluationId: \"\",\n url: \"\",\n errorMessage: e instanceof Error ? e.message : String(e),\n };\n }\n }\n\n public async evaluateInBatches(\n evalId: StringUUID,\n ): Promise<EvaluationDatapoint<D, T, O>[]> {\n const baseContext = LaminarContextManager.getContext();\n const currentAssociationProperties = (baseContext.getValue(\n ASSOCIATION_PROPERTIES_KEY,\n ) ?? {}) as Record<string, any>;\n const entityContext = baseContext.setValue(ASSOCIATION_PROPERTIES_KEY, {\n ...currentAssociationProperties,\n metadata: {\n ...(currentAssociationProperties.metadata ?? {}),\n evaluation_id: evalId,\n },\n });\n\n return LaminarContextManager.runWithIsolatedContext<\n Promise<EvaluationDatapoint<D, T, O>[]>\n >([entityContext], async () => {\n const semaphore = new Semaphore(this.concurrencyLimit);\n const tasks: Promise<[number, EvaluationDatapoint<D, T, O>]>[] = [];\n\n const evaluateTask = async (\n datapoint: Datapoint<D, T>,\n index: number,\n ): Promise<[number, EvaluationDatapoint<D, T, O>]> => {\n try {\n const result = await this.evaluateDatapoint(\n evalId,\n datapoint,\n index,\n );\n this.progressReporter.update(1);\n return [index, result];\n } finally {\n semaphore.release();\n }\n };\n\n for (let i = 0; i < (await this.getLength()); i++) {\n await semaphore.acquire();\n const datapoint = Array.isArray(this.data)\n ? this.data[i]\n : await this.data.get(i);\n tasks.push(evaluateTask(datapoint, i));\n }\n const results = await Promise.all(tasks);\n\n return results.sort((a, b) => a[0] - b[0]).map(([, result]) => result);\n });\n }\n\n private async evaluateDatapoint(\n evalId: StringUUID,\n datapoint: Datapoint<D, T>,\n index: number,\n ): Promise<EvaluationDatapoint<D, T, O>> {\n return observe(\n { name: \"evaluation\", traceType: \"EVALUATION\" },\n async () => {\n trace\n .getSpan(LaminarContextManager.getContext())!\n .setAttribute(SPAN_TYPE, \"EVALUATION\");\n const executorSpan = Laminar.startSpan({\n name: \"executor\",\n input: datapoint.data,\n });\n executorSpan.setAttribute(SPAN_TYPE, \"EXECUTOR\");\n const executorSpanId = otelSpanIdToUUID(\n executorSpan.spanContext().spanId,\n );\n const datapointId = newUUID();\n const partialDatapoint = {\n id: datapointId,\n data: datapoint.data,\n target: datapoint.target,\n metadata: datapoint.metadata,\n traceId: otelTraceIdToUUID(\n trace.getSpan(LaminarContextManager.getContext())!.spanContext()\n .traceId,\n ),\n executorSpanId,\n index,\n } as EvaluationDatapoint<D, T, O>;\n\n // Add dataset link if data is from LaminarDataset\n if (\n this.data instanceof LaminarDataset &&\n this.data.id &&\n datapoint.id &&\n datapoint.createdAt\n ) {\n partialDatapoint.datasetLink = {\n datasetId: this.data.id,\n datapointId: datapoint.id,\n createdAt: datapoint.createdAt,\n };\n }\n\n // first create the datapoint in the database and await\n await this.client.evals.saveDatapoints({\n evalId,\n datapoints: [partialDatapoint],\n groupName: this.groupName,\n });\n\n const output = await Laminar.withSpan(\n executorSpan,\n async () => {\n const result = await this.executor(datapoint.data);\n Laminar.setSpanOutput(result);\n return result;\n },\n true,\n );\n const target = datapoint.target;\n\n let scores: Record<string, number | null> = {};\n for (const [evaluatorName, evaluator] of Object.entries(\n this.evaluators,\n )) {\n const value = await observe(\n { name: evaluatorName },\n async (output: O, target?: T, data?: D) => {\n if (evaluator instanceof HumanEvaluator) {\n const activeSpan = trace.getSpan(\n LaminarContextManager.getContext(),\n );\n if (activeSpan) {\n activeSpan.setAttribute(SPAN_TYPE, \"HUMAN_EVALUATOR\");\n if (evaluator.options) {\n activeSpan.setAttribute(\n HUMAN_EVALUATOR_OPTIONS,\n JSON.stringify(evaluator.options),\n );\n }\n }\n return null;\n } else {\n const activeSpan = trace.getSpan(\n LaminarContextManager.getContext(),\n );\n if (activeSpan) {\n activeSpan.setAttribute(SPAN_TYPE, \"EVALUATOR\");\n }\n return await evaluator(output, target, data);\n }\n },\n output,\n datapoint.target,\n datapoint.data,\n );\n\n if (evaluator instanceof HumanEvaluator) {\n scores[evaluatorName] = null;\n continue;\n }\n\n if (typeof value === \"number\") {\n if (isNaN(value)) {\n throw new Error(`Evaluator ${evaluatorName} returned NaN`);\n }\n scores[evaluatorName] = value;\n } else if (value !== null) {\n scores = { ...scores, ...value };\n }\n }\n\n const resultDatapoint = {\n id: datapointId,\n executorOutput: output,\n data: datapoint.data,\n target,\n metadata: datapoint.metadata,\n scores,\n traceId: otelTraceIdToUUID(\n trace.getSpan(LaminarContextManager.getContext())!.spanContext()\n .traceId,\n ),\n executorSpanId,\n index,\n } as EvaluationDatapoint<D, T, O>;\n\n // Add dataset link if data is from LaminarDataset\n if (\n this.data instanceof LaminarDataset &&\n this.data.id &&\n datapoint.id &&\n datapoint.createdAt\n ) {\n resultDatapoint.datasetLink = {\n datasetId: this.data.id,\n datapointId: datapoint.id,\n createdAt: datapoint.createdAt,\n };\n }\n\n const uploadPromise = this.client.evals.saveDatapoints({\n evalId,\n datapoints: [resultDatapoint],\n groupName: this.groupName,\n });\n this.uploadPromises.push(uploadPromise);\n\n return resultDatapoint;\n },\n );\n }\n\n private async getLength(): Promise<number> {\n return this.data instanceof EvaluationDataset\n ? await this.data.size()\n : this.data.length;\n }\n\n public setFrontendPort(port: number) {\n this.frontendPort = port;\n this.progressReporter.frontendPort = port;\n }\n}\n\n/**\n * If added to the file which is called through lmnr eval command, then simply\n * registers the evaluation. Otherwise, returns a promise which resolves when\n * the evaluation is finished. If the evaluation has no async logic, then it\n * will be executed synchronously.\n *\n * @param props.data List of data points to evaluate. `data` is the input to the\n * executor function, `target` is the input to the evaluator function.\n * @param props.executor The executor function. Takes the data point + any\n * additional arguments and returns the output to evaluate.\n * @param props.evaluators Map from evaluator name to evaluator function. Each\n * evaluator function takes the output of the executor and the target data, and\n * returns.\n * @param props.name Optional name of the evaluation. Used to easily identify\n * the evaluation in the group.\n * @param props.metadata Optional metadata to evaluation\n * @param props.config Optional override configurations for the evaluator.\n */\nexport async function evaluate<D, T, O>({\n data,\n executor,\n evaluators,\n groupName,\n name,\n metadata,\n config,\n}: EvaluationConstructorProps<D, T, O>): Promise<\n EvaluationRunResult | undefined\n> {\n const evaluation = new Evaluation<D, T, O>({\n data,\n executor,\n evaluators,\n name,\n groupName,\n metadata,\n config,\n });\n if (globalThis._set_global_evaluation) {\n // TODO: if we load files concurrently, we need to use a mutex to protect\n // concurrent writes to globalThis._evaluations\n globalThis._evaluations = [...(globalThis._evaluations ?? []), evaluation];\n } else {\n return await evaluation.run();\n }\n}\n","import {\n type JSONSchema7,\n type LanguageModelV3,\n type LanguageModelV3CallOptions,\n type LanguageModelV3Content,\n type LanguageModelV3FinishReason,\n type LanguageModelV3FunctionTool,\n type LanguageModelV3Message,\n type LanguageModelV3ProviderTool,\n type LanguageModelV3ResponseMetadata,\n type LanguageModelV3Usage,\n type SharedV3Headers,\n type SharedV3ProviderMetadata,\n type SharedV3Warning,\n} from \"@ai-sdk/provider\";\nimport {\n type LanguageModelV2,\n type LanguageModelV2CallOptions,\n type LanguageModelV2CallWarning,\n type LanguageModelV2Content,\n type LanguageModelV2FinishReason,\n type LanguageModelV2FunctionTool,\n type LanguageModelV2Message,\n type LanguageModelV2ProviderDefinedTool,\n type LanguageModelV2ResponseMetadata,\n type LanguageModelV2Usage,\n type SharedV2Headers,\n type SharedV2ProviderMetadata,\n} from \"@ai-sdk/provider-v2\";\nimport type {\n CacheServerResponse,\n LanguageModelTextBlock,\n LanguageModelToolDefinitionOverride,\n} from '@lmnr-ai/types';\n\nimport { Laminar } from \"../../../laminar\";\nimport { stringifyPromptForTelemetry } from \"./utils\";\n\n/**\n * Base class for Laminar language model wrappers.\n * Implements shared caching and override logic for both V2 and V3 specifications.\n * Uses method overloads for type safety across versions.\n */\nexport abstract class BaseLaminarLanguageModel {\n protected readonly innerLanguageModel: LanguageModelV2 | LanguageModelV3;\n readonly provider: string;\n readonly modelId: string;\n readonly supportedUrls: PromiseLike<Record<string, RegExp[]>> | Record<string, RegExp[]>;\n\n private pathToCount: Record<string, number> = {};\n private pathToCurrentIndex: Record<string, number> = {};\n private overrides: Record<string, {\n system?: string | Array<{ type: 'text'; text: string }>;\n tools?: Array<{ name: string; description?: string; parameters?: Record<string, any> }>;\n }> = {};\n private initialized: boolean = false;\n\n constructor(languageModel: LanguageModelV2 | LanguageModelV3) {\n this.innerLanguageModel = languageModel;\n this.provider = languageModel.provider;\n this.modelId = languageModel.modelId;\n this.supportedUrls = languageModel.supportedUrls;\n }\n\n /**\n * Creates a version-specific usage object.\n * V2 and V3 have different usage structures, so this must be implemented by subclasses.\n */\n protected abstract createUsageObject(): LanguageModelV2Usage | LanguageModelV3Usage;\n\n /**\n * Creates a version-specific stream from cached response data.\n * V2 and V3 have different stream part types, so this must be implemented by subclasses.\n */\n protected abstract createStreamFromCachedResponse(\n content: Array<LanguageModelV2Content | LanguageModelV3Content>,\n finishReason: LanguageModelV2FinishReason | LanguageModelV3FinishReason,\n usage: LanguageModelV2Usage | LanguageModelV3Usage\n ): ReadableStream<any>;\n\n /**\n * Main generation method with caching support for V2\n */\n protected doGenerateWithCaching(\n options: LanguageModelV2CallOptions,\n doGenerateFn: (opts: LanguageModelV2CallOptions) => PromiseLike<{\n content: Array<LanguageModelV2Content>;\n finishReason: LanguageModelV2FinishReason;\n usage: LanguageModelV2Usage;\n providerMetadata?: SharedV2ProviderMetadata;\n request?: { body?: unknown };\n response?: LanguageModelV2ResponseMetadata & { headers?: SharedV2Headers; body?: unknown };\n warnings: Array<LanguageModelV2CallWarning>;\n }>\n ): PromiseLike<{\n content: Array<LanguageModelV2Content>;\n finishReason: LanguageModelV2FinishReason;\n usage: LanguageModelV2Usage;\n providerMetadata?: SharedV2ProviderMetadata;\n request?: { body?: unknown };\n response?: LanguageModelV2ResponseMetadata & { headers?: SharedV2Headers; body?: unknown };\n warnings: Array<LanguageModelV2CallWarning>;\n }>;\n\n /**\n * Main generation method with caching support for V3\n */\n protected doGenerateWithCaching(\n options: LanguageModelV3CallOptions,\n doGenerateFn: (opts: LanguageModelV3CallOptions) => PromiseLike<{\n content: Array<LanguageModelV3Content>;\n finishReason: LanguageModelV3FinishReason;\n usage: LanguageModelV3Usage;\n providerMetadata?: SharedV3ProviderMetadata;\n request?: { body?: unknown };\n response?: LanguageModelV3ResponseMetadata & { headers?: SharedV3Headers; body?: unknown };\n warnings: Array<SharedV3Warning>;\n }>\n ): PromiseLike<{\n content: Array<LanguageModelV3Content>;\n finishReason: LanguageModelV3FinishReason;\n usage: LanguageModelV3Usage;\n providerMetadata?: SharedV3ProviderMetadata;\n request?: { body?: unknown };\n response?: LanguageModelV3ResponseMetadata & { headers?: SharedV3Headers; body?: unknown };\n warnings: Array<SharedV3Warning>;\n }>;\n\n /**\n * Implementation of doGenerateWithCaching\n * Handles rollout session logic, path tracking, and cache lookups\n */\n protected doGenerateWithCaching(\n options: LanguageModelV2CallOptions | LanguageModelV3CallOptions,\n doGenerateFn: (opts: any) => PromiseLike<any>,\n ): PromiseLike<any> {\n return this.doGenerateOrStreamWithCaching(\n options,\n doGenerateFn,\n this.cachedDoGenerate.bind(this),\n );\n }\n\n /**\n * Main streaming method with caching support for V2\n */\n protected doStreamWithCaching(\n options: LanguageModelV2CallOptions,\n doStreamFn: (opts: LanguageModelV2CallOptions) => PromiseLike<{\n stream: ReadableStream<any>;\n request?: { body?: unknown };\n response?: { headers?: SharedV2Headers };\n }>\n ): PromiseLike<{\n stream: ReadableStream<any>;\n request?: { body?: unknown };\n response?: { headers?: SharedV2Headers };\n }>;\n\n /**\n * Main streaming method with caching support for V3\n */\n protected doStreamWithCaching(\n options: LanguageModelV3CallOptions,\n doStreamFn: (opts: LanguageModelV3CallOptions) => PromiseLike<{\n stream: ReadableStream<any>;\n request?: { body?: unknown };\n response?: { headers?: SharedV3Headers };\n }>\n ): PromiseLike<{\n stream: ReadableStream<any>;\n request?: { body?: unknown };\n response?: { headers?: SharedV3Headers };\n }>;\n\n /**\n * Implementation of doStreamWithCaching\n * Handles rollout session logic, path tracking, and cache lookups for streaming\n */\n protected doStreamWithCaching(\n options: LanguageModelV2CallOptions | LanguageModelV3CallOptions,\n doStreamFn: (opts: any) => PromiseLike<any>,\n ): PromiseLike<any> {\n return this.doGenerateOrStreamWithCaching(\n options,\n doStreamFn,\n this.cachedDoStream.bind(this),\n );\n }\n\n /**\n * Common implementation for both doGenerateWithCaching and doStreamWithCaching.\n * Handles rollout session logic, path tracking, cache lookups, and override application.\n */\n private doGenerateOrStreamWithCaching(\n options: LanguageModelV2CallOptions | LanguageModelV3CallOptions,\n originalFn: (opts: any) => PromiseLike<any>,\n cachedFn: (path: string, index: number) => Promise<\n {\n stream: ReadableStream<any>;\n } | {\n content: Array<LanguageModelV2Content | LanguageModelV3Content>;\n finishReason: LanguageModelV2FinishReason | LanguageModelV3FinishReason;\n usage: LanguageModelV2Usage | LanguageModelV3Usage;\n warnings: Array<LanguageModelV2CallWarning | SharedV3Warning>;\n } | undefined\n >,\n ): PromiseLike<any> {\n const sessionId = process.env.LMNR_ROLLOUT_SESSION_ID;\n if (sessionId) {\n return this.ensureInitialized().then(() => {\n const span = Laminar.getCurrentSpan();\n span?.setAttribute('lmnr.rollout.session_id', sessionId);\n const pathArray = Laminar.getLaminarSpanContext()?.spanPath;\n if (pathArray) {\n const path = pathArray.join('.');\n const currentIndex = this.pathToCurrentIndex[path] ?? 0;\n this.pathToCurrentIndex[path] = currentIndex + 1;\n\n const optionsWithOverrides = this.applyOverrides(path, options);\n // By this time, we are already inside the .doGenerate/.doStream span, and the\n // input attributes (ai.prompt.messages) and ai.prompt.tools have\n // already been set, and so we can override them here.\n span?.setAttribute(\n 'ai.prompt.messages',\n stringifyPromptForTelemetry(optionsWithOverrides.prompt),\n );\n if (optionsWithOverrides.tools && optionsWithOverrides.tools.length > 0) {\n span?.setAttribute(\n 'ai.prompt.tools',\n optionsWithOverrides.tools.map(tool => JSON.stringify(tool)),\n );\n }\n if (this.pathToCount[path] && currentIndex >= this.pathToCount[path]) {\n return originalFn(optionsWithOverrides);\n }\n return cachedFn(path, currentIndex).then(cachedResult => {\n if (cachedResult) {\n return cachedResult;\n }\n return originalFn(optionsWithOverrides);\n });\n }\n return originalFn(options);\n });\n }\n return originalFn(options);\n }\n\n /**\n * Ensures metadata is initialized by fetching (pathToCount, overrides) from cache server.\n * Only runs once per instance since each run event spawns a new worker process.\n */\n private async ensureInitialized(): Promise<void> {\n if (this.initialized) {\n return;\n }\n\n // Initialize by fetching metadata from cache server\n await this.fetchInitialMetadata();\n this.initialized = true;\n }\n\n /**\n * Fetches initial metadata (pathToCount, overrides) from cache server.\n */\n private async fetchInitialMetadata(): Promise<void> {\n const data = await this.fetchCachedSpan('', 0);\n if (data) {\n this.pathToCount = data.pathToCount;\n if (data.overrides) {\n this.overrides = data.overrides;\n }\n }\n }\n\n /**\n * Fetches and parses cached span data into content blocks, usage, and finish reason.\n * Updates pathToCount and overrides as a side effect.\n */\n private async fetchAndParseCachedSpan(\n path: string,\n index: number,\n ): Promise<undefined | {\n content: Array<LanguageModelV2Content | LanguageModelV3Content>;\n finishReason: LanguageModelV2FinishReason | LanguageModelV3FinishReason;\n usage: LanguageModelV2Usage | LanguageModelV3Usage;\n }> {\n const data = await this.fetchCachedSpan(path, index);\n if (!data) {\n return;\n }\n\n // Update pathToCount and overrides from response (dynamic)\n this.pathToCount = data.pathToCount;\n if (data.overrides) {\n this.overrides = data.overrides;\n }\n\n if (!data.span) {\n return;\n }\n\n // Parse output\n let parsedOutput: string | Record<string, any>[] = data.span.output;\n try {\n parsedOutput = JSON.parse(data.span.output);\n } catch {\n // Ignore - keep as string\n }\n\n const currentSpan = Laminar.getCurrentSpan();\n if (currentSpan) {\n currentSpan.setAttributes({\n 'lmnr.span.type': 'CACHED',\n 'lmnr.span.original_type': 'LLM',\n 'lmnr.rollout.session_id': process.env.LMNR_ROLLOUT_SESSION_ID,\n 'lmnr.rollout.path.count': index + 1,\n });\n }\n\n const content = this.convertToContentBlocks(parsedOutput);\n const usage = this.createUsageObject();\n const finishReason = data.span.attributes['ai.response.finishReason'] ?? 'stop';\n\n return { content, usage, finishReason };\n }\n\n private async cachedDoGenerate(path: string, index: number): Promise<undefined | {\n content: Array<LanguageModelV2Content | LanguageModelV3Content>;\n finishReason: LanguageModelV2FinishReason | LanguageModelV3FinishReason;\n usage: LanguageModelV2Usage | LanguageModelV3Usage;\n warnings: Array<LanguageModelV2CallWarning | SharedV3Warning>;\n }> {\n const parsed = await this.fetchAndParseCachedSpan(path, index);\n if (!parsed) {\n return;\n }\n\n return {\n ...parsed,\n warnings: [],\n };\n }\n\n private async cachedDoStream(path: string, index: number): Promise<undefined | {\n stream: ReadableStream<any>;\n }> {\n const parsed = await this.fetchAndParseCachedSpan(path, index);\n if (!parsed) {\n return;\n }\n\n // Create stream using version-specific implementation\n const stream = this.createStreamFromCachedResponse(\n parsed.content,\n parsed.finishReason,\n parsed.usage,\n );\n\n return { stream };\n }\n\n private applyOverrides(\n path: string,\n options: LanguageModelV2CallOptions | LanguageModelV3CallOptions,\n ): LanguageModelV2CallOptions | LanguageModelV3CallOptions {\n const pathOverride = this.overrides[path];\n if (!pathOverride) {\n return options;\n }\n\n const modifiedOptions = {\n ...options,\n } as LanguageModelV2CallOptions | LanguageModelV3CallOptions;\n\n // Apply system override\n const systemOverride = this.normalizeSystemOverride(pathOverride.system);\n if (systemOverride) {\n modifiedOptions.prompt =\n this.applySystemOverride(modifiedOptions.prompt, systemOverride) as any;\n }\n\n // Apply tool overrides\n if (pathOverride.tools) {\n modifiedOptions.tools =\n this.applyToolOverrides(modifiedOptions.tools, pathOverride.tools) as any;\n }\n\n return modifiedOptions;\n }\n\n /**\n * Converts output from span to content blocks compatible with both V2 and V3\n */\n private convertToContentBlocks(\n output: string | Record<string, any>[],\n ): Array<LanguageModelV3Content | LanguageModelV2Content> {\n if (typeof output === 'string') {\n return [{\n type: 'text',\n text: output,\n }];\n }\n\n const handleItem = (item: Record<string, any>): LanguageModelV3Content[] => {\n if (item.type === 'text') {\n return [{\n type: 'text',\n text: item.text ?? '',\n }];\n }\n if (['tool-call', 'tool_call'].includes(item.type)) {\n return [{\n type: 'tool-call',\n toolCallId: item.toolCallId ?? item.id,\n toolName: item.toolName ?? item.name,\n input: JSON.stringify(item.input ?? item.arguments),\n }];\n }\n if (item.type === 'reasoning') {\n return [{\n type: 'reasoning',\n text: item.text ?? '',\n }];\n }\n return [{\n type: 'text',\n text: JSON.stringify(item),\n }];\n };\n\n return output.flatMap(item => {\n if (item.role && item.content) {\n let parsedContent: Record<string, any>[] = item.content;\n try {\n parsedContent = JSON.parse(item.content);\n } catch {\n if (typeof item === 'string') {\n return [{\n type: 'text',\n text: item,\n }];\n }\n }\n return parsedContent.flatMap(handleItem);\n }\n return handleItem(item);\n });\n }\n\n /**\n * Fetches cached span data from the local rollout cache server\n */\n private async fetchCachedSpan(\n path: string,\n index: number,\n ): Promise<CacheServerResponse | undefined> {\n const serverUrl = process.env.LMNR_ROLLOUT_STATE_SERVER_ADDRESS;\n if (!serverUrl) {\n return;\n }\n\n try {\n const response = await fetch(`${serverUrl}/cached`, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify({ path, index }),\n });\n\n if (!response.ok) {\n // 404 means cache miss\n return;\n }\n\n return await response.json() as CacheServerResponse;\n } catch {\n // Network error or other issues - return undefined to fall back to original model\n return;\n }\n }\n\n /**\n * Normalizes system override to a string\n */\n private normalizeSystemOverride(\n system: string | LanguageModelTextBlock[] | undefined,\n ): string | undefined {\n if (!system) {\n return undefined;\n }\n\n if (typeof system === 'string') {\n return system;\n }\n\n // Join text blocks into a single string\n return system.map(block => block.text ?? '').join('\\n');\n }\n\n /**\n * Applies system message override to prompt\n */\n private applySystemOverride(\n prompt: Array<LanguageModelV2Message | LanguageModelV3Message>,\n systemOverride: string | undefined,\n ): Array<LanguageModelV2Message | LanguageModelV3Message> {\n if (!systemOverride) {\n return prompt;\n }\n\n // Filter out existing system messages\n const withoutSystem = prompt.filter(msg => msg.role !== 'system');\n\n // Add override as first message\n return [\n { role: 'system', content: systemOverride },\n ...withoutSystem,\n ];\n }\n\n /**\n * Applies tool overrides\n */\n private applyToolOverrides<\n F extends LanguageModelV2FunctionTool | LanguageModelV3FunctionTool,\n P extends LanguageModelV2ProviderDefinedTool | LanguageModelV3ProviderTool,\n >(\n tools: Array<F | P> | undefined,\n toolOverrides: LanguageModelToolDefinitionOverride[] | undefined,\n ): Array<F | P> | undefined {\n if (!toolOverrides || toolOverrides.length === 0) {\n return tools;\n }\n\n if (!tools || tools.length === 0) {\n // If no tools exist, create new tools from overrides that have inputSchema\n const newTools = [];\n for (const override of toolOverrides) {\n if (override.parameters) {\n newTools.push({\n type: 'function',\n name: override.name,\n description: override.description,\n inputSchema: override.parameters as JSONSchema7,\n });\n }\n }\n return newTools.length > 0 ? newTools as Array<F | P> : undefined;\n }\n\n const updatedTools = [...tools] as Array<F>;\n\n for (const override of toolOverrides) {\n const existingToolIndex = updatedTools.findIndex(\n tool => tool.type === 'function' && tool.name === override.name,\n );\n\n if (existingToolIndex !== -1) {\n const existingTool = updatedTools[existingToolIndex];\n // Merge with existing function tool (override takes priority)\n updatedTools[existingToolIndex] = {\n ...existingTool,\n description: override.description ?? existingTool.description,\n inputSchema: override.parameters\n ? (override.parameters as JSONSchema7)\n : (existingTool.inputSchema),\n };\n } else if (override.parameters) {\n // Add new tool if it has inputSchema\n updatedTools.push({\n type: 'function',\n name: override.name,\n description: override.description,\n inputSchema: override.parameters as JSONSchema7,\n } as F);\n }\n }\n\n return updatedTools;\n }\n}\n","import {\n type LanguageModelV2,\n type LanguageModelV2CallOptions,\n type LanguageModelV2CallWarning,\n type LanguageModelV2Content,\n type LanguageModelV2FinishReason,\n type LanguageModelV2ResponseMetadata,\n type LanguageModelV2StreamPart,\n type LanguageModelV2Usage,\n type SharedV2Headers,\n type SharedV2ProviderMetadata,\n} from \"@ai-sdk/provider-v2\";\n\nimport { BaseLaminarLanguageModel } from \"./base-language-model\";\n\nexport class LaminarLanguageModelV2 extends BaseLaminarLanguageModel implements LanguageModelV2 {\n readonly specificationVersion = 'v2';\n\n protected readonly innerLanguageModel: LanguageModelV2;\n\n constructor(languageModel: LanguageModelV2) {\n super(languageModel);\n this.innerLanguageModel = languageModel;\n }\n\n protected createUsageObject(): LanguageModelV2Usage {\n return {\n inputTokens: 0,\n outputTokens: 0,\n totalTokens: 0,\n };\n }\n\n doGenerate(options: LanguageModelV2CallOptions): PromiseLike<{\n content: Array<LanguageModelV2Content>;\n finishReason: LanguageModelV2FinishReason;\n usage: LanguageModelV2Usage;\n providerMetadata?: SharedV2ProviderMetadata;\n request?: {\n body?: unknown;\n };\n response?: LanguageModelV2ResponseMetadata & {\n headers?: SharedV2Headers;\n body?: unknown;\n };\n warnings: Array<LanguageModelV2CallWarning>;\n }> {\n return this.doGenerateWithCaching(\n options,\n (opts) => this.innerLanguageModel.doGenerate(opts),\n );\n }\n\n doStream(options: LanguageModelV2CallOptions): PromiseLike<{\n stream: ReadableStream<LanguageModelV2StreamPart>;\n request?: {\n body?: unknown;\n };\n response?: {\n headers?: SharedV2Headers;\n };\n }> {\n return this.doStreamWithCaching(\n options,\n (opts) => this.innerLanguageModel.doStream(opts),\n );\n }\n\n protected createStreamFromCachedResponse(\n content: Array<LanguageModelV2Content>,\n finishReason: LanguageModelV2FinishReason,\n usage: LanguageModelV2Usage,\n ): ReadableStream<LanguageModelV2StreamPart> {\n const parts: LanguageModelV2StreamPart[] = [];\n\n // Stream start\n parts.push({ type: 'stream-start', warnings: [] });\n\n // Process each content block\n let textIndex = 0;\n let toolIndex = 0;\n let reasoningIndex = 0;\n\n for (const block of content) {\n if (block.type === 'text') {\n const id = `text-${textIndex++}`;\n parts.push({ type: 'text-start', id });\n parts.push({ type: 'text-delta', id, delta: block.text });\n parts.push({ type: 'text-end', id });\n } else if (block.type === 'tool-call') {\n const id = `tool-${toolIndex++}`;\n parts.push({\n type: 'tool-input-start',\n id,\n toolName: block.toolName,\n });\n parts.push({\n type: 'tool-input-delta',\n id,\n delta: block.input,\n });\n parts.push({ type: 'tool-input-end', id });\n parts.push({\n type: 'tool-call',\n toolCallId: block.toolCallId,\n toolName: block.toolName,\n input: block.input,\n });\n } else if (block.type === 'reasoning') {\n const id = `reasoning-${reasoningIndex++}`;\n parts.push({ type: 'reasoning-start', id });\n parts.push({ type: 'reasoning-delta', id, delta: block.text });\n parts.push({ type: 'reasoning-end', id });\n }\n }\n\n // Finish event\n parts.push({ type: 'finish', usage, finishReason });\n\n // Create readable stream from the parts array\n return new ReadableStream({\n start(controller) {\n for (const part of parts) {\n controller.enqueue(part);\n }\n controller.close();\n },\n });\n }\n}\n","import {\n type LanguageModelV3,\n type LanguageModelV3CallOptions,\n type LanguageModelV3Content,\n type LanguageModelV3FinishReason,\n type LanguageModelV3ResponseMetadata,\n type LanguageModelV3StreamPart,\n type LanguageModelV3Usage,\n type SharedV3Headers,\n type SharedV3ProviderMetadata,\n type SharedV3Warning,\n} from \"@ai-sdk/provider\";\n\nimport { BaseLaminarLanguageModel } from \"./base-language-model\";\n\nexport class LaminarLanguageModelV3 extends BaseLaminarLanguageModel implements LanguageModelV3 {\n readonly specificationVersion = 'v3';\n\n protected readonly innerLanguageModel: LanguageModelV3;\n\n constructor(languageModel: LanguageModelV3) {\n super(languageModel);\n this.innerLanguageModel = languageModel;\n }\n\n protected createUsageObject(): LanguageModelV3Usage {\n return {\n inputTokens: {\n total: 0,\n noCache: 0,\n cacheRead: 0,\n cacheWrite: 0,\n },\n outputTokens: {\n total: 0,\n text: 0,\n reasoning: 0,\n },\n };\n }\n\n doGenerate(options: LanguageModelV3CallOptions): PromiseLike<{\n content: Array<LanguageModelV3Content>;\n finishReason: LanguageModelV3FinishReason;\n usage: LanguageModelV3Usage;\n providerMetadata?: SharedV3ProviderMetadata;\n request?: {\n body?: unknown;\n };\n response?: LanguageModelV3ResponseMetadata & {\n headers?: SharedV3Headers;\n body?: unknown;\n };\n warnings: Array<SharedV3Warning>;\n }> {\n return this.doGenerateWithCaching(\n options,\n (opts) => this.innerLanguageModel.doGenerate(opts),\n );\n }\n\n doStream(options: LanguageModelV3CallOptions): PromiseLike<{\n stream: ReadableStream<LanguageModelV3StreamPart>;\n request?: {\n body?: unknown;\n };\n response?: {\n headers?: SharedV3Headers;\n };\n }> {\n return this.doStreamWithCaching(\n options,\n (opts) => this.innerLanguageModel.doStream(opts),\n );\n }\n\n protected createStreamFromCachedResponse(\n content: Array<LanguageModelV3Content>,\n finishReason: LanguageModelV3FinishReason,\n usage: LanguageModelV3Usage,\n ): ReadableStream<LanguageModelV3StreamPart> {\n const parts: LanguageModelV3StreamPart[] = [];\n\n // Stream start\n parts.push({ type: 'stream-start', warnings: [] });\n\n // Process each content block\n let textIndex = 0;\n let toolIndex = 0;\n let reasoningIndex = 0;\n\n for (const block of content) {\n if (block.type === 'text') {\n const id = `text-${textIndex++}`;\n parts.push({ type: 'text-start', id });\n parts.push({ type: 'text-delta', id, delta: block.text });\n parts.push({ type: 'text-end', id });\n } else if (block.type === 'tool-call') {\n const id = `tool-${toolIndex++}`;\n parts.push({\n type: 'tool-input-start',\n id,\n toolName: block.toolName,\n });\n parts.push({\n type: 'tool-input-delta',\n id,\n delta: block.input,\n });\n parts.push({ type: 'tool-input-end', id });\n parts.push({\n type: 'tool-call',\n toolCallId: block.toolCallId,\n toolName: block.toolName,\n input: block.input,\n });\n } else if (block.type === 'reasoning') {\n const id = `reasoning-${reasoningIndex++}`;\n parts.push({ type: 'reasoning-start', id });\n parts.push({ type: 'reasoning-delta', id, delta: block.text });\n parts.push({ type: 'reasoning-end', id });\n }\n }\n\n // Finish event\n parts.push({ type: 'finish', usage, finishReason });\n\n // Create readable stream from the parts array\n return new ReadableStream({\n start(controller) {\n for (const part of parts) {\n controller.enqueue(part);\n }\n controller.close();\n },\n });\n }\n}\n","import { type LanguageModelV3 } from \"@ai-sdk/provider\";\nimport { type LanguageModelV2 } from \"@ai-sdk/provider-v2\";\nimport type * as AI from \"ai\";\n\nimport { getTracer } from \"../../tracing\";\nimport { LaminarLanguageModelV2 } from \"./v2\";\nimport { LaminarLanguageModelV3 } from \"./v3\";\n\nconst AI_FUNCTIONS = [\n 'generateText',\n 'generateObject',\n 'streamText',\n 'streamObject',\n 'embed',\n 'embedMany',\n];\n\nexport const wrapAISDK = (ai: typeof AI): typeof AI => {\n const wrapped: Record<string, any> = {};\n Object.entries(ai).forEach(([key, value]) => {\n if (typeof value === 'function' && AI_FUNCTIONS.includes(key)) {\n const originalFn = value as (...args: any[]) => any;\n wrapped[key] = (...args: any[]) => {\n if (args[0] && typeof args[0] === 'object') {\n const defaultTelemetry = {\n isEnabled: true,\n tracer: getTracer(),\n };\n args[0] = {\n ...args[0],\n experimental_telemetry: {\n ...defaultTelemetry,\n ...args[0].experimental_telemetry,\n },\n };\n }\n // eslint-disable-next-line @typescript-eslint/no-unsafe-return\n return originalFn(...args);\n };\n } else {\n wrapped[key] = value;\n }\n });\n return wrapped as typeof AI;\n};\n\nexport function wrapLanguageModel(languageModel: LanguageModelV3): LanguageModelV3;\nexport function wrapLanguageModel(languageModel: LanguageModelV2): LanguageModelV2;\nexport function wrapLanguageModel(\n languageModel: LanguageModelV2 | LanguageModelV3,\n): LanguageModelV2 | LanguageModelV3 {\n if (languageModel.specificationVersion === 'v3') {\n return new LaminarLanguageModelV3(languageModel);\n }\n return new LaminarLanguageModelV2(languageModel);\n}\n","// Narrow structural types for Mastra's observability contract. Declared\n// locally to avoid a hard runtime/peer dep on @mastra/core. Also includes\n// the slice of AI SDK message shape we serialize into `ai.prompt.messages`.\n\nexport type LaminarSpanType = \"LLM\" | \"TOOL\" | \"DEFAULT\";\n\n// Mirror of the subset of `@mastra/core`'s `SpanType` enum we care about.\n// Declared locally so the exporter has no peer dep on Mastra. Mastra may\n// emit other span types we don't branch on — `MastraExportedSpan.type` stays\n// `string` to keep forward-compat, but every comparison in the exporter\n// should use this constant so the set of consumed types is centralized.\n//\n// Defined as a `const` object (not a TS `enum`) so equality checks against\n// `MastraExportedSpan.type: string` don't trip `no-unsafe-enum-comparison`.\nexport const MastraSpanType = {\n MODEL_GENERATION: \"model_generation\",\n MODEL_STEP: \"model_step\",\n MODEL_CHUNK: \"model_chunk\",\n TOOL_CALL: \"tool_call\",\n MCP_TOOL_CALL: \"mcp_tool_call\",\n} as const;\nexport type MastraSpanType =\n (typeof MastraSpanType)[keyof typeof MastraSpanType];\n\nexport interface MastraUsageStats {\n inputTokens?: number;\n outputTokens?: number;\n inputDetails?: {\n cacheRead?: number;\n cacheWrite?: number;\n };\n outputDetails?: {\n reasoning?: number;\n };\n}\n\nexport interface MastraSpanErrorInfo {\n message: string;\n name?: string;\n stack?: string;\n details?: Record<string, unknown>;\n}\n\nexport interface MastraExportedSpan {\n id: string;\n traceId: string;\n parentSpanId?: string;\n name: string;\n type: string;\n startTime: Date;\n endTime?: Date;\n attributes?: Record<string, any>;\n metadata?: Record<string, any>;\n tags?: string[];\n input?: unknown;\n output?: unknown;\n errorInfo?: MastraSpanErrorInfo;\n isEvent: boolean;\n isRootSpan: boolean;\n}\n\nexport interface MastraTracingEvent {\n type: \"span_started\" | \"span_updated\" | \"span_ended\";\n exportedSpan: MastraExportedSpan;\n}\n\nexport interface MastraExporterOptions {\n /**\n * Flush the underlying span processor after every span end. Useful for\n * short-lived processes that exit before the batch processor would drain\n * on its own.\n */\n realtime?: boolean;\n /**\n * When true (default), reparent Mastra traces under the caller's active\n * OpenTelemetry span if one exists. This lets `observe()`-wrapped code that\n * calls a Mastra agent produce a single unified trace instead of two\n * disconnected ones (user's OTel trace + Mastra's own trace).\n *\n * Mastra does not propagate OTel context into its event bus, so without\n * this we'd emit under Mastra's self-assigned trace id with no parent.\n * Set to false to preserve Mastra's original trace id even when nested\n * inside `observe()`.\n */\n linkToActiveContext?: boolean;\n}\n\nexport interface AISdkContentPart {\n type: string;\n text?: string;\n toolCallId?: string;\n toolName?: string;\n input?: unknown;\n output?: unknown;\n}\n\nexport interface AISdkMessage {\n role: string;\n content: string | AISdkContentPart[];\n tool_call_id?: string;\n}\n","import { HrTime } from \"@opentelemetry/api\";\n\nimport { LaminarAttributes } from \"../../tracing/attributes\";\nimport {\n AISdkContentPart,\n AISdkMessage,\n LaminarSpanType,\n MastraExportedSpan,\n MastraSpanType,\n MastraUsageStats,\n} from \"./types\";\n\nexport const dateToHrTime = (date: Date): HrTime => {\n const ms = date.getTime();\n const seconds = Math.floor(ms / 1e3);\n const nanoseconds = (ms % 1e3) * 1e6;\n return [seconds, nanoseconds];\n};\n\nexport const normalizeProvider = (provider: string): string =>\n provider.split(\".\").shift()?.toLowerCase().trim() ||\n provider.toLowerCase().trim();\n\n// `model_step` is Laminar's atomic LLM call. `model_generation` wraps the\n// whole agent-to-LLM flow (which may include multiple steps + tool calls),\n// so it stays DEFAULT.\nexport const mapLaminarSpanType = (spanType: string): LaminarSpanType => {\n switch (spanType) {\n case MastraSpanType.MODEL_STEP:\n return \"LLM\";\n case MastraSpanType.TOOL_CALL:\n case MastraSpanType.MCP_TOOL_CALL:\n return \"TOOL\";\n default:\n return \"DEFAULT\";\n }\n};\n\nexport const serializeJSON = (value: unknown): string => {\n if (typeof value === \"string\") return value;\n try {\n return JSON.stringify(value);\n } catch {\n return \"[unserializable]\";\n }\n};\n\ntype AttrPrimitive = string | number | boolean;\nexport const toAttributeValue = (\n value: unknown,\n): AttrPrimitive | AttrPrimitive[] | undefined => {\n if (value === undefined || value === null) return undefined;\n if (\n typeof value === \"string\" ||\n typeof value === \"number\" ||\n typeof value === \"boolean\"\n ) {\n return value;\n }\n if (Array.isArray(value)) {\n if (value.every((v) => typeof v === \"string\")) return value;\n if (value.every((v) => typeof v === \"number\")) return value;\n if (value.every((v) => typeof v === \"boolean\")) return value;\n }\n return serializeJSON(value);\n};\n\nexport const extractMessages = (input: unknown): unknown[] | undefined => {\n if (!input) return undefined;\n if (Array.isArray(input)) return input as unknown[];\n if (typeof input === \"object\") {\n const asObj = input as Record<string, unknown>;\n if (Array.isArray(asObj.messages)) return asObj.messages as unknown[];\n if (Array.isArray(asObj.prompt)) return asObj.prompt as unknown[];\n if (Array.isArray(asObj.input)) return asObj.input as unknown[];\n }\n return undefined;\n};\n\n// `MODEL_GENERATION.input` is the messages array the user passed into\n// `agent.generate(...)`, forwarded by Mastra to the AI SDK unchanged. It's\n// already in AI SDK shape by construction — the AI SDK would have rejected\n// it before observability fired otherwise. Just coerce non-object items to\n// a string user message so `ai.prompt.messages` always serializes as a\n// well-formed message list.\nexport const normalizeInputMessages = (raw: unknown[]): AISdkMessage[] => {\n const out: AISdkMessage[] = [];\n for (const m of raw) {\n if (m == null) continue;\n if (typeof m === \"object\") {\n out.push(m as AISdkMessage);\n continue;\n }\n out.push({\n role: \"user\",\n content: typeof m === \"string\" ? m : serializeJSON(m),\n });\n }\n return out;\n};\n\n// Synthesizes the assistant turn cached on the surrounding MODEL_GENERATION\n// and replayed in the next step's `ai.prompt.messages`. Reasoning text is\n// intentionally omitted — providers don't re-send it in subsequent turns.\nexport const buildAssistantMessageFromStepOutput = (\n output: unknown,\n): AISdkMessage | null => {\n if (!output || typeof output !== \"object\") return null;\n const outObj = output as Record<string, unknown>;\n const parts: AISdkContentPart[] = [];\n if (typeof outObj.text === \"string\" && outObj.text.length > 0) {\n parts.push({ type: \"text\", text: outObj.text });\n }\n if (Array.isArray(outObj.toolCalls)) {\n for (const tc of outObj.toolCalls as Array<Record<string, unknown>>) {\n if (!tc) continue;\n parts.push({\n type: \"tool-call\",\n toolCallId: tc.toolCallId as string | undefined,\n toolName: tc.toolName as string | undefined,\n // `args` is the legacy AI SDK v4 name, `input` is v5+; keep both\n // until we drop v4 support.\n input: tc.input ?? tc.args,\n });\n }\n }\n if (parts.length === 0) return null;\n return { role: \"assistant\", content: parts };\n};\n\nexport const extractToolCallId = (\n span: MastraExportedSpan,\n): string | undefined => {\n const v = span.attributes?.toolCallId;\n return typeof v === \"string\" ? v : undefined;\n};\n\n// Mastra names tool spans like `tool: 'myToolId'` or, for MCP tools,\n// `mcp_tool: 'toolId' on 'serverName'` (see @mastra/core buildSpanName).\n// Strip that prefix + quotes (and the trailing ` on '<server>'` for MCP) so\n// both the TOOL span's `ai.toolCall.name` and the tool-result message's\n// `toolName` agree on the raw tool id.\nexport const cleanMastraToolName = (\n name: string | undefined,\n): string | undefined =>\n name?.replace(/^(?:mcp_tool|tool):\\s*'?(.*?)'?(?:\\s+on\\s+'[^']*')?$/, \"$1\");\n\n// Tool-call args are sometimes a string (already-serialized JSON), sometimes\n// an object — stringify the latter so `ai.response.toolCalls` is always a\n// JSON-string at the AI SDK boundary. Mirror `serializeJSON`'s guard so\n// unserializable inputs (BigInt, circular refs) degrade the arg payload\n// rather than bubbling up and causing the caller's outer catch to drop the\n// whole LLM span.\nexport const stringifyArgs = (raw: unknown): string | undefined => {\n if (raw === undefined) return undefined;\n if (typeof raw === \"string\") return raw;\n try {\n return JSON.stringify(raw);\n } catch {\n return \"[unserializable]\";\n }\n};\n\nexport const formatUsage = (\n usage?: MastraUsageStats,\n): Record<string, number> => {\n if (!usage) return {};\n const out: Record<string, number> = {};\n if (typeof usage.inputTokens === \"number\") {\n out[LaminarAttributes.INPUT_TOKEN_COUNT] = usage.inputTokens;\n }\n if (typeof usage.outputTokens === \"number\") {\n out[LaminarAttributes.OUTPUT_TOKEN_COUNT] = usage.outputTokens;\n }\n const total = (usage.inputTokens ?? 0) + (usage.outputTokens ?? 0);\n if (total > 0) {\n out[LaminarAttributes.TOTAL_TOKEN_COUNT] = total;\n }\n if (typeof usage.inputDetails?.cacheWrite === \"number\") {\n out[\"gen_ai.usage.cache_creation_input_tokens\"] =\n usage.inputDetails.cacheWrite;\n }\n if (typeof usage.inputDetails?.cacheRead === \"number\") {\n out[\"gen_ai.usage.cache_read_input_tokens\"] = usage.inputDetails.cacheRead;\n }\n if (typeof usage.outputDetails?.reasoning === \"number\") {\n out[\"gen_ai.usage.reasoning_tokens\"] = usage.outputDetails.reasoning;\n }\n return out;\n};\n","import {\n Context,\n context as contextApi,\n Span,\n SpanContext,\n SpanKind,\n SpanStatusCode,\n trace,\n TraceFlags,\n} from \"@opentelemetry/api\";\n\nimport { version as SDK_VERSION } from \"../../../../package.json\";\nimport {\n initializeLogger,\n normalizeOtelSpanId,\n normalizeOtelTraceId,\n otelSpanIdToUUID,\n} from \"../../../utils\";\nimport { getLangVersion } from \"../../../version\";\nimport {\n ASSOCIATION_PROPERTIES,\n LaminarAttributes,\n SESSION_ID,\n SPAN_IDS_PATH,\n SPAN_INPUT,\n SPAN_INSTRUMENTATION_SOURCE,\n SPAN_LANGUAGE_VERSION,\n SPAN_OUTPUT,\n SPAN_PATH,\n SPAN_SDK_VERSION,\n SPAN_TYPE,\n USER_ID,\n} from \"../../tracing/attributes\";\nimport { LaminarContextManager } from \"../../tracing/context\";\nimport { getSpanProcessor, getTracerProvider } from \"../../tracing/index\";\nimport {\n AISdkMessage,\n MastraExportedSpan,\n MastraExporterOptions,\n MastraSpanType,\n MastraTracingEvent,\n MastraUsageStats,\n} from \"./types\";\nimport {\n buildAssistantMessageFromStepOutput,\n cleanMastraToolName,\n dateToHrTime,\n extractMessages,\n extractToolCallId,\n formatUsage,\n mapLaminarSpanType,\n normalizeInputMessages,\n normalizeProvider,\n serializeJSON,\n stringifyArgs,\n toAttributeValue,\n} from \"./utils\";\n\nconst logger = initializeLogger();\n\ninterface TraceState {\n spanPathById: Map<string, string[]>;\n spanIdsPathById: Map<string, string[]>;\n activeSpanIds: Set<string>;\n // When the first event for this Mastra trace arrived inside an active OTel\n // span (e.g. a user's `observe()` wrapper), we rewrite outgoing spans onto\n // that OTel trace so the whole thing renders as a single trace. Stored\n // once, at first-event time, and reused for every span in the trace.\n otelTraceId?: string;\n otelRootParentSpanId?: string;\n // Outer span's SPAN_PATH / SPAN_IDS_PATH, captured from the active\n // LaminarSpan's attributes at first-event time. Prepended to every Mastra\n // span's path so Laminar's UI (which builds its hierarchy from\n // `lmnr.span.ids_path`, NOT OTel parent-span relationships) renders the\n // Mastra subtree nested under the outer `observe()` span rather than as a\n // sibling on the same trace.\n otelParentSpanPath?: string[];\n otelParentSpanIdsPath?: string[];\n}\n\ninterface ToolCallChild {\n // Tool-call identifier extracted from the tool_call span (attributes/input)\n // when available. Used to pair results with the step's declared toolCalls\n // by id — necessary when tools execute in parallel and finish out of\n // declaration order. Falls back to arrival-order pairing when absent.\n toolCallId?: string;\n toolName: string;\n input: unknown;\n output: unknown;\n}\n\ninterface GenerationState {\n // Messages as originally passed into the generation (system + user).\n baseMessages: AISdkMessage[];\n // Per-step conversation turn (assistant message + tool-result messages).\n // Assembled at step-end, keyed by stepIndex so we can replay history for\n // step N as [baseMessages, ...turns in order].\n turnsByStepIndex: Map<number, AISdkMessage[]>;\n // Ordered tool_call children per step — collected as they end (always\n // before their parent MODEL_STEP ends) so we can pair them by arrival\n // order with the step's declared `toolCalls` when the step itself ends.\n toolCallChildrenByStepIndex: Map<number, ToolCallChild[]>;\n // Accumulated reasoning text per step, built up from MODEL_CHUNK children\n // with `chunkType: \"reasoning\"`. Mastra drops reasoning from\n // `MODEL_STEP.output`/`attributes` entirely (see `#endStepSpan` in\n // `@mastra/observability/dist/index.js`), and `MODEL_GENERATION.output`\n // only carries a flat cross-step `reasoningText` string — neither preserves\n // per-step boundaries. Reasoning chunks DO, and they end before the parent\n // MODEL_STEP ends, so accumulating from them is the only way to surface\n // thinking tokens on the step's LLM span.\n reasoningTextByStepIndex: Map<number, string>;\n}\n\n/**\n * Bridges Mastra's ObservabilityExporter contract to Laminar's OTLP ingestion.\n *\n * Span-type mapping:\n * - `model_step` → LLM (atomic LLM call; rendered with message history).\n * - `tool_call`, `mcp_tool_call` → TOOL.\n * - `model_chunk` → dropped (per-delta, noise).\n * - everything else → DEFAULT.\n *\n * Tool-call reconstruction: Mastra's `extractStepInput` collapses prior tool\n * calls/results into empty user messages on MODEL_STEP inputs, so we\n * reconstruct the full conversation by accumulating (baseMessages → step0's\n * assistant message → tool-result message(s) → step1's assistant message →\n * …) using the parent MODEL_GENERATION's input and the children TOOL_CALL\n * spans paired by arrival order with each step's declared toolCalls.\n *\n * For LLM spans we emit `ai.prompt.messages` (stringified AI SDK messages\n * with tool-call / tool-result content parts) and `ai.response.text` +\n * `ai.response.toolCalls` so Laminar's backend parser threads them into\n * the LLM message history view.\n */\nexport class MastraExporter {\n public readonly name = \"laminar\";\n\n private readonly config: {\n realtime: boolean;\n linkToActiveContext: boolean;\n };\n\n private readonly traceMap: Map<string, TraceState> = new Map();\n private readonly generationStateById: Map<string, GenerationState> =\n new Map();\n private readonly generationAttrsById: Map<string, Record<string, unknown>> =\n new Map();\n private readonly generationIdByStepId: Map<string, string> = new Map();\n // Remember step index per step span so tool_call children can look up which\n // step they belong to (fan-in by arrival order).\n private readonly stepIndexBySpanId: Map<string, number> = new Map();\n // Live OTel spans created via `tracer.startSpan` at span_started time, held\n // until span_ended. Lookups by Mastra span id let us:\n // - apply attributes / status / exception on the live span at end-time;\n // - resolve a Mastra child's parent context to the *mutated* live span,\n // so the SDK threads parent linkage with our Mastra-derived ids.\n private readonly liveOtelSpanByMastraId: Map<string, Span> = new Map();\n\n private warnedNotInitialized = false;\n\n constructor(options: MastraExporterOptions = {}) {\n this.config = {\n realtime: options.realtime ?? false,\n linkToActiveContext: options.linkToActiveContext ?? true,\n };\n }\n\n // Mastra calls `init({ config: { serviceName } })` on registration. We rely\n // on Laminar's tracer provider (set by `Laminar.initialize()`) for span\n // creation and export, so this is a no-op kept only for API compatibility\n // with Mastra's exporter contract.\n // eslint-disable-next-line @typescript-eslint/no-unused-vars\n init(_options?: unknown): void {}\n\n async exportTracingEvent(event: MastraTracingEvent): Promise<void> {\n const span = event.exportedSpan;\n // Point-in-time event spans have no start/end pair and don't map cleanly\n // onto our span-tree model — drop them regardless of phase. The guard must\n // cover span_ended too: otherwise an isEvent span arriving on that phase\n // slips through to handleSpanEnded and gets exported as a real span.\n if (span.isEvent) return;\n\n // Always remember generation attributes so children can resolve them\n // regardless of event ordering. Merge (not overwrite) so a span_updated\n // event carrying only a delta (e.g. just `usage`) can't clobber keys\n // set earlier at span_started (e.g. `provider`, `model`). MODEL_STEP\n // children end before MODEL_GENERATION's span_ended restores the full\n // set, so applyLlmAttributes would otherwise read a partial snapshot.\n if (span.type === MastraSpanType.MODEL_GENERATION && span.attributes) {\n const existing = this.generationAttrsById.get(span.id);\n // Always spread into a fresh object (even on first insert): Mastra\n // owns `span.attributes` and could reuse or mutate it between events,\n // which would silently corrupt the stored snapshot children read at\n // `applyLlmAttributes` time.\n this.generationAttrsById.set(\n span.id,\n existing\n ? { ...existing, ...span.attributes }\n : { ...span.attributes },\n );\n }\n\n if (event.type === \"span_started\") {\n this.handleSpanStarted(span);\n return;\n }\n if (event.type !== \"span_ended\") return;\n await this.handleSpanEnded(span);\n }\n\n async onTracingEvent(event: MastraTracingEvent): Promise<void> {\n await this.exportTracingEvent(event);\n }\n\n async flush(): Promise<void> {\n // Flush Laminar's span processor — the only pipeline our spans flow\n // through. No-op when Laminar isn't initialized.\n const processor = getSpanProcessor();\n if (!processor) return;\n try {\n await processor.forceFlush();\n } catch (err) {\n logger.error(\n `[MastraExporter] forceFlush failed: ${err instanceof Error ? err.message : String(err)}`,\n );\n }\n }\n\n shutdown(): Promise<void> {\n // Don't tear down Laminar's pipeline here — it may still be needed by\n // other instrumentations. Just drop our per-trace bookkeeping.\n this.traceMap.clear();\n this.generationStateById.clear();\n this.generationAttrsById.clear();\n this.generationIdByStepId.clear();\n this.stepIndexBySpanId.clear();\n this.liveOtelSpanByMastraId.clear();\n return Promise.resolve();\n }\n\n private handleSpanStarted(span: MastraExportedSpan): void {\n if (span.type === MastraSpanType.MODEL_CHUNK) return;\n\n const traceState = this.getOrCreateTraceState(span.traceId);\n const parentId = span.parentSpanId;\n\n this.recordSpanPath(span, traceState);\n traceState.activeSpanIds.add(span.id);\n\n if (span.type === MastraSpanType.MODEL_GENERATION) {\n this.initGenerationState(span);\n } else if (span.type === MastraSpanType.MODEL_STEP && parentId) {\n this.generationIdByStepId.set(span.id, parentId);\n const stepIndex =\n typeof span.attributes?.stepIndex === \"number\"\n ? span.attributes.stepIndex\n : 0;\n this.stepIndexBySpanId.set(span.id, stepIndex);\n }\n\n this.startOtelSpan(span, traceState);\n }\n\n // Build `spanPath` / `spanIdsPath` for a Mastra span and store them on the\n // trace state. For Mastra roots we seed from the captured outer observe()\n // path so Laminar's UI nests the subtree underneath; for Mastra descendants\n // we reuse the parent Mastra span's stored path (already has the outer\n // path prepended).\n private recordSpanPath(\n span: MastraExportedSpan,\n traceState: TraceState,\n ): void {\n const parentId = span.parentSpanId;\n const parentPath = parentId\n ? traceState.spanPathById.get(parentId)\n : traceState.otelParentSpanPath;\n const parentIdsPath = parentId\n ? traceState.spanIdsPathById.get(parentId)\n : traceState.otelParentSpanIdsPath;\n\n // Derive the UUID from the normalized (16-hex-char) span id so this\n // path entry agrees with what `convertSpanToOtel` produces via\n // `normalizeOtelSpanId` → OTel span id. Mastra occasionally emits span ids\n // longer than 16 hex chars; passing the raw value here would truncate\n // differently from the exported id and Laminar's UI (which threads\n // nesting on `lmnr.span.ids_path`) would render the Mastra subtree flat.\n const spanUuid = otelSpanIdToUUID(normalizeOtelSpanId(span.id));\n const spanPath = parentPath ? [...parentPath, span.name] : [span.name];\n const spanIdsPath = parentIdsPath\n ? [...parentIdsPath, spanUuid]\n : [spanUuid];\n\n traceState.spanPathById.set(span.id, spanPath);\n traceState.spanIdsPathById.set(span.id, spanIdsPath);\n }\n\n private async handleSpanEnded(span: MastraExportedSpan): Promise<void> {\n if (span.type === MastraSpanType.MODEL_CHUNK) {\n // MODEL_CHUNK spans are streaming noise as far as trace output goes\n // (dropped below), but reasoning chunks carry the thinking text for\n // their parent MODEL_STEP — accumulate before dropping. Mastra emits\n // these as `attributes.chunkType: \"reasoning\"` with `output.text` =\n // the delta text accumulated by its own chunk-span transform, and\n // the chunk ends BEFORE its parent MODEL_STEP ends, so the reasoning\n // is available when we build the step's LLM attributes.\n this.captureReasoningChunk(span);\n return;\n }\n\n const traceState = this.getOrCreateTraceState(span.traceId);\n\n try {\n // Backfill path/state when SPAN_STARTED was missed (e.g. exporter\n // registered mid-trace, the global SDK was unrecording at start time).\n // Inside the try so a throw here still runs cleanup.\n if (!traceState.spanPathById.has(span.id)) {\n this.recordSpanPath(span, traceState);\n // Mirror handleSpanStarted: register the span as active so the\n // finally block's `activeSpanIds.delete` is balanced.\n traceState.activeSpanIds.add(span.id);\n }\n if (\n span.type === MastraSpanType.MODEL_GENERATION &&\n !this.generationStateById.has(span.id)\n ) {\n this.initGenerationState(span);\n }\n if (\n span.type === MastraSpanType.MODEL_STEP &&\n !this.generationIdByStepId.has(span.id) &&\n span.parentSpanId\n ) {\n this.generationIdByStepId.set(span.id, span.parentSpanId);\n }\n if (!this.liveOtelSpanByMastraId.has(span.id)) {\n // Span_ended without a prior span_started, or the SDK was a\n // NonRecording proxy at start time and we couldn't keep a live\n // reference. Create the live span now (zero-duration if Mastra\n // didn't carry startTime).\n this.startOtelSpan(span, traceState);\n }\n\n // Record state transitions BEFORE applying attrs; LLM attribute\n // construction reads the accumulated step turn history.\n this.updateGenerationStateOnSpanEnd(span);\n\n const otelSpan = this.liveOtelSpanByMastraId.get(span.id);\n if (!otelSpan) {\n // `Laminar.initialize()` wasn't called, so the tracer was a noop\n // and `startOtelSpan` couldn't keep a recording span. Skip silently\n // — `startOtelSpan` already logged a one-time warning.\n return;\n }\n\n this.applyEndAttributes(span, otelSpan, traceState);\n\n if (span.errorInfo) {\n otelSpan.setStatus({\n code: SpanStatusCode.ERROR,\n message: span.errorInfo.message,\n });\n otelSpan.recordException({\n name: span.errorInfo.name ?? \"Error\",\n message: span.errorInfo.message,\n stack: span.errorInfo.stack,\n });\n }\n\n const endTime = span.endTime\n ? dateToHrTime(span.endTime)\n : dateToHrTime(span.startTime);\n otelSpan.end(endTime);\n\n // `Span.end()` synchronously fanned out to every processor on\n // Laminar's tracer provider. No manual onEnd push needed.\n if (this.config.realtime) {\n await getSpanProcessor()?.forceFlush();\n }\n } catch (err) {\n logger.error(\n `[MastraExporter] failed to export span ${span.id}: ${\n err instanceof Error ? err.message : String(err)\n }`,\n );\n } finally {\n traceState.activeSpanIds.delete(span.id);\n if (traceState.activeSpanIds.size === 0) {\n this.traceMap.delete(span.traceId);\n }\n if (span.type === MastraSpanType.MODEL_GENERATION) {\n this.generationStateById.delete(span.id);\n this.generationAttrsById.delete(span.id);\n }\n if (span.type === MastraSpanType.MODEL_STEP) {\n this.generationIdByStepId.delete(span.id);\n this.stepIndexBySpanId.delete(span.id);\n }\n this.liveOtelSpanByMastraId.delete(span.id);\n }\n }\n\n private initGenerationState(span: MastraExportedSpan): void {\n const rawMessages = extractMessages(span.input);\n const baseMessages = rawMessages ? normalizeInputMessages(rawMessages) : [];\n this.generationStateById.set(span.id, {\n baseMessages,\n turnsByStepIndex: new Map(),\n toolCallChildrenByStepIndex: new Map(),\n reasoningTextByStepIndex: new Map(),\n });\n }\n\n // Accumulate reasoning text from a MODEL_CHUNK into its parent MODEL_STEP's\n // slot on the surrounding MODEL_GENERATION. Chunks' `parentSpanId` is the\n // step span's id, and the step span was registered in `stepIndexBySpanId` /\n // `generationIdByStepId` at span_started, so we can resolve the slot\n // without any extra bookkeeping.\n //\n // When a step produces multiple separate reasoning blocks (reasoning-start\n // / reasoning-end fires more than once inside one step — Mastra starts a\n // new chunk span each time), we concatenate their texts in arrival order\n // to mirror Mastra's own `#bufferedReasoning` behavior.\n private captureReasoningChunk(span: MastraExportedSpan): void {\n const attrs = span.attributes ?? {};\n if (attrs.chunkType !== \"reasoning\") return;\n const output = span.output;\n if (!output || typeof output !== \"object\") return;\n const text = (output as Record<string, unknown>).text;\n if (typeof text !== \"string\" || text.length === 0) return;\n\n const stepSpanId = span.parentSpanId;\n if (!stepSpanId) return;\n const generationId = this.generationIdByStepId.get(stepSpanId);\n if (!generationId) return;\n const gen = this.generationStateById.get(generationId);\n if (!gen) return;\n const stepIndex = this.stepIndexBySpanId.get(stepSpanId);\n if (stepIndex === undefined) return;\n\n const existing = gen.reasoningTextByStepIndex.get(stepIndex);\n gen.reasoningTextByStepIndex.set(\n stepIndex,\n existing ? existing + text : text,\n );\n }\n\n private updateGenerationStateOnSpanEnd(span: MastraExportedSpan): void {\n if (span.type === MastraSpanType.MODEL_STEP) {\n const generationId = this.generationIdByStepId.get(span.id);\n if (!generationId) return;\n const gen = this.generationStateById.get(generationId);\n if (!gen) return;\n const stepIndex =\n this.stepIndexBySpanId.get(span.id) ??\n (typeof span.attributes?.stepIndex === \"number\"\n ? span.attributes.stepIndex\n : 0);\n this.stepIndexBySpanId.set(span.id, stepIndex);\n\n // Pair the declared `toolCalls` with tool_call children. Prefer id-based\n // pairing when children carry a toolCallId (parallel tool execution can\n // finish out of declaration order, so array-index pairing would\n // mis-attribute outputs). Fall back to arrival-order pairing among\n // unclaimed children when ids aren't available on the child spans.\n const output = span.output as Record<string, unknown> | undefined;\n const declaredToolCalls = Array.isArray(output?.toolCalls)\n ? (output.toolCalls as Array<Record<string, unknown>>)\n : [];\n const children = gen.toolCallChildrenByStepIndex.get(stepIndex) ?? [];\n const childrenById = new Map<string, ToolCallChild>();\n for (const c of children) {\n if (c.toolCallId) childrenById.set(c.toolCallId, c);\n }\n // Track already-paired children so the arrival-order fallback can't\n // re-pick a child that was already claimed by an id-based match.\n const consumed = new Set<ToolCallChild>();\n\n const turnMessages: AISdkMessage[] = [];\n const assistantMsg = buildAssistantMessageFromStepOutput(output);\n if (assistantMsg) turnMessages.push(assistantMsg);\n\n for (const dec of declaredToolCalls) {\n const toolCallId =\n (dec?.toolCallId as string | undefined) ??\n (dec?.id as string | undefined);\n // Don't skip declarations with a missing id:\n // `buildAssistantMessageFromStepOutput` still emitted a `tool-call`\n // part for this declaration, so we must emit a matching tool-result\n // or subsequent steps will see a broken history (assistant calls a\n // tool, no result follows). Fall through to arrival-order pairing in\n // that case, using whatever id the child span carries (may be\n // undefined).\n let child = toolCallId ? childrenById.get(toolCallId) : undefined;\n if (child && consumed.has(child)) child = undefined;\n if (!child) {\n // Fallback: first unconsumed child in arrival order.\n child = children.find((c) => !consumed.has(c));\n }\n if (!child) continue;\n consumed.add(child);\n const resolvedToolCallId = toolCallId ?? child.toolCallId;\n const toolName =\n (dec?.toolName as string | undefined) ??\n (dec?.name as string | undefined) ??\n child.toolName;\n turnMessages.push({\n role: \"tool\",\n tool_call_id: resolvedToolCallId,\n content: [\n {\n type: \"tool-result\",\n toolCallId: resolvedToolCallId,\n toolName,\n output: child.output,\n },\n ],\n });\n }\n\n gen.turnsByStepIndex.set(stepIndex, turnMessages);\n } else if (\n span.type === MastraSpanType.TOOL_CALL ||\n span.type === MastraSpanType.MCP_TOOL_CALL\n ) {\n // Record the tool_call child under its step so the step-end handler\n // can pair it with the declared toolCalls. Tool spans always end\n // before their parent MODEL_STEP ends, so the parent's stepIndex is\n // still in `stepIndexBySpanId` here.\n const parentId = span.parentSpanId;\n if (!parentId) return;\n const generationId = this.generationIdByStepId.get(parentId);\n if (!generationId) return;\n const gen = this.generationStateById.get(generationId);\n if (!gen) return;\n const stepIndex = this.stepIndexBySpanId.get(parentId);\n if (stepIndex === undefined) return;\n const list = gen.toolCallChildrenByStepIndex.get(stepIndex) ?? [];\n list.push({\n toolCallId: extractToolCallId(span),\n toolName: cleanMastraToolName(span.name) || \"tool\",\n input: span.input,\n output: span.output,\n });\n gen.toolCallChildrenByStepIndex.set(stepIndex, list);\n }\n }\n\n private getOrCreateTraceState(traceId: string): TraceState {\n const existing = this.traceMap.get(traceId);\n if (existing) return existing;\n const created: TraceState = {\n spanPathById: new Map(),\n spanIdsPathById: new Map(),\n activeSpanIds: new Set(),\n };\n // Capture the caller's active span synchronously, at the moment the\n // first event for this Mastra trace arrives — Mastra invokes exporter\n // handlers within the originating async context (see\n // `@mastra/observability` routeToHandler), so the caller's wrapper\n // span is still active here. Any later reads would be outside that\n // context (Mastra's event bus schedules handlers).\n //\n // Two stacks to consult, in order:\n // 1. Laminar's own context manager — the only place spans started via\n // `Laminar.startActiveSpan()` are tracked. That helper writes onto\n // a separate AsyncLocalStorage instance and never touches OTel's\n // standard context, so step (2) alone wouldn't see them.\n // 2. OTel's standard active span — set by `observe()` via\n // `context.with(...)` and by any other OTel-aware instrumentation.\n if (this.config?.linkToActiveContext) {\n const activeSpan =\n trace.getSpan(LaminarContextManager.getContext()) ??\n trace.getActiveSpan();\n const ctx = activeSpan?.spanContext();\n if (ctx && ctx.traceId && ctx.spanId) {\n created.otelTraceId = normalizeOtelTraceId(ctx.traceId);\n created.otelRootParentSpanId = normalizeOtelSpanId(ctx.spanId);\n // Also capture SPAN_PATH / SPAN_IDS_PATH so we can prepend them to\n // every Mastra span's path. Laminar's backend/UI threads the span\n // tree on `lmnr.span.ids_path` — sharing the traceId alone isn't\n // enough to make the Mastra subtree render under `observe()`.\n const attrs = (\n activeSpan as unknown as { attributes?: Record<string, unknown> }\n ).attributes;\n if (attrs) {\n const parentPath = attrs[SPAN_PATH];\n if (\n Array.isArray(parentPath) &&\n parentPath.every((p) => typeof p === \"string\")\n ) {\n created.otelParentSpanPath = parentPath;\n }\n const parentIdsPath = attrs[SPAN_IDS_PATH];\n if (\n Array.isArray(parentIdsPath) &&\n parentIdsPath.every((p) => typeof p === \"string\")\n ) {\n created.otelParentSpanIdsPath = parentIdsPath;\n }\n }\n }\n }\n this.traceMap.set(traceId, created);\n return created;\n }\n\n // Create a real OTel span via Laminar's tracer, then mutate its ids /\n // parent references so they line up with Mastra's. We use a real span (not\n // a manually-constructed ReadableSpan) so the registered LaminarSpanProcessor\n // sees it on `onStart` and can attach association properties from the\n // active context.\n private startOtelSpan(\n span: MastraExportedSpan,\n traceState: TraceState,\n ): void {\n // `Laminar.initialize()` is expected to have set up the tracer provider.\n // If it wasn't called, `getTracerProvider()` falls back to the global\n // OTel API which is a NoopTracerProvider, and `tracer.startSpan` will\n // return a NonRecordingSpan — caught below.\n const tracer = getTracerProvider().getTracer(\n \"@lmnr-ai/lmnr\",\n SDK_VERSION,\n );\n const parentCtx = this.buildParentContext(span, traceState);\n\n const otelSpan = tracer.startSpan(\n span.name,\n {\n startTime: dateToHrTime(span.startTime),\n kind: SpanKind.INTERNAL,\n },\n parentCtx,\n );\n\n if (!otelSpan.isRecording()) {\n this.warnNotInitializedOnce();\n return;\n }\n\n // Capture the SDK-allocated span id before we overwrite it.\n // `LaminarSpanProcessor.onStart` already fired synchronously inside\n // `tracer.startSpan` and keyed its `_spanIdToPath` / `_spanIdLists`\n // entries under this id — without dropping them below, `onEnd` (which\n // reads the post-mutation Mastra id) would delete nothing and both maps\n // would grow unboundedly.\n const sdkAllocatedSpanId = otelSpan.spanContext().spanId;\n\n // Stamp Mastra-derived ids over the SDK's auto-generated ones. Mutates\n // the SpanContext object the SDK holds internally — `spanContext()`\n // returns the same reference each call.\n const mastraSpanId = normalizeOtelSpanId(span.id);\n const mastraTraceId =\n traceState.otelTraceId ?? normalizeOtelTraceId(span.traceId);\n Object.assign(otelSpan.spanContext(), {\n traceId: mastraTraceId,\n spanId: mastraSpanId,\n });\n\n // Drop the orphaned path entry the processor cached under the original\n // SDK-allocated id. Safe no-op if the id happened to match the Mastra id\n // (unlikely but possible).\n if (sdkAllocatedSpanId !== mastraSpanId) {\n getSpanProcessor()?.dropPathInfo(sdkAllocatedSpanId);\n }\n\n // Patch parent references. We set both `parentSpanContext` (SDK v2) and\n // `parentSpanId` (SDK v1 alias) so this works across SDK versions.\n const parentSpanIdNormalized = span.parentSpanId\n ? normalizeOtelSpanId(span.parentSpanId)\n : traceState.otelRootParentSpanId;\n if (parentSpanIdNormalized) {\n const parentSpanContext: SpanContext = {\n traceId: mastraTraceId,\n spanId: parentSpanIdNormalized,\n traceFlags: TraceFlags.SAMPLED,\n isRemote: false,\n };\n Object.assign(otelSpan, {\n parentSpanId: parentSpanIdNormalized,\n parentSpanContext,\n });\n } else {\n // Mastra root with no captured outer span — clear whatever parent the\n // SDK inferred from `parentCtx` so the exported span reads as a root.\n Object.assign(otelSpan, {\n parentSpanId: undefined,\n parentSpanContext: undefined,\n });\n }\n\n // Globally-registered processors (e.g. LaminarSpanProcessor) wrote\n // `lmnr.span.path` / `lmnr.span.ids_path` during their `onStart` hook\n // using the SDK's auto-generated span id — overwrite with our\n // Mastra-derived versions so Laminar's UI nests the subtree correctly.\n const spanPath = traceState.spanPathById.get(span.id);\n const spanIdsPath = traceState.spanIdsPathById.get(span.id);\n if (spanPath) otelSpan.setAttribute(SPAN_PATH, spanPath);\n if (spanIdsPath) otelSpan.setAttribute(SPAN_IDS_PATH, spanIdsPath);\n\n this.liveOtelSpanByMastraId.set(span.id, otelSpan);\n }\n\n private warnNotInitializedOnce(): void {\n if (this.warnedNotInitialized) return;\n this.warnedNotInitialized = true;\n logger.warn(\n \"[MastraExporter] Laminar tracer is not initialized — Mastra spans \" +\n \"will be dropped. Call `Laminar.initialize({ projectApiKey: ... })` \" +\n \"before constructing `MastraExporter`.\",\n );\n }\n\n // Pick the OTel context whose active span will become the new Mastra\n // span's parent. Resolution order:\n // 1. Mastra child? Use the *live* (mutated) parent span we already\n // created — its spanContext exposes the Mastra-derived parent ids.\n // 2. Mastra root? Use the outer `observe()` / `Laminar.startActiveSpan()`\n // span captured at first-event time (if linking is enabled).\n // 3. Otherwise the bare context (Laminar's first, OTel's as fallback).\n private buildParentContext(\n span: MastraExportedSpan,\n traceState: TraceState,\n ): Context {\n const baseCtx =\n LaminarContextManager.getContext() ?? contextApi.active();\n\n if (span.parentSpanId) {\n const parentLive = this.liveOtelSpanByMastraId.get(span.parentSpanId);\n if (parentLive) return trace.setSpan(baseCtx, parentLive);\n }\n\n if (traceState.otelTraceId && traceState.otelRootParentSpanId) {\n const wrapped = trace.wrapSpanContext({\n traceId: traceState.otelTraceId,\n spanId: traceState.otelRootParentSpanId,\n traceFlags: TraceFlags.SAMPLED,\n isRemote: false,\n });\n return trace.setSpan(baseCtx, wrapped);\n }\n\n return baseCtx;\n }\n\n private applyEndAttributes(\n span: MastraExportedSpan,\n otelSpan: Span,\n traceState: TraceState,\n ): void {\n const attributes = this.buildLaminarAttributes(span, traceState);\n // Path attrs were already written in `startOtelSpan`. Skip them on the\n // bulk re-apply both to avoid pointless setAttribute churn and to keep\n // the path values stable if some upstream processor mutated them.\n delete attributes[SPAN_PATH];\n delete attributes[SPAN_IDS_PATH];\n otelSpan.setAttributes(attributes);\n }\n\n private buildLaminarAttributes(\n span: MastraExportedSpan,\n traceState: TraceState,\n ): Record<string, any> {\n const attributes: Record<string, any> = {};\n\n const spanPath = traceState.spanPathById.get(span.id);\n const spanIdsPath = traceState.spanIdsPathById.get(span.id);\n if (spanPath) attributes[SPAN_PATH] = spanPath;\n if (spanIdsPath) attributes[SPAN_IDS_PATH] = spanIdsPath;\n\n const laminarSpanType = mapLaminarSpanType(span.type);\n attributes[SPAN_TYPE] = laminarSpanType;\n attributes[SPAN_INSTRUMENTATION_SOURCE] = \"javascript\";\n attributes[SPAN_SDK_VERSION] = SDK_VERSION;\n const langVersion = getLangVersion();\n if (langVersion) attributes[SPAN_LANGUAGE_VERSION] = langVersion;\n\n // Preserve the original Mastra span type so e.g. agent_run vs\n // model_generation (both DEFAULT) can be told apart downstream.\n attributes[\"lmnr.internal.mastra.span_type\"] = span.type;\n\n const metadata = span.metadata ?? {};\n const sessionId = metadata.sessionId ?? metadata.session_id;\n if (typeof sessionId === \"string\" && sessionId.length > 0) {\n attributes[SESSION_ID] = sessionId;\n }\n const userId = metadata.userId ?? metadata.user_id;\n if (typeof userId === \"string\" && userId.length > 0) {\n attributes[USER_ID] = userId;\n }\n for (const [key, value] of Object.entries(metadata)) {\n if (\n key === \"sessionId\" ||\n key === \"session_id\" ||\n key === \"userId\" ||\n key === \"user_id\" ||\n value === undefined ||\n value === null\n ) {\n continue;\n }\n const av = toAttributeValue(value);\n if (av !== undefined) {\n attributes[`${ASSOCIATION_PROPERTIES}.metadata.${key}`] = av;\n }\n }\n if (span.isRootSpan && span.tags?.length) {\n attributes[`${ASSOCIATION_PROPERTIES}.tags`] = span.tags;\n }\n\n if (laminarSpanType === \"LLM\") {\n this.applyLlmAttributes(span, attributes);\n } else if (laminarSpanType === \"TOOL\") {\n this.applyToolAttributes(span, attributes);\n } else {\n if (span.input !== undefined) {\n attributes[SPAN_INPUT] = serializeJSON(span.input);\n }\n if (span.output !== undefined) {\n attributes[SPAN_OUTPUT] = serializeJSON(span.output);\n }\n }\n\n return attributes;\n }\n\n private applyLlmAttributes(\n span: MastraExportedSpan,\n attributes: Record<string, any>,\n ): void {\n const generationId =\n this.generationIdByStepId.get(span.id) ?? span.parentSpanId;\n const gen = generationId\n ? this.generationStateById.get(generationId)\n : undefined;\n const generationAttrs = generationId\n ? this.generationAttrsById.get(generationId)\n : undefined;\n\n const stepAttrs = span.attributes ?? {};\n const stepIndex =\n this.stepIndexBySpanId.get(span.id) ??\n (typeof stepAttrs.stepIndex === \"number\" ? stepAttrs.stepIndex : 0);\n\n const provider = generationAttrs?.provider as string | undefined;\n const model = generationAttrs?.model as string | undefined;\n const responseModel = generationAttrs?.responseModel as string | undefined;\n if (provider)\n attributes[LaminarAttributes.PROVIDER] = normalizeProvider(provider);\n if (model) attributes[LaminarAttributes.REQUEST_MODEL] = model;\n if (responseModel)\n attributes[LaminarAttributes.RESPONSE_MODEL] = responseModel;\n else if (model) attributes[LaminarAttributes.RESPONSE_MODEL] = model;\n\n const usage = (stepAttrs.usage ?? generationAttrs?.usage) as\n | MastraUsageStats\n | undefined;\n Object.assign(attributes, formatUsage(usage));\n\n if (typeof stepAttrs.finishReason === \"string\") {\n attributes[\"gen_ai.response.finish_reason\"] = stepAttrs.finishReason;\n attributes[\"ai.response.finishReason\"] = stepAttrs.finishReason;\n }\n\n // Build the full message history for this step: base + every turn with\n // stepIndex < current stepIndex, flattened.\n const messages: AISdkMessage[] = gen ? [...gen.baseMessages] : [];\n if (gen) {\n for (let i = 0; i < stepIndex; i++) {\n const turn = gen.turnsByStepIndex.get(i);\n if (turn) messages.push(...turn);\n }\n }\n\n if (messages.length > 0) {\n attributes[\"ai.prompt.messages\"] = serializeJSON(messages);\n } else if (span.input !== undefined) {\n attributes[SPAN_INPUT] = serializeJSON(span.input);\n }\n\n const out = span.output;\n const reasoningText = gen?.reasoningTextByStepIndex.get(stepIndex);\n if (out && typeof out === \"object\") {\n const outObj = out as Record<string, unknown>;\n if (typeof outObj.text === \"string\" && outObj.text.length > 0) {\n attributes[\"ai.response.text\"] = outObj.text;\n }\n const normalizedToolCalls =\n Array.isArray(outObj.toolCalls) && outObj.toolCalls.length > 0\n ? (outObj.toolCalls as Array<Record<string, unknown>>).map((tc) => ({\n toolCallType: tc.toolCallType ?? \"function\",\n toolCallId: tc.toolCallId,\n toolName: tc.toolName,\n args: stringifyArgs(tc.args ?? tc.input),\n }))\n : [];\n if (normalizedToolCalls.length > 0) {\n attributes[\"ai.response.toolCalls\"] =\n JSON.stringify(normalizedToolCalls);\n }\n // Emit OTel GenAI semconv `gen_ai.output.messages` with a `thinking`\n // part when this step had reasoning chunks. Laminar's backend\n // preserves this attribute verbatim on `self.output` and the frontend\n // GenAI parser maps `thinking` → ModelMessage `reasoning`, rendered\n // with a \"Thinking\" label in the trace UI.\n if (typeof reasoningText === \"string\" && reasoningText.length > 0) {\n const parts: Array<Record<string, unknown>> = [\n { type: \"thinking\", content: reasoningText },\n ];\n if (typeof outObj.text === \"string\" && outObj.text.length > 0) {\n parts.push({ type: \"text\", content: outObj.text });\n }\n for (const tc of normalizedToolCalls) {\n let argsObj: unknown = tc.args;\n if (typeof argsObj === \"string\") {\n try {\n argsObj = JSON.parse(argsObj);\n } catch {\n // leave as string; backend still accepts it\n }\n }\n parts.push({\n type: \"tool_call\",\n id: tc.toolCallId,\n name: tc.toolName,\n arguments: argsObj,\n });\n }\n attributes[\"gen_ai.output.messages\"] = JSON.stringify([\n { role: \"assistant\", parts },\n ]);\n }\n } else if (out !== undefined) {\n attributes[SPAN_OUTPUT] = serializeJSON(out);\n }\n }\n\n private applyToolAttributes(\n span: MastraExportedSpan,\n attributes: Record<string, any>,\n ): void {\n if (span.input !== undefined) {\n attributes[SPAN_INPUT] = serializeJSON(span.input);\n }\n if (span.output !== undefined) {\n attributes[SPAN_OUTPUT] = serializeJSON(span.output);\n }\n const toolAttrs = span.attributes ?? {};\n // Strip the `tool: 'x'` prefix Mastra prepends so the displayed name is\n // just the tool id.\n const cleanedName = cleanMastraToolName(span.name) || span.name;\n if (cleanedName) attributes[\"ai.toolCall.name\"] = cleanedName;\n if (typeof toolAttrs.toolType === \"string\") {\n attributes[\"ai.toolCall.type\"] = toolAttrs.toolType;\n }\n if (typeof toolAttrs.mcpServer === \"string\") {\n attributes[\"mcp.server\"] = toolAttrs.mcpServer;\n }\n }\n}\n"],"mappings":";;;;;;AAKA,MAAM,qBAAqB;AAE3B,IAAsB,oBAAtB,MAA8C;CAC5C,MAAa,MAAM,OAAe,KAAyC;EACzE,MAAM,SAAS,EAAE;AACjB,OAAK,IAAI,IAAI,KAAK,IAAI,OAAO,EAAE,EAAE,IAAI,KAAK,IAAI,KAAK,MAAM,KAAK,MAAM,CAAC,EAAE,IACrE,QAAO,KAAK,MAAM,KAAK,IAAI,EAAE,CAAC;AAEhC,SAAO;;;AAMX,IAAa,iBAAb,cAA0C,kBAAwB;CAUhE,YAAY,MAAe,SAAmD;AAC5E,SAAO;sBAViC,EAAE;aACf;gBACJ;gBAEmB,KAAA;AAO1C,MAAI,CAAC,QAAQ,CAAC,SAAS,GACrB,OAAM,IAAI,MAAM,qCAAqC;AAEvD,MAAI,QAAQ,SAAS,GACnB,OAAM,IAAI,MAAM,0CAA0C;AAE5D,OAAK,OAAO;AACZ,OAAK,KAAK,SAAS;AACnB,OAAK,YAAY,SAAS,aAAa;;CAGzC,UAAiB,QAAuB;AACtC,OAAK,SAAS;;CAGhB,MAAc,aAAa;AACzB,MAAI,CAAC,KAAK,OACR,OAAM,IAAI,MAAM,iBAAiB;EAEnC,MAAM,aAAa,KAAK,KAAK,EAAE,IAAI,KAAK,IAAI,GAAG,EAAE,MAAM,KAAK,MAAO;EACnE,MAAM,OAAO,MAAM,KAAK,OAAO,SAAS,KAAW;GACjD,GAAG;GACH,QAAQ,KAAK;GACb,OAAO,KAAK;GACb,CAAC;AACF,OAAK,eAAe,KAAK,aAAa,OAAO,KAAK,MAAM;AACxD,OAAK,SAAS,KAAK,aAAa;AAChC,MAAI,KAAK,QAAQ,KACf,MAAK,MAAM,KAAK;;CAIpB,MAAa,OAAwB;AACnC,MAAI,KAAK,QAAQ,KACf,OAAM,KAAK,YAAY;AAEzB,SAAO,KAAK;;CAGd,MAAa,IAAI,OAAyC;AACxD,MAAI,SAAS,KAAK,aAAa,OAC7B,OAAM,KAAK,YAAY;AAEzB,SAAO,KAAK,aAAa;;;;;;;;CAS3B,MAAa,KAAK,OAA0B,YAAqB,OAAsB;AACrF,MAAI,CAAC,KAAK,OACR,OAAM,IAAI,MAAM,iBAAiB;EAInC,MAAM,EAAE,kBAAkB,MAAM,OAAO,6BAAA,MAAA,MAAA,EAAA,EAAA;EAGvC,MAAM,OAAO,MAAM,cADD,MAAM,QAAQ,MAAM,GAAG,QAAQ,CAAC,MAAM,EACN,UAAU;AAE5D,MAAI,KAAK,WAAW,GAAG;AACrB,WAAQ,KAAK,4BAA4B;AACzC;;EAGF,MAAM,aAAa,KAAK,KAAK,EAAE,IAAI,KAAK,IAAI,GAAG,EAAE,MAAM,KAAK,MAAO;AACnE,QAAM,KAAK,OAAO,SAAS,KAAK;GAC9B,QAAQ;GACR,GAAG;GACJ,CAAC;AAEF,UAAQ,IAAI,uBAAuB,KAAK,OAAO,wBAAwB;;;;;AC7E3E,SAAS;AAET,MAAM,sBAAsB;AAC5B,MAAM,wBAAwB;AAQ9B,MAAMA,WAAS,kBAAkB;AAEjC,MAAM,oBACJ,WACA,cACA,SACA,iBACW;AAEX,QAAO,GADK,eAAe,SAAS,aAAa,CACnC,WAAW,UAAU,eAAe;;AAGpD,MAAM,oBACJ,YAC2B;CAC3B,MAAM,iBAA2C,EAAE;AACnD,MAAK,MAAM,UAAU,QACnB,MAAK,MAAM,OAAO,OAAO,QAAQ;EAC/B,MAAM,QAAQ,OAAO,OAAO;AAC5B,MAAI,eAAe,QAAQ,UAAU,KACnC,gBAAe,KAAK,KAAK,MAAM;MAE/B,gBAAe,OAAO,UAAU,OAAO,CAAC,MAAM,GAAG,EAAE;;CAKzD,MAAM,gBAAwC,EAAE;AAChD,MAAK,MAAM,OAAO,eAChB,eAAc,OACZ,eAAe,KAAK,QAAQ,GAAG,MAAM,IAAI,GAAG,EAAE,GAC9C,eAAe,KAAK;AAGxB,QAAO;;;;;AAgGT,IAAa,iBAAb,MAA4B;CAG1B,YAAY,SAA8C;AACxD,OAAK,UAAU;;;;;;AAqEnB,IAAM,qBAAN,MAAyB;CASvB,YAAY,SAAkB,cAAuB;qBARR,IAAI,YAAY,UAC3D,EAAE,EACF,YAAY,QAAQ,eACrB;yBACiC;AAKhC,OAAK,UAAU,WAAW;AAC1B,OAAK,eAAe;;CAGtB,MAAa,EAAE,UAA8B;AAC3C,OAAK,YAAY,MAAM,QAAQ,EAAE;;CAGnC,OAAc,aAAqB;AACjC,OAAK,mBAAmB;AACxB,OAAK,YAAY,OAAO,KAAK,gBAAgB;;CAI/C,cAAqB,OAAc;AACjC,OAAK,YAAY,MAAM;AACvB,UAAQ,OAAO,MAAM,YAAY,MAAM,QAAQ,IAAI;;CAIrD,KAAY,EACV,eACA,WACA,gBAKC;AACD,OAAK,YAAY,MAAM;EACvB,MAAM,MAAM,iBACV,WACA,cACA,KAAK,SACL,KAAK,aACN;AACD,UAAQ,OAAO,MAAM,KAAK;AAC1B,UAAQ,OAAO,MAAM,sBAAsB;AAC3C,OAAK,MAAM,OAAO,cAChB,SAAQ,OAAO,MAAM,GAAG,IAAI,IAAI,cAAc,KAAK,IAAI;AAEzD,UAAQ,OAAO,MAAM,sBAAsB,IAAI,IAAI;;;AAIvD,IAAa,aAAb,MAAiC;CAoB/B,YAAY,EACV,MACA,UACA,YACA,WACA,MACA,UACA,UACsC;oBA3BV;0BAYK;2BACE;8BAEE;wBACE,EAAE;AAYzC,MAAI,OAAO,KAAK,WAAW,CAAC,WAAW,EACrC,OAAM,IAAI,MAAM,yBAAyB;EAG3C,MAAM,qBAAqB;AAE3B,OAAK,MAAM,OAAO,WAChB,KAAI,CAAC,mBAAmB,KAAK,IAAI,CAC/B,OAAM,IAAI,MACR,2BAA2B,IAAI,4EAEhC;AAIL,OAAK,eAAe,QAAQ;AAC5B,OAAK,mBAAmB,IAAI,mBAC1B,QAAQ,SACR,QAAQ,aACT;AACD,OAAK,OAAO;AACZ,OAAK,WAAW;AAChB,OAAK,aAAa;AAClB,OAAK,YAAY;AACjB,OAAK,WAAW;AAChB,OAAK,OAAO;AAEZ,MAAI,QAAQ;AACV,OACE,OAAO,qBAAqB,KAAA,KAC5B,OAAO,mBAAmB,GAC1B;AACA,aAAO,KACL,kEAAkE,sBACnE;AACD,SAAK,mBAAmB;SAExB,MAAK,mBAAmB,OAAO,oBAAoB;AAErD,QAAK,oBAAoB,OAAO,qBAAqB;AACrD,QAAK,2BAA2B,OAAO;AACvC,QAAK,uBACH,OAAO,wBAAwB;;AAGnC,MAAI,QAAQ,aAAa,EAAE;AACzB,QAAK,SAAS,IAAI,cAAc;IAC9B,SAAS,QAAQ,YAAY;IAC7B,eAAe,QAAQ,kBAAkB;IAC1C,CAAC;AACF,OACE,QAAQ,iBACR,OAAO,kBAAkB,QAAQ,kBAAkB,CAEnD,UAAO,KACL,6HAED;AAEH;;EAGF,MAAM,MAAM,QAAQ,iBAAiB,QAAQ,IAAI;AACjD,MAAI,QAAQ,KAAA,EACV,OAAM,IAAI,MACR,sHAED;EAGH,MAAM,MACJ,QAAQ,WAAW,SAAS,KAAK,iBAAiB;EACpD,MAAM,UAAU,QAAQ,eAAe;EACvC,MAAM,WACJ,QAAQ,aACP,QAAQ,MAAM,aAAa,GACxB,SAAS,QAAQ,MAAM,aAAa,CAAE,GAAG,MAAM,EAAE,CAAC,GAClD;EAIN,MAAM,cAAc,GAHI,QACrB,QAAQ,OAAO,GAAG,CAClB,QAAQ,cAAc,GAAG,CACW,GAAG;AAE1C,OAAK,SAAS,IAAI,cAAc;GAC9B,SAAS;GACT,eAAe;GAChB,CAAC;AAEF,UAAQ,WAAW;GACjB,eAAe,QAAQ;GACvB,SAAS;GACT;GACA;GACA,UAAU,QAAQ;GAClB,mBAAmB,QAAQ;GAC3B,cAAc,KAAK;GACnB,0BAA0B,KAAK;GAC/B,oBAAoB,KAAK;GAC1B,CAAC;;CAGJ,MAAa,MAAoC;AAC/C,MAAI,KAAK,WACP,OAAM,IAAI,MAAM,iCAAiC;AAEnD,MAAI,KAAK,gBAAgB,gBAAgB;AACvC,QAAK,KAAK,UAAU,KAAK,OAAO;AAEhC,OAAI,CAAC,KAAK,KAAK,GACb,KAAI;IACF,MAAM,WAAW,MAAM,KAAK,OAAO,SAAS,iBACzC,KAAK,KAAa,KACpB;AACD,QAAI,SAAS,SAAS,EACpB,MAAK,KAAK,KAAK,SAAS,GAAG;QAE3B,UAAO,KAAK,WAAY,KAAK,KAAa,KAAK,YAAY;YAEtD,OAAO;AAEd,aAAO,KACL,yBAAyB,KAAK,KAAK,KAAK,IACnC,iBAAiB,QAAQ,MAAM,UAAU,OAAO,MAAM,GAC5D;;;EAKP,IAAI;AACJ,MAAI;GACF,MAAM,aAAa,MAAM,KAAK,OAAO,MAAM,KACzC,KAAK,MACL,KAAK,WACL,KAAK,SACN;GACD,MAAM,MAAM,iBACV,WAAW,WACX,WAAW,IACX,KAAK,iBAAiB,SACtB,KAAK,aACN;AACD,WAAQ,OAAO,MAAM,sBAAsB,IAAI,IAAI;AACnD,QAAK,iBAAiB,MAAM,EAAE,QAAQ,MAAM,KAAK,WAAW,EAAE,CAAC;AAE/D,sBAAmB,MAAM,KAAK,kBAAkB,WAAW,GAAG;GAC9D,MAAM,gBAAgB,iBAAiB,iBAAiB;AACxD,OAAI,KAAK,eAAe,SAAS,EAC/B,OAAM,QAAQ,IAAI,KAAK,eAAe;AAExC,QAAK,iBAAiB,KAAK;IACzB;IACA,WAAW,WAAW;IACtB,cAAc,WAAW;IAC1B,CAAC;AACF,QAAK,aAAa;AAElB,SAAM,QAAQ,UAAU;AACxB,UAAO;IACL;IACA,WAAW,WAAW;IACtB,cAAc,WAAW;IACzB;IACD;WACM,GAAG;AACV,QAAK,iBAAiB,cAAc,EAAW;AAC/C,QAAK,aAAa;AAClB,UAAO;IACL,eAAe,EAAE;IACjB,WAAW;IACX,cAAc;IACd,KAAK;IACL,cAAc,aAAa,QAAQ,EAAE,UAAU,OAAO,EAAE;IACzD;;;CAIL,MAAa,kBACX,QACyC;EACzC,MAAM,cAAc,sBAAsB,YAAY;EACtD,MAAM,+BAAgC,YAAY,SAChD,2BACD,IAAI,EAAE;EACP,MAAM,gBAAgB,YAAY,SAAS,4BAA4B;GACrE,GAAG;GACH,UAAU;IACR,GAAI,6BAA6B,YAAY,EAAE;IAC/C,eAAe;IAChB;GACF,CAAC;AAEF,SAAO,sBAAsB,uBAE3B,CAAC,cAAc,EAAE,YAAY;GAC7B,MAAM,YAAY,IAAI,UAAU,KAAK,iBAAiB;GACtD,MAAM,QAA2D,EAAE;GAEnE,MAAM,eAAe,OACnB,WACA,UACoD;AACpD,QAAI;KACF,MAAM,SAAS,MAAM,KAAK,kBACxB,QACA,WACA,MACD;AACD,UAAK,iBAAiB,OAAO,EAAE;AAC/B,YAAO,CAAC,OAAO,OAAO;cACd;AACR,eAAU,SAAS;;;AAIvB,QAAK,IAAI,IAAI,GAAG,IAAK,MAAM,KAAK,WAAW,EAAG,KAAK;AACjD,UAAM,UAAU,SAAS;IACzB,MAAM,YAAY,MAAM,QAAQ,KAAK,KAAK,GACtC,KAAK,KAAK,KACV,MAAM,KAAK,KAAK,IAAI,EAAE;AAC1B,UAAM,KAAK,aAAa,WAAW,EAAE,CAAC;;AAIxC,WAFgB,MAAM,QAAQ,IAAI,MAAM,EAEzB,MAAM,GAAG,MAAM,EAAE,KAAK,EAAE,GAAG,CAAC,KAAK,GAAG,YAAY,OAAO;IACtE;;CAGJ,MAAc,kBACZ,QACA,WACA,OACuC;AACvC,SAAO,QACL;GAAE,MAAM;GAAc,WAAW;GAAc,EAC/C,YAAY;AACV,SACG,QAAQ,sBAAsB,YAAY,CAAC,CAC3C,aAAa,WAAW,aAAa;GACxC,MAAM,eAAe,QAAQ,UAAU;IACrC,MAAM;IACN,OAAO,UAAU;IAClB,CAAC;AACF,gBAAa,aAAa,WAAW,WAAW;GAChD,MAAM,iBAAiB,iBACrB,aAAa,aAAa,CAAC,OAC5B;GACD,MAAM,cAAc,SAAS;GAC7B,MAAM,mBAAmB;IACvB,IAAI;IACJ,MAAM,UAAU;IAChB,QAAQ,UAAU;IAClB,UAAU,UAAU;IACpB,SAAS,kBACP,MAAM,QAAQ,sBAAsB,YAAY,CAAC,CAAE,aAAa,CAC7D,QACJ;IACD;IACA;IACD;AAGD,OACE,KAAK,gBAAgB,kBACrB,KAAK,KAAK,MACV,UAAU,MACV,UAAU,UAEV,kBAAiB,cAAc;IAC7B,WAAW,KAAK,KAAK;IACrB,aAAa,UAAU;IACvB,WAAW,UAAU;IACtB;AAIH,SAAM,KAAK,OAAO,MAAM,eAAe;IACrC;IACA,YAAY,CAAC,iBAAiB;IAC9B,WAAW,KAAK;IACjB,CAAC;GAEF,MAAM,SAAS,MAAM,QAAQ,SAC3B,cACA,YAAY;IACV,MAAM,SAAS,MAAM,KAAK,SAAS,UAAU,KAAK;AAClD,YAAQ,cAAc,OAAO;AAC7B,WAAO;MAET,KACD;GACD,MAAM,SAAS,UAAU;GAEzB,IAAI,SAAwC,EAAE;AAC9C,QAAK,MAAM,CAAC,eAAe,cAAc,OAAO,QAC9C,KAAK,WACN,EAAE;IACD,MAAM,QAAQ,MAAM,QAClB,EAAE,MAAM,eAAe,EACvB,OAAO,QAAW,QAAY,SAAa;AACzC,SAAI,qBAAqB,gBAAgB;MACvC,MAAM,aAAa,MAAM,QACvB,sBAAsB,YAAY,CACnC;AACD,UAAI,YAAY;AACd,kBAAW,aAAa,WAAW,kBAAkB;AACrD,WAAI,UAAU,QACZ,YAAW,aACT,yBACA,KAAK,UAAU,UAAU,QAAQ,CAClC;;AAGL,aAAO;YACF;MACL,MAAM,aAAa,MAAM,QACvB,sBAAsB,YAAY,CACnC;AACD,UAAI,WACF,YAAW,aAAa,WAAW,YAAY;AAEjD,aAAO,MAAM,UAAU,QAAQ,QAAQ,KAAK;;OAGhD,QACA,UAAU,QACV,UAAU,KACX;AAED,QAAI,qBAAqB,gBAAgB;AACvC,YAAO,iBAAiB;AACxB;;AAGF,QAAI,OAAO,UAAU,UAAU;AAC7B,SAAI,MAAM,MAAM,CACd,OAAM,IAAI,MAAM,aAAa,cAAc,eAAe;AAE5D,YAAO,iBAAiB;eACf,UAAU,KACnB,UAAS;KAAE,GAAG;KAAQ,GAAG;KAAO;;GAIpC,MAAM,kBAAkB;IACtB,IAAI;IACJ,gBAAgB;IAChB,MAAM,UAAU;IAChB;IACA,UAAU,UAAU;IACpB;IACA,SAAS,kBACP,MAAM,QAAQ,sBAAsB,YAAY,CAAC,CAAE,aAAa,CAC7D,QACJ;IACD;IACA;IACD;AAGD,OACE,KAAK,gBAAgB,kBACrB,KAAK,KAAK,MACV,UAAU,MACV,UAAU,UAEV,iBAAgB,cAAc;IAC5B,WAAW,KAAK,KAAK;IACrB,aAAa,UAAU;IACvB,WAAW,UAAU;IACtB;GAGH,MAAM,gBAAgB,KAAK,OAAO,MAAM,eAAe;IACrD;IACA,YAAY,CAAC,gBAAgB;IAC7B,WAAW,KAAK;IACjB,CAAC;AACF,QAAK,eAAe,KAAK,cAAc;AAEvC,UAAO;IAEV;;CAGH,MAAc,YAA6B;AACzC,SAAO,KAAK,gBAAgB,oBACxB,MAAM,KAAK,KAAK,MAAM,GACtB,KAAK,KAAK;;CAGhB,gBAAuB,MAAc;AACnC,OAAK,eAAe;AACpB,OAAK,iBAAiB,eAAe;;;;;;;;;;;;;;;;;;;;;AAsBzC,eAAsB,SAAkB,EACtC,MACA,UACA,YACA,WACA,MACA,UACA,UAGA;CACA,MAAM,aAAa,IAAI,WAAoB;EACzC;EACA;EACA;EACA;EACA;EACA;EACA;EACD,CAAC;AACF,KAAI,WAAW,uBAGb,YAAW,eAAe,CAAC,GAAI,WAAW,gBAAgB,EAAE,EAAG,WAAW;KAE1E,QAAO,MAAM,WAAW,KAAK;;;;;;;;;ACltBjC,IAAsB,2BAAtB,MAA+C;CAc7C,YAAY,eAAkD;qBARhB,EAAE;4BACK,EAAE;mBAIlD,EAAE;qBACwB;AAG7B,OAAK,qBAAqB;AAC1B,OAAK,WAAW,cAAc;AAC9B,OAAK,UAAU,cAAc;AAC7B,OAAK,gBAAgB,cAAc;;;;;;CAuErC,sBACE,SACA,cACkB;AAClB,SAAO,KAAK,8BACV,SACA,cACA,KAAK,iBAAiB,KAAK,KAAK,CACjC;;;;;;CAuCH,oBACE,SACA,YACkB;AAClB,SAAO,KAAK,8BACV,SACA,YACA,KAAK,eAAe,KAAK,KAAK,CAC/B;;;;;;CAOH,8BACE,SACA,YACA,UAUkB;EAClB,MAAM,YAAY,QAAQ,IAAI;AAC9B,MAAI,UACF,QAAO,KAAK,mBAAmB,CAAC,WAAW;GACzC,MAAM,OAAO,QAAQ,gBAAgB;AACrC,SAAM,aAAa,2BAA2B,UAAU;GACxD,MAAM,YAAY,QAAQ,uBAAuB,EAAE;AACnD,OAAI,WAAW;IACb,MAAM,OAAO,UAAU,KAAK,IAAI;IAChC,MAAM,eAAe,KAAK,mBAAmB,SAAS;AACtD,SAAK,mBAAmB,QAAQ,eAAe;IAE/C,MAAM,uBAAuB,KAAK,eAAe,MAAM,QAAQ;AAI/D,UAAM,aACJ,sBACA,4BAA4B,qBAAqB,OAAO,CACzD;AACD,QAAI,qBAAqB,SAAS,qBAAqB,MAAM,SAAS,EACpE,OAAM,aACJ,mBACA,qBAAqB,MAAM,KAAI,SAAQ,KAAK,UAAU,KAAK,CAAC,CAC7D;AAEH,QAAI,KAAK,YAAY,SAAS,gBAAgB,KAAK,YAAY,MAC7D,QAAO,WAAW,qBAAqB;AAEzC,WAAO,SAAS,MAAM,aAAa,CAAC,MAAK,iBAAgB;AACvD,SAAI,aACF,QAAO;AAET,YAAO,WAAW,qBAAqB;MACvC;;AAEJ,UAAO,WAAW,QAAQ;IAC1B;AAEJ,SAAO,WAAW,QAAQ;;;;;;CAO5B,MAAc,oBAAmC;AAC/C,MAAI,KAAK,YACP;AAIF,QAAM,KAAK,sBAAsB;AACjC,OAAK,cAAc;;;;;CAMrB,MAAc,uBAAsC;EAClD,MAAM,OAAO,MAAM,KAAK,gBAAgB,IAAI,EAAE;AAC9C,MAAI,MAAM;AACR,QAAK,cAAc,KAAK;AACxB,OAAI,KAAK,UACP,MAAK,YAAY,KAAK;;;;;;;CAS5B,MAAc,wBACZ,MACA,OAKC;EACD,MAAM,OAAO,MAAM,KAAK,gBAAgB,MAAM,MAAM;AACpD,MAAI,CAAC,KACH;AAIF,OAAK,cAAc,KAAK;AACxB,MAAI,KAAK,UACP,MAAK,YAAY,KAAK;AAGxB,MAAI,CAAC,KAAK,KACR;EAIF,IAAI,eAA+C,KAAK,KAAK;AAC7D,MAAI;AACF,kBAAe,KAAK,MAAM,KAAK,KAAK,OAAO;UACrC;EAIR,MAAM,cAAc,QAAQ,gBAAgB;AAC5C,MAAI,YACF,aAAY,cAAc;GACxB,kBAAkB;GAClB,2BAA2B;GAC3B,2BAA2B,QAAQ,IAAI;GACvC,2BAA2B,QAAQ;GACpC,CAAC;AAOJ,SAAO;GAAE,SAJO,KAAK,uBAAuB,aAAa;GAIvC,OAHJ,KAAK,mBAAmB;GAGb,cAFJ,KAAK,KAAK,WAAW,+BAA+B;GAElC;;CAGzC,MAAc,iBAAiB,MAAc,OAK1C;EACD,MAAM,SAAS,MAAM,KAAK,wBAAwB,MAAM,MAAM;AAC9D,MAAI,CAAC,OACH;AAGF,SAAO;GACL,GAAG;GACH,UAAU,EAAE;GACb;;CAGH,MAAc,eAAe,MAAc,OAExC;EACD,MAAM,SAAS,MAAM,KAAK,wBAAwB,MAAM,MAAM;AAC9D,MAAI,CAAC,OACH;AAUF,SAAO,EAAE,QANM,KAAK,+BAClB,OAAO,SACP,OAAO,cACP,OAAO,MACR,EAEgB;;CAGnB,eACE,MACA,SACyD;EACzD,MAAM,eAAe,KAAK,UAAU;AACpC,MAAI,CAAC,aACH,QAAO;EAGT,MAAM,kBAAkB,EACtB,GAAG,SACJ;EAGD,MAAM,iBAAiB,KAAK,wBAAwB,aAAa,OAAO;AACxE,MAAI,eACF,iBAAgB,SACd,KAAK,oBAAoB,gBAAgB,QAAQ,eAAe;AAIpE,MAAI,aAAa,MACf,iBAAgB,QACd,KAAK,mBAAmB,gBAAgB,OAAO,aAAa,MAAM;AAGtE,SAAO;;;;;CAMT,uBACE,QACwD;AACxD,MAAI,OAAO,WAAW,SACpB,QAAO,CAAC;GACN,MAAM;GACN,MAAM;GACP,CAAC;EAGJ,MAAM,cAAc,SAAwD;AAC1E,OAAI,KAAK,SAAS,OAChB,QAAO,CAAC;IACN,MAAM;IACN,MAAM,KAAK,QAAQ;IACpB,CAAC;AAEJ,OAAI,CAAC,aAAa,YAAY,CAAC,SAAS,KAAK,KAAK,CAChD,QAAO,CAAC;IACN,MAAM;IACN,YAAY,KAAK,cAAc,KAAK;IACpC,UAAU,KAAK,YAAY,KAAK;IAChC,OAAO,KAAK,UAAU,KAAK,SAAS,KAAK,UAAU;IACpD,CAAC;AAEJ,OAAI,KAAK,SAAS,YAChB,QAAO,CAAC;IACN,MAAM;IACN,MAAM,KAAK,QAAQ;IACpB,CAAC;AAEJ,UAAO,CAAC;IACN,MAAM;IACN,MAAM,KAAK,UAAU,KAAK;IAC3B,CAAC;;AAGJ,SAAO,OAAO,SAAQ,SAAQ;AAC5B,OAAI,KAAK,QAAQ,KAAK,SAAS;IAC7B,IAAI,gBAAuC,KAAK;AAChD,QAAI;AACF,qBAAgB,KAAK,MAAM,KAAK,QAAQ;YAClC;AACN,SAAI,OAAO,SAAS,SAClB,QAAO,CAAC;MACN,MAAM;MACN,MAAM;MACP,CAAC;;AAGN,WAAO,cAAc,QAAQ,WAAW;;AAE1C,UAAO,WAAW,KAAK;IACvB;;;;;CAMJ,MAAc,gBACZ,MACA,OAC0C;EAC1C,MAAM,YAAY,QAAQ,IAAI;AAC9B,MAAI,CAAC,UACH;AAGF,MAAI;GACF,MAAM,WAAW,MAAM,MAAM,GAAG,UAAU,UAAU;IAClD,QAAQ;IACR,SAAS,EACP,gBAAgB,oBACjB;IACD,MAAM,KAAK,UAAU;KAAE;KAAM;KAAO,CAAC;IACtC,CAAC;AAEF,OAAI,CAAC,SAAS,GAEZ;AAGF,UAAO,MAAM,SAAS,MAAM;UACtB;AAEN;;;;;;CAOJ,wBACE,QACoB;AACpB,MAAI,CAAC,OACH;AAGF,MAAI,OAAO,WAAW,SACpB,QAAO;AAIT,SAAO,OAAO,KAAI,UAAS,MAAM,QAAQ,GAAG,CAAC,KAAK,KAAK;;;;;CAMzD,oBACE,QACA,gBACwD;AACxD,MAAI,CAAC,eACH,QAAO;EAIT,MAAM,gBAAgB,OAAO,QAAO,QAAO,IAAI,SAAS,SAAS;AAGjE,SAAO,CACL;GAAE,MAAM;GAAU,SAAS;GAAgB,EAC3C,GAAG,cACJ;;;;;CAMH,mBAIE,OACA,eAC0B;AAC1B,MAAI,CAAC,iBAAiB,cAAc,WAAW,EAC7C,QAAO;AAGT,MAAI,CAAC,SAAS,MAAM,WAAW,GAAG;GAEhC,MAAM,WAAW,EAAE;AACnB,QAAK,MAAM,YAAY,cACrB,KAAI,SAAS,WACX,UAAS,KAAK;IACZ,MAAM;IACN,MAAM,SAAS;IACf,aAAa,SAAS;IACtB,aAAa,SAAS;IACvB,CAAC;AAGN,UAAO,SAAS,SAAS,IAAI,WAA2B,KAAA;;EAG1D,MAAM,eAAe,CAAC,GAAG,MAAM;AAE/B,OAAK,MAAM,YAAY,eAAe;GACpC,MAAM,oBAAoB,aAAa,WACrC,SAAQ,KAAK,SAAS,cAAc,KAAK,SAAS,SAAS,KAC5D;AAED,OAAI,sBAAsB,IAAI;IAC5B,MAAM,eAAe,aAAa;AAElC,iBAAa,qBAAqB;KAChC,GAAG;KACH,aAAa,SAAS,eAAe,aAAa;KAClD,aAAa,SAAS,aACjB,SAAS,aACT,aAAa;KACnB;cACQ,SAAS,WAElB,cAAa,KAAK;IAChB,MAAM;IACN,MAAM,SAAS;IACf,aAAa,SAAS;IACtB,aAAa,SAAS;IACvB,CAAM;;AAIX,SAAO;;;;;ACtjBX,IAAa,yBAAb,cAA4C,yBAAoD;CAK9F,YAAY,eAAgC;AAC1C,QAAM,cAAc;8BALU;AAM9B,OAAK,qBAAqB;;CAG5B,oBAAoD;AAClD,SAAO;GACL,aAAa;GACb,cAAc;GACd,aAAa;GACd;;CAGH,WAAW,SAaR;AACD,SAAO,KAAK,sBACV,UACC,SAAS,KAAK,mBAAmB,WAAW,KAAK,CACnD;;CAGH,SAAS,SAQN;AACD,SAAO,KAAK,oBACV,UACC,SAAS,KAAK,mBAAmB,SAAS,KAAK,CACjD;;CAGH,+BACE,SACA,cACA,OAC2C;EAC3C,MAAM,QAAqC,EAAE;AAG7C,QAAM,KAAK;GAAE,MAAM;GAAgB,UAAU,EAAE;GAAE,CAAC;EAGlD,IAAI,YAAY;EAChB,IAAI,YAAY;EAChB,IAAI,iBAAiB;AAErB,OAAK,MAAM,SAAS,QAClB,KAAI,MAAM,SAAS,QAAQ;GACzB,MAAM,KAAK,QAAQ;AACnB,SAAM,KAAK;IAAE,MAAM;IAAc;IAAI,CAAC;AACtC,SAAM,KAAK;IAAE,MAAM;IAAc;IAAI,OAAO,MAAM;IAAM,CAAC;AACzD,SAAM,KAAK;IAAE,MAAM;IAAY;IAAI,CAAC;aAC3B,MAAM,SAAS,aAAa;GACrC,MAAM,KAAK,QAAQ;AACnB,SAAM,KAAK;IACT,MAAM;IACN;IACA,UAAU,MAAM;IACjB,CAAC;AACF,SAAM,KAAK;IACT,MAAM;IACN;IACA,OAAO,MAAM;IACd,CAAC;AACF,SAAM,KAAK;IAAE,MAAM;IAAkB;IAAI,CAAC;AAC1C,SAAM,KAAK;IACT,MAAM;IACN,YAAY,MAAM;IAClB,UAAU,MAAM;IAChB,OAAO,MAAM;IACd,CAAC;aACO,MAAM,SAAS,aAAa;GACrC,MAAM,KAAK,aAAa;AACxB,SAAM,KAAK;IAAE,MAAM;IAAmB;IAAI,CAAC;AAC3C,SAAM,KAAK;IAAE,MAAM;IAAmB;IAAI,OAAO,MAAM;IAAM,CAAC;AAC9D,SAAM,KAAK;IAAE,MAAM;IAAiB;IAAI,CAAC;;AAK7C,QAAM,KAAK;GAAE,MAAM;GAAU;GAAO;GAAc,CAAC;AAGnD,SAAO,IAAI,eAAe,EACxB,MAAM,YAAY;AAChB,QAAK,MAAM,QAAQ,MACjB,YAAW,QAAQ,KAAK;AAE1B,cAAW,OAAO;KAErB,CAAC;;;;;AChHN,IAAa,yBAAb,cAA4C,yBAAoD;CAK9F,YAAY,eAAgC;AAC1C,QAAM,cAAc;8BALU;AAM9B,OAAK,qBAAqB;;CAG5B,oBAAoD;AAClD,SAAO;GACL,aAAa;IACX,OAAO;IACP,SAAS;IACT,WAAW;IACX,YAAY;IACb;GACD,cAAc;IACZ,OAAO;IACP,MAAM;IACN,WAAW;IACZ;GACF;;CAGH,WAAW,SAaR;AACD,SAAO,KAAK,sBACV,UACC,SAAS,KAAK,mBAAmB,WAAW,KAAK,CACnD;;CAGH,SAAS,SAQN;AACD,SAAO,KAAK,oBACV,UACC,SAAS,KAAK,mBAAmB,SAAS,KAAK,CACjD;;CAGH,+BACE,SACA,cACA,OAC2C;EAC3C,MAAM,QAAqC,EAAE;AAG7C,QAAM,KAAK;GAAE,MAAM;GAAgB,UAAU,EAAE;GAAE,CAAC;EAGlD,IAAI,YAAY;EAChB,IAAI,YAAY;EAChB,IAAI,iBAAiB;AAErB,OAAK,MAAM,SAAS,QAClB,KAAI,MAAM,SAAS,QAAQ;GACzB,MAAM,KAAK,QAAQ;AACnB,SAAM,KAAK;IAAE,MAAM;IAAc;IAAI,CAAC;AACtC,SAAM,KAAK;IAAE,MAAM;IAAc;IAAI,OAAO,MAAM;IAAM,CAAC;AACzD,SAAM,KAAK;IAAE,MAAM;IAAY;IAAI,CAAC;aAC3B,MAAM,SAAS,aAAa;GACrC,MAAM,KAAK,QAAQ;AACnB,SAAM,KAAK;IACT,MAAM;IACN;IACA,UAAU,MAAM;IACjB,CAAC;AACF,SAAM,KAAK;IACT,MAAM;IACN;IACA,OAAO,MAAM;IACd,CAAC;AACF,SAAM,KAAK;IAAE,MAAM;IAAkB;IAAI,CAAC;AAC1C,SAAM,KAAK;IACT,MAAM;IACN,YAAY,MAAM;IAClB,UAAU,MAAM;IAChB,OAAO,MAAM;IACd,CAAC;aACO,MAAM,SAAS,aAAa;GACrC,MAAM,KAAK,aAAa;AACxB,SAAM,KAAK;IAAE,MAAM;IAAmB;IAAI,CAAC;AAC3C,SAAM,KAAK;IAAE,MAAM;IAAmB;IAAI,OAAO,MAAM;IAAM,CAAC;AAC9D,SAAM,KAAK;IAAE,MAAM;IAAiB;IAAI,CAAC;;AAK7C,QAAM,KAAK;GAAE,MAAM;GAAU;GAAO;GAAc,CAAC;AAGnD,SAAO,IAAI,eAAe,EACxB,MAAM,YAAY;AAChB,QAAK,MAAM,QAAQ,MACjB,YAAW,QAAQ,KAAK;AAE1B,cAAW,OAAO;KAErB,CAAC;;;;;AC/HN,MAAM,eAAe;CACnB;CACA;CACA;CACA;CACA;CACA;CACD;AAED,MAAa,aAAa,OAA6B;CACrD,MAAM,UAA+B,EAAE;AACvC,QAAO,QAAQ,GAAG,CAAC,SAAS,CAAC,KAAK,WAAW;AAC3C,MAAI,OAAO,UAAU,cAAc,aAAa,SAAS,IAAI,EAAE;GAC7D,MAAM,aAAa;AACnB,WAAQ,QAAQ,GAAG,SAAgB;AACjC,QAAI,KAAK,MAAM,OAAO,KAAK,OAAO,UAAU;KAC1C,MAAM,mBAAmB;MACvB,WAAW;MACX,QAAQ,WAAW;MACpB;AACD,UAAK,KAAK;MACR,GAAG,KAAK;MACR,wBAAwB;OACtB,GAAG;OACH,GAAG,KAAK,GAAG;OACZ;MACF;;AAGH,WAAO,WAAW,GAAG,KAAK;;QAG5B,SAAQ,OAAO;GAEjB;AACF,QAAO;;AAKT,SAAgB,kBACd,eACmC;AACnC,KAAI,cAAc,yBAAyB,KACzC,QAAO,IAAI,uBAAuB,cAAc;AAElD,QAAO,IAAI,uBAAuB,cAAc;;;;ACxClD,MAAa,iBAAiB;CAC5B,kBAAkB;CAClB,YAAY;CACZ,aAAa;CACb,WAAW;CACX,eAAe;CAChB;;;ACRD,MAAa,gBAAgB,SAAuB;CAClD,MAAM,KAAK,KAAK,SAAS;AAGzB,QAAO,CAFS,KAAK,MAAM,KAAK,IAAI,EACf,KAAK,MAAO,IACJ;;AAG/B,MAAa,qBAAqB,aAChC,SAAS,MAAM,IAAI,CAAC,OAAO,EAAE,aAAa,CAAC,MAAM,IACjD,SAAS,aAAa,CAAC,MAAM;AAK/B,MAAa,sBAAsB,aAAsC;AACvE,SAAQ,UAAR;EACE,KAAK,eAAe,WAClB,QAAO;EACT,KAAK,eAAe;EACpB,KAAK,eAAe,cAClB,QAAO;EACT,QACE,QAAO;;;AAIb,MAAa,iBAAiB,UAA2B;AACvD,KAAI,OAAO,UAAU,SAAU,QAAO;AACtC,KAAI;AACF,SAAO,KAAK,UAAU,MAAM;SACtB;AACN,SAAO;;;AAKX,MAAa,oBACX,UACgD;AAChD,KAAI,UAAU,KAAA,KAAa,UAAU,KAAM,QAAO,KAAA;AAClD,KACE,OAAO,UAAU,YACjB,OAAO,UAAU,YACjB,OAAO,UAAU,UAEjB,QAAO;AAET,KAAI,MAAM,QAAQ,MAAM,EAAE;AACxB,MAAI,MAAM,OAAO,MAAM,OAAO,MAAM,SAAS,CAAE,QAAO;AACtD,MAAI,MAAM,OAAO,MAAM,OAAO,MAAM,SAAS,CAAE,QAAO;AACtD,MAAI,MAAM,OAAO,MAAM,OAAO,MAAM,UAAU,CAAE,QAAO;;AAEzD,QAAO,cAAc,MAAM;;AAG7B,MAAa,mBAAmB,UAA0C;AACxE,KAAI,CAAC,MAAO,QAAO,KAAA;AACnB,KAAI,MAAM,QAAQ,MAAM,CAAE,QAAO;AACjC,KAAI,OAAO,UAAU,UAAU;EAC7B,MAAM,QAAQ;AACd,MAAI,MAAM,QAAQ,MAAM,SAAS,CAAE,QAAO,MAAM;AAChD,MAAI,MAAM,QAAQ,MAAM,OAAO,CAAE,QAAO,MAAM;AAC9C,MAAI,MAAM,QAAQ,MAAM,MAAM,CAAE,QAAO,MAAM;;;AAWjD,MAAa,0BAA0B,QAAmC;CACxE,MAAM,MAAsB,EAAE;AAC9B,MAAK,MAAM,KAAK,KAAK;AACnB,MAAI,KAAK,KAAM;AACf,MAAI,OAAO,MAAM,UAAU;AACzB,OAAI,KAAK,EAAkB;AAC3B;;AAEF,MAAI,KAAK;GACP,MAAM;GACN,SAAS,OAAO,MAAM,WAAW,IAAI,cAAc,EAAE;GACtD,CAAC;;AAEJ,QAAO;;AAMT,MAAa,uCACX,WACwB;AACxB,KAAI,CAAC,UAAU,OAAO,WAAW,SAAU,QAAO;CAClD,MAAM,SAAS;CACf,MAAM,QAA4B,EAAE;AACpC,KAAI,OAAO,OAAO,SAAS,YAAY,OAAO,KAAK,SAAS,EAC1D,OAAM,KAAK;EAAE,MAAM;EAAQ,MAAM,OAAO;EAAM,CAAC;AAEjD,KAAI,MAAM,QAAQ,OAAO,UAAU,CACjC,MAAK,MAAM,MAAM,OAAO,WAA6C;AACnE,MAAI,CAAC,GAAI;AACT,QAAM,KAAK;GACT,MAAM;GACN,YAAY,GAAG;GACf,UAAU,GAAG;GAGb,OAAO,GAAG,SAAS,GAAG;GACvB,CAAC;;AAGN,KAAI,MAAM,WAAW,EAAG,QAAO;AAC/B,QAAO;EAAE,MAAM;EAAa,SAAS;EAAO;;AAG9C,MAAa,qBACX,SACuB;CACvB,MAAM,IAAI,KAAK,YAAY;AAC3B,QAAO,OAAO,MAAM,WAAW,IAAI,KAAA;;AAQrC,MAAa,uBACX,SAEA,MAAM,QAAQ,wDAAwD,KAAK;AAQ7E,MAAa,iBAAiB,QAAqC;AACjE,KAAI,QAAQ,KAAA,EAAW,QAAO,KAAA;AAC9B,KAAI,OAAO,QAAQ,SAAU,QAAO;AACpC,KAAI;AACF,SAAO,KAAK,UAAU,IAAI;SACpB;AACN,SAAO;;;AAIX,MAAa,eACX,UAC2B;AAC3B,KAAI,CAAC,MAAO,QAAO,EAAE;CACrB,MAAM,MAA8B,EAAE;AACtC,KAAI,OAAO,MAAM,gBAAgB,SAC/B,KAAI,kBAAkB,qBAAqB,MAAM;AAEnD,KAAI,OAAO,MAAM,iBAAiB,SAChC,KAAI,kBAAkB,sBAAsB,MAAM;CAEpD,MAAM,SAAS,MAAM,eAAe,MAAM,MAAM,gBAAgB;AAChE,KAAI,QAAQ,EACV,KAAI,kBAAkB,qBAAqB;AAE7C,KAAI,OAAO,MAAM,cAAc,eAAe,SAC5C,KAAI,8CACF,MAAM,aAAa;AAEvB,KAAI,OAAO,MAAM,cAAc,cAAc,SAC3C,KAAI,0CAA0C,MAAM,aAAa;AAEnE,KAAI,OAAO,MAAM,eAAe,cAAc,SAC5C,KAAI,mCAAmC,MAAM,cAAc;AAE7D,QAAO;;;;AClIT,MAAM,SAAS,kBAAkB;;;;;;;;;;;;;;;;;;;;;;AA4EjC,IAAa,iBAAb,MAA4B;CA0B1B,YAAY,UAAiC,EAAE,EAAE;cAzB1B;kCAO8B,IAAI,KAAK;6CAE5D,IAAI,KAAK;6CAET,IAAI,KAAK;8CACkD,IAAI,KAAK;2CAGZ,IAAI,KAAK;gDAMN,IAAI,KAAK;8BAEvC;AAG7B,OAAK,SAAS;GACZ,UAAU,QAAQ,YAAY;GAC9B,qBAAqB,QAAQ,uBAAuB;GACrD;;CAQH,KAAK,UAA0B;CAE/B,MAAM,mBAAmB,OAA0C;EACjE,MAAM,OAAO,MAAM;AAKnB,MAAI,KAAK,QAAS;AAQlB,MAAI,KAAK,SAAS,eAAe,oBAAoB,KAAK,YAAY;GACpE,MAAM,WAAW,KAAK,oBAAoB,IAAI,KAAK,GAAG;AAKtD,QAAK,oBAAoB,IACvB,KAAK,IACL,WACI;IAAE,GAAG;IAAU,GAAG,KAAK;IAAY,GACnC,EAAE,GAAG,KAAK,YAAY,CAC3B;;AAGH,MAAI,MAAM,SAAS,gBAAgB;AACjC,QAAK,kBAAkB,KAAK;AAC5B;;AAEF,MAAI,MAAM,SAAS,aAAc;AACjC,QAAM,KAAK,gBAAgB,KAAK;;CAGlC,MAAM,eAAe,OAA0C;AAC7D,QAAM,KAAK,mBAAmB,MAAM;;CAGtC,MAAM,QAAuB;EAG3B,MAAM,YAAY,kBAAkB;AACpC,MAAI,CAAC,UAAW;AAChB,MAAI;AACF,SAAM,UAAU,YAAY;WACrB,KAAK;AACZ,UAAO,MACL,uCAAuC,eAAe,QAAQ,IAAI,UAAU,OAAO,IAAI,GACxF;;;CAIL,WAA0B;AAGxB,OAAK,SAAS,OAAO;AACrB,OAAK,oBAAoB,OAAO;AAChC,OAAK,oBAAoB,OAAO;AAChC,OAAK,qBAAqB,OAAO;AACjC,OAAK,kBAAkB,OAAO;AAC9B,OAAK,uBAAuB,OAAO;AACnC,SAAO,QAAQ,SAAS;;CAG1B,kBAA0B,MAAgC;AACxD,MAAI,KAAK,SAAS,eAAe,YAAa;EAE9C,MAAM,aAAa,KAAK,sBAAsB,KAAK,QAAQ;EAC3D,MAAM,WAAW,KAAK;AAEtB,OAAK,eAAe,MAAM,WAAW;AACrC,aAAW,cAAc,IAAI,KAAK,GAAG;AAErC,MAAI,KAAK,SAAS,eAAe,iBAC/B,MAAK,oBAAoB,KAAK;WACrB,KAAK,SAAS,eAAe,cAAc,UAAU;AAC9D,QAAK,qBAAqB,IAAI,KAAK,IAAI,SAAS;GAChD,MAAM,YACJ,OAAO,KAAK,YAAY,cAAc,WAClC,KAAK,WAAW,YAChB;AACN,QAAK,kBAAkB,IAAI,KAAK,IAAI,UAAU;;AAGhD,OAAK,cAAc,MAAM,WAAW;;CAQtC,eACE,MACA,YACM;EACN,MAAM,WAAW,KAAK;EACtB,MAAM,aAAa,WACf,WAAW,aAAa,IAAI,SAAS,GACrC,WAAW;EACf,MAAM,gBAAgB,WAClB,WAAW,gBAAgB,IAAI,SAAS,GACxC,WAAW;EAQf,MAAM,WAAW,iBAAiB,oBAAoB,KAAK,GAAG,CAAC;EAC/D,MAAM,WAAW,aAAa,CAAC,GAAG,YAAY,KAAK,KAAK,GAAG,CAAC,KAAK,KAAK;EACtE,MAAM,cAAc,gBAChB,CAAC,GAAG,eAAe,SAAS,GAC5B,CAAC,SAAS;AAEd,aAAW,aAAa,IAAI,KAAK,IAAI,SAAS;AAC9C,aAAW,gBAAgB,IAAI,KAAK,IAAI,YAAY;;CAGtD,MAAc,gBAAgB,MAAyC;AACrE,MAAI,KAAK,SAAS,eAAe,aAAa;AAQ5C,QAAK,sBAAsB,KAAK;AAChC;;EAGF,MAAM,aAAa,KAAK,sBAAsB,KAAK,QAAQ;AAE3D,MAAI;AAIF,OAAI,CAAC,WAAW,aAAa,IAAI,KAAK,GAAG,EAAE;AACzC,SAAK,eAAe,MAAM,WAAW;AAGrC,eAAW,cAAc,IAAI,KAAK,GAAG;;AAEvC,OACE,KAAK,SAAS,eAAe,oBAC7B,CAAC,KAAK,oBAAoB,IAAI,KAAK,GAAG,CAEtC,MAAK,oBAAoB,KAAK;AAEhC,OACE,KAAK,SAAS,eAAe,cAC7B,CAAC,KAAK,qBAAqB,IAAI,KAAK,GAAG,IACvC,KAAK,aAEL,MAAK,qBAAqB,IAAI,KAAK,IAAI,KAAK,aAAa;AAE3D,OAAI,CAAC,KAAK,uBAAuB,IAAI,KAAK,GAAG,CAK3C,MAAK,cAAc,MAAM,WAAW;AAKtC,QAAK,+BAA+B,KAAK;GAEzC,MAAM,WAAW,KAAK,uBAAuB,IAAI,KAAK,GAAG;AACzD,OAAI,CAAC,SAIH;AAGF,QAAK,mBAAmB,MAAM,UAAU,WAAW;AAEnD,OAAI,KAAK,WAAW;AAClB,aAAS,UAAU;KACjB,MAAM,eAAe;KACrB,SAAS,KAAK,UAAU;KACzB,CAAC;AACF,aAAS,gBAAgB;KACvB,MAAM,KAAK,UAAU,QAAQ;KAC7B,SAAS,KAAK,UAAU;KACxB,OAAO,KAAK,UAAU;KACvB,CAAC;;GAGJ,MAAM,UAAU,KAAK,UACjB,aAAa,KAAK,QAAQ,GAC1B,aAAa,KAAK,UAAU;AAChC,YAAS,IAAI,QAAQ;AAIrB,OAAI,KAAK,OAAO,SACd,OAAM,kBAAkB,EAAE,YAAY;WAEjC,KAAK;AACZ,UAAO,MACL,0CAA0C,KAAK,GAAG,IAChD,eAAe,QAAQ,IAAI,UAAU,OAAO,IAAI,GAEnD;YACO;AACR,cAAW,cAAc,OAAO,KAAK,GAAG;AACxC,OAAI,WAAW,cAAc,SAAS,EACpC,MAAK,SAAS,OAAO,KAAK,QAAQ;AAEpC,OAAI,KAAK,SAAS,eAAe,kBAAkB;AACjD,SAAK,oBAAoB,OAAO,KAAK,GAAG;AACxC,SAAK,oBAAoB,OAAO,KAAK,GAAG;;AAE1C,OAAI,KAAK,SAAS,eAAe,YAAY;AAC3C,SAAK,qBAAqB,OAAO,KAAK,GAAG;AACzC,SAAK,kBAAkB,OAAO,KAAK,GAAG;;AAExC,QAAK,uBAAuB,OAAO,KAAK,GAAG;;;CAI/C,oBAA4B,MAAgC;EAC1D,MAAM,cAAc,gBAAgB,KAAK,MAAM;EAC/C,MAAM,eAAe,cAAc,uBAAuB,YAAY,GAAG,EAAE;AAC3E,OAAK,oBAAoB,IAAI,KAAK,IAAI;GACpC;GACA,kCAAkB,IAAI,KAAK;GAC3B,6CAA6B,IAAI,KAAK;GACtC,0CAA0B,IAAI,KAAK;GACpC,CAAC;;CAaJ,sBAA8B,MAAgC;AAE5D,OADc,KAAK,cAAc,EAAE,EACzB,cAAc,YAAa;EACrC,MAAM,SAAS,KAAK;AACpB,MAAI,CAAC,UAAU,OAAO,WAAW,SAAU;EAC3C,MAAM,OAAQ,OAAmC;AACjD,MAAI,OAAO,SAAS,YAAY,KAAK,WAAW,EAAG;EAEnD,MAAM,aAAa,KAAK;AACxB,MAAI,CAAC,WAAY;EACjB,MAAM,eAAe,KAAK,qBAAqB,IAAI,WAAW;AAC9D,MAAI,CAAC,aAAc;EACnB,MAAM,MAAM,KAAK,oBAAoB,IAAI,aAAa;AACtD,MAAI,CAAC,IAAK;EACV,MAAM,YAAY,KAAK,kBAAkB,IAAI,WAAW;AACxD,MAAI,cAAc,KAAA,EAAW;EAE7B,MAAM,WAAW,IAAI,yBAAyB,IAAI,UAAU;AAC5D,MAAI,yBAAyB,IAC3B,WACA,WAAW,WAAW,OAAO,KAC9B;;CAGH,+BAAuC,MAAgC;AACrE,MAAI,KAAK,SAAS,eAAe,YAAY;GAC3C,MAAM,eAAe,KAAK,qBAAqB,IAAI,KAAK,GAAG;AAC3D,OAAI,CAAC,aAAc;GACnB,MAAM,MAAM,KAAK,oBAAoB,IAAI,aAAa;AACtD,OAAI,CAAC,IAAK;GACV,MAAM,YACJ,KAAK,kBAAkB,IAAI,KAAK,GAAG,KAClC,OAAO,KAAK,YAAY,cAAc,WACnC,KAAK,WAAW,YAChB;AACN,QAAK,kBAAkB,IAAI,KAAK,IAAI,UAAU;GAO9C,MAAM,SAAS,KAAK;GACpB,MAAM,oBAAoB,MAAM,QAAQ,QAAQ,UAAU,GACrD,OAAO,YACR,EAAE;GACN,MAAM,WAAW,IAAI,4BAA4B,IAAI,UAAU,IAAI,EAAE;GACrE,MAAM,+BAAe,IAAI,KAA4B;AACrD,QAAK,MAAM,KAAK,SACd,KAAI,EAAE,WAAY,cAAa,IAAI,EAAE,YAAY,EAAE;GAIrD,MAAM,2BAAW,IAAI,KAAoB;GAEzC,MAAM,eAA+B,EAAE;GACvC,MAAM,eAAe,oCAAoC,OAAO;AAChE,OAAI,aAAc,cAAa,KAAK,aAAa;AAEjD,QAAK,MAAM,OAAO,mBAAmB;IACnC,MAAM,aACH,KAAK,cACL,KAAK;IAQR,IAAI,QAAQ,aAAa,aAAa,IAAI,WAAW,GAAG,KAAA;AACxD,QAAI,SAAS,SAAS,IAAI,MAAM,CAAE,SAAQ,KAAA;AAC1C,QAAI,CAAC,MAEH,SAAQ,SAAS,MAAM,MAAM,CAAC,SAAS,IAAI,EAAE,CAAC;AAEhD,QAAI,CAAC,MAAO;AACZ,aAAS,IAAI,MAAM;IACnB,MAAM,qBAAqB,cAAc,MAAM;IAC/C,MAAM,WACH,KAAK,YACL,KAAK,QACN,MAAM;AACR,iBAAa,KAAK;KAChB,MAAM;KACN,cAAc;KACd,SAAS,CACP;MACE,MAAM;MACN,YAAY;MACZ;MACA,QAAQ,MAAM;MACf,CACF;KACF,CAAC;;AAGJ,OAAI,iBAAiB,IAAI,WAAW,aAAa;aAEjD,KAAK,SAAS,eAAe,aAC7B,KAAK,SAAS,eAAe,eAC7B;GAKA,MAAM,WAAW,KAAK;AACtB,OAAI,CAAC,SAAU;GACf,MAAM,eAAe,KAAK,qBAAqB,IAAI,SAAS;AAC5D,OAAI,CAAC,aAAc;GACnB,MAAM,MAAM,KAAK,oBAAoB,IAAI,aAAa;AACtD,OAAI,CAAC,IAAK;GACV,MAAM,YAAY,KAAK,kBAAkB,IAAI,SAAS;AACtD,OAAI,cAAc,KAAA,EAAW;GAC7B,MAAM,OAAO,IAAI,4BAA4B,IAAI,UAAU,IAAI,EAAE;AACjE,QAAK,KAAK;IACR,YAAY,kBAAkB,KAAK;IACnC,UAAU,oBAAoB,KAAK,KAAK,IAAI;IAC5C,OAAO,KAAK;IACZ,QAAQ,KAAK;IACd,CAAC;AACF,OAAI,4BAA4B,IAAI,WAAW,KAAK;;;CAIxD,sBAA8B,SAA6B;EACzD,MAAM,WAAW,KAAK,SAAS,IAAI,QAAQ;AAC3C,MAAI,SAAU,QAAO;EACrB,MAAM,UAAsB;GAC1B,8BAAc,IAAI,KAAK;GACvB,iCAAiB,IAAI,KAAK;GAC1B,+BAAe,IAAI,KAAK;GACzB;AAeD,MAAI,KAAK,QAAQ,qBAAqB;GACpC,MAAM,aACJ,MAAM,QAAQ,sBAAsB,YAAY,CAAC,IACjD,MAAM,eAAe;GACvB,MAAM,MAAM,YAAY,aAAa;AACrC,OAAI,OAAO,IAAI,WAAW,IAAI,QAAQ;AACpC,YAAQ,cAAc,qBAAqB,IAAI,QAAQ;AACvD,YAAQ,uBAAuB,oBAAoB,IAAI,OAAO;IAK9D,MAAM,QACJ,WACA;AACF,QAAI,OAAO;KACT,MAAM,aAAa,MAAM;AACzB,SACE,MAAM,QAAQ,WAAW,IACzB,WAAW,OAAO,MAAM,OAAO,MAAM,SAAS,CAE9C,SAAQ,qBAAqB;KAE/B,MAAM,gBAAgB,MAAM;AAC5B,SACE,MAAM,QAAQ,cAAc,IAC5B,cAAc,OAAO,MAAM,OAAO,MAAM,SAAS,CAEjD,SAAQ,wBAAwB;;;;AAKxC,OAAK,SAAS,IAAI,SAAS,QAAQ;AACnC,SAAO;;CAQT,cACE,MACA,YACM;EAKN,MAAM,SAAS,mBAAmB,CAAC,UACjC,iBACAC,QACD;EACD,MAAM,YAAY,KAAK,mBAAmB,MAAM,WAAW;EAE3D,MAAM,WAAW,OAAO,UACtB,KAAK,MACL;GACE,WAAW,aAAa,KAAK,UAAU;GACvC,MAAM,SAAS;GAChB,EACD,UACD;AAED,MAAI,CAAC,SAAS,aAAa,EAAE;AAC3B,QAAK,wBAAwB;AAC7B;;EASF,MAAM,qBAAqB,SAAS,aAAa,CAAC;EAKlD,MAAM,eAAe,oBAAoB,KAAK,GAAG;EACjD,MAAM,gBACJ,WAAW,eAAe,qBAAqB,KAAK,QAAQ;AAC9D,SAAO,OAAO,SAAS,aAAa,EAAE;GACpC,SAAS;GACT,QAAQ;GACT,CAAC;AAKF,MAAI,uBAAuB,aACzB,mBAAkB,EAAE,aAAa,mBAAmB;EAKtD,MAAM,yBAAyB,KAAK,eAChC,oBAAoB,KAAK,aAAa,GACtC,WAAW;AACf,MAAI,wBAAwB;GAC1B,MAAM,oBAAiC;IACrC,SAAS;IACT,QAAQ;IACR,YAAY,WAAW;IACvB,UAAU;IACX;AACD,UAAO,OAAO,UAAU;IACtB,cAAc;IACd;IACD,CAAC;QAIF,QAAO,OAAO,UAAU;GACtB,cAAc,KAAA;GACd,mBAAmB,KAAA;GACpB,CAAC;EAOJ,MAAM,WAAW,WAAW,aAAa,IAAI,KAAK,GAAG;EACrD,MAAM,cAAc,WAAW,gBAAgB,IAAI,KAAK,GAAG;AAC3D,MAAI,SAAU,UAAS,aAAa,WAAW,SAAS;AACxD,MAAI,YAAa,UAAS,aAAa,eAAe,YAAY;AAElE,OAAK,uBAAuB,IAAI,KAAK,IAAI,SAAS;;CAGpD,yBAAuC;AACrC,MAAI,KAAK,qBAAsB;AAC/B,OAAK,uBAAuB;AAC5B,SAAO,KACL,6KAGD;;CAUH,mBACE,MACA,YACS;EACT,MAAM,UACJ,sBAAsB,YAAY,IAAIC,QAAW,QAAQ;AAE3D,MAAI,KAAK,cAAc;GACrB,MAAM,aAAa,KAAK,uBAAuB,IAAI,KAAK,aAAa;AACrE,OAAI,WAAY,QAAO,MAAM,QAAQ,SAAS,WAAW;;AAG3D,MAAI,WAAW,eAAe,WAAW,sBAAsB;GAC7D,MAAM,UAAU,MAAM,gBAAgB;IACpC,SAAS,WAAW;IACpB,QAAQ,WAAW;IACnB,YAAY,WAAW;IACvB,UAAU;IACX,CAAC;AACF,UAAO,MAAM,QAAQ,SAAS,QAAQ;;AAGxC,SAAO;;CAGT,mBACE,MACA,UACA,YACM;EACN,MAAM,aAAa,KAAK,uBAAuB,MAAM,WAAW;AAIhE,SAAO,WAAW;AAClB,SAAO,WAAW;AAClB,WAAS,cAAc,WAAW;;CAGpC,uBACE,MACA,YACqB;EACrB,MAAM,aAAkC,EAAE;EAE1C,MAAM,WAAW,WAAW,aAAa,IAAI,KAAK,GAAG;EACrD,MAAM,cAAc,WAAW,gBAAgB,IAAI,KAAK,GAAG;AAC3D,MAAI,SAAU,YAAW,aAAa;AACtC,MAAI,YAAa,YAAW,iBAAiB;EAE7C,MAAM,kBAAkB,mBAAmB,KAAK,KAAK;AACrD,aAAW,aAAa;AACxB,aAAW,+BAA+B;AAC1C,aAAW,oBAAoBD;EAC/B,MAAM,cAAc,gBAAgB;AACpC,MAAI,YAAa,YAAW,yBAAyB;AAIrD,aAAW,oCAAoC,KAAK;EAEpD,MAAM,WAAW,KAAK,YAAY,EAAE;EACpC,MAAM,YAAY,SAAS,aAAa,SAAS;AACjD,MAAI,OAAO,cAAc,YAAY,UAAU,SAAS,EACtD,YAAW,cAAc;EAE3B,MAAM,SAAS,SAAS,UAAU,SAAS;AAC3C,MAAI,OAAO,WAAW,YAAY,OAAO,SAAS,EAChD,YAAW,WAAW;AAExB,OAAK,MAAM,CAAC,KAAK,UAAU,OAAO,QAAQ,SAAS,EAAE;AACnD,OACE,QAAQ,eACR,QAAQ,gBACR,QAAQ,YACR,QAAQ,aACR,UAAU,KAAA,KACV,UAAU,KAEV;GAEF,MAAM,KAAK,iBAAiB,MAAM;AAClC,OAAI,OAAO,KAAA,EACT,YAAW,GAAG,uBAAuB,YAAY,SAAS;;AAG9D,MAAI,KAAK,cAAc,KAAK,MAAM,OAChC,YAAW,GAAG,uBAAuB,UAAU,KAAK;AAGtD,MAAI,oBAAoB,MACtB,MAAK,mBAAmB,MAAM,WAAW;WAChC,oBAAoB,OAC7B,MAAK,oBAAoB,MAAM,WAAW;OACrC;AACL,OAAI,KAAK,UAAU,KAAA,EACjB,YAAW,cAAc,cAAc,KAAK,MAAM;AAEpD,OAAI,KAAK,WAAW,KAAA,EAClB,YAAW,eAAe,cAAc,KAAK,OAAO;;AAIxD,SAAO;;CAGT,mBACE,MACA,YACM;EACN,MAAM,eACJ,KAAK,qBAAqB,IAAI,KAAK,GAAG,IAAI,KAAK;EACjD,MAAM,MAAM,eACR,KAAK,oBAAoB,IAAI,aAAa,GAC1C,KAAA;EACJ,MAAM,kBAAkB,eACpB,KAAK,oBAAoB,IAAI,aAAa,GAC1C,KAAA;EAEJ,MAAM,YAAY,KAAK,cAAc,EAAE;EACvC,MAAM,YACJ,KAAK,kBAAkB,IAAI,KAAK,GAAG,KAClC,OAAO,UAAU,cAAc,WAAW,UAAU,YAAY;EAEnE,MAAM,WAAW,iBAAiB;EAClC,MAAM,QAAQ,iBAAiB;EAC/B,MAAM,gBAAgB,iBAAiB;AACvC,MAAI,SACF,YAAW,kBAAkB,YAAY,kBAAkB,SAAS;AACtE,MAAI,MAAO,YAAW,kBAAkB,iBAAiB;AACzD,MAAI,cACF,YAAW,kBAAkB,kBAAkB;WACxC,MAAO,YAAW,kBAAkB,kBAAkB;EAE/D,MAAM,QAAS,UAAU,SAAS,iBAAiB;AAGnD,SAAO,OAAO,YAAY,YAAY,MAAM,CAAC;AAE7C,MAAI,OAAO,UAAU,iBAAiB,UAAU;AAC9C,cAAW,mCAAmC,UAAU;AACxD,cAAW,8BAA8B,UAAU;;EAKrD,MAAM,WAA2B,MAAM,CAAC,GAAG,IAAI,aAAa,GAAG,EAAE;AACjE,MAAI,IACF,MAAK,IAAI,IAAI,GAAG,IAAI,WAAW,KAAK;GAClC,MAAM,OAAO,IAAI,iBAAiB,IAAI,EAAE;AACxC,OAAI,KAAM,UAAS,KAAK,GAAG,KAAK;;AAIpC,MAAI,SAAS,SAAS,EACpB,YAAW,wBAAwB,cAAc,SAAS;WACjD,KAAK,UAAU,KAAA,EACxB,YAAW,cAAc,cAAc,KAAK,MAAM;EAGpD,MAAM,MAAM,KAAK;EACjB,MAAM,gBAAgB,KAAK,yBAAyB,IAAI,UAAU;AAClE,MAAI,OAAO,OAAO,QAAQ,UAAU;GAClC,MAAM,SAAS;AACf,OAAI,OAAO,OAAO,SAAS,YAAY,OAAO,KAAK,SAAS,EAC1D,YAAW,sBAAsB,OAAO;GAE1C,MAAM,sBACJ,MAAM,QAAQ,OAAO,UAAU,IAAI,OAAO,UAAU,SAAS,IACxD,OAAO,UAA6C,KAAK,QAAQ;IAClE,cAAc,GAAG,gBAAgB;IACjC,YAAY,GAAG;IACf,UAAU,GAAG;IACb,MAAM,cAAc,GAAG,QAAQ,GAAG,MAAM;IACzC,EAAE,GACD,EAAE;AACR,OAAI,oBAAoB,SAAS,EAC/B,YAAW,2BACT,KAAK,UAAU,oBAAoB;AAOvC,OAAI,OAAO,kBAAkB,YAAY,cAAc,SAAS,GAAG;IACjE,MAAM,QAAwC,CAC5C;KAAE,MAAM;KAAY,SAAS;KAAe,CAC7C;AACD,QAAI,OAAO,OAAO,SAAS,YAAY,OAAO,KAAK,SAAS,EAC1D,OAAM,KAAK;KAAE,MAAM;KAAQ,SAAS,OAAO;KAAM,CAAC;AAEpD,SAAK,MAAM,MAAM,qBAAqB;KACpC,IAAI,UAAmB,GAAG;AAC1B,SAAI,OAAO,YAAY,SACrB,KAAI;AACF,gBAAU,KAAK,MAAM,QAAQ;aACvB;AAIV,WAAM,KAAK;MACT,MAAM;MACN,IAAI,GAAG;MACP,MAAM,GAAG;MACT,WAAW;MACZ,CAAC;;AAEJ,eAAW,4BAA4B,KAAK,UAAU,CACpD;KAAE,MAAM;KAAa;KAAO,CAC7B,CAAC;;aAEK,QAAQ,KAAA,EACjB,YAAW,eAAe,cAAc,IAAI;;CAIhD,oBACE,MACA,YACM;AACN,MAAI,KAAK,UAAU,KAAA,EACjB,YAAW,cAAc,cAAc,KAAK,MAAM;AAEpD,MAAI,KAAK,WAAW,KAAA,EAClB,YAAW,eAAe,cAAc,KAAK,OAAO;EAEtD,MAAM,YAAY,KAAK,cAAc,EAAE;EAGvC,MAAM,cAAc,oBAAoB,KAAK,KAAK,IAAI,KAAK;AAC3D,MAAI,YAAa,YAAW,sBAAsB;AAClD,MAAI,OAAO,UAAU,aAAa,SAChC,YAAW,sBAAsB,UAAU;AAE7C,MAAI,OAAO,UAAU,cAAc,SACjC,YAAW,gBAAgB,UAAU"}
|