UNPKG

@mastra/core

Version:
484 lines (483 loc) 15.2 kB
const require_utils = require("./utils-CNiGU0Uf.cjs"); require("./tracing-BUrUJwCM.cjs"); //#region src/observability/types/core.ts /** * Sampling strategy types */ let SamplingStrategyType = /* @__PURE__ */ function(SamplingStrategyType) { SamplingStrategyType["ALWAYS"] = "always"; SamplingStrategyType["NEVER"] = "never"; SamplingStrategyType["RATIO"] = "ratio"; SamplingStrategyType["CUSTOM"] = "custom"; return SamplingStrategyType; }({}); //#endregion //#region src/observability/types/metrics.ts /** * Default labels to block from metrics to prevent cardinality explosion. * These are high-cardinality fields that should not be used as metric labels. */ const DEFAULT_BLOCKED_LABELS = [ "trace_id", "span_id", "run_id", "request_id", "user_id", "resource_id", "session_id", "thread_id" ]; //#endregion //#region src/observability/no-op.ts const noOpCounter = { add() {} }; const noOpGauge = { set() {} }; const noOpHistogram = { record() {} }; /** * No-op tracing context used when observability is not configured. */ const noOpTracingContext = { currentSpan: void 0 }; /** * No-op logger context that silently discards all log calls. * Used when observability is not configured. */ const noOpLoggerContext = { debug() {}, info() {}, warn() {}, error() {}, fatal() {} }; /** * No-op metrics context that silently discards all metric operations. * Used when observability is not configured. */ const noOpMetricsContext = { emit() {}, counter() { return noOpCounter; }, gauge() { return noOpGauge; }, histogram() { return noOpHistogram; } }; /** No-op observability entrypoint that silently discards all operations. */ var NoOpObservability = class { setMastraContext(_options) {} setLogger(_options) {} getSelectedInstance(_options) {} async getRecordedTrace(_args) { return null; } async addScore(_args) {} async addFeedback(_args) {} registerInstance(_name, _instance, _isDefault = false) {} getInstance(_name) {} getDefaultInstance() {} listInstances() { return /* @__PURE__ */ new Map(); } unregisterInstance(_name) { return false; } hasInstance(_name) { return false; } setConfigSelector(_selector) {} clear() {} async flush() {} async shutdown() {} }; //#endregion //#region src/observability/context-factory.ts /** * Derives a LoggerContext from the current span's ObservabilityInstance. * Falls back to no-op when there is no span or the instance doesn't support logging. */ function deriveLoggerContext(tracing) { const span = tracing.currentSpan; return span?.observabilityInstance?.getLoggerContext?.(span) ?? noOpLoggerContext; } /** * Derives a MetricsContext from the current span's ObservabilityInstance. * Falls back to no-op when there is no span or the instance doesn't support metrics. */ function deriveMetricsContext(tracing) { const span = tracing.currentSpan; return span?.observabilityInstance?.getMetricsContext?.(span) ?? noOpMetricsContext; } /** * Creates an observability context with real or no-op implementations for * tracing, logging, and metrics. * * When a TracingContext with a current span is provided, the logger and metrics * contexts are derived from the span's ObservabilityInstance so that log entries * and metric data points are automatically correlated to the active trace. * * @param tracingContext - TracingContext with current span, or undefined for no-op * @returns ObservabilityContext with all three signals (tracing, logger, metrics) */ function createObservabilityContext(tracingContext) { const tracing = tracingContext ?? noOpTracingContext; return { tracing, loggerVNext: deriveLoggerContext(tracing), metrics: deriveMetricsContext(tracing), tracingContext: tracing }; } /** * Resolves a partial observability context (from execute params) into a * complete ObservabilityContext with no-op defaults for any missing fields. * * Explicitly provided logger/metrics contexts are preserved (e.g. when set * upstream). When missing, they are derived from the tracing context's span, * following the same derivation logic as createObservabilityContext(). * * @param partial - Partial context from ExecuteFunctionParams * @returns Complete ObservabilityContext */ function resolveObservabilityContext(partial) { const tracing = partial.tracing ?? partial.tracingContext ?? noOpTracingContext; return { tracing, loggerVNext: partial.loggerVNext ?? deriveLoggerContext(tracing), metrics: partial.metrics ?? deriveMetricsContext(tracing), tracingContext: tracing }; } //#endregion //#region src/observability/context.ts const AGENT_GETTERS = ["getAgent", "getAgentById"]; const AGENT_METHODS_TO_WRAP = [ "generate", "stream", "generateLegacy", "streamLegacy" ]; const WORKFLOW_GETTERS = ["getWorkflow", "getWorkflowById"]; const WORKFLOW_METHODS_TO_WRAP = [ "execute", "createRun", "createRun" ]; /** * Helper function to detect NoOp spans to avoid unnecessary wrapping */ function isNoOpSpan(span) { return span.constructor.name === "NoOpSpan" || span.__isNoOp === true; } /** * Checks to see if a passed object is an actual instance of Mastra * (for the purposes of wrapping it for Tracing) */ function isMastra(mastra) { const hasAgentGetters = AGENT_GETTERS.every((method) => typeof mastra?.[method] === "function"); const hasWorkflowGetters = WORKFLOW_GETTERS.every((method) => typeof mastra?.[method] === "function"); return hasAgentGetters && hasWorkflowGetters; } /** * Creates a tracing-aware Mastra proxy that automatically injects * tracing context into agent and workflow method calls */ function wrapMastra(mastra, tracingContext) { if (!tracingContext.currentSpan || isNoOpSpan(tracingContext.currentSpan)) return mastra; if (!isMastra(mastra)) return mastra; try { return new Proxy(mastra, { get(target, prop) { try { if (AGENT_GETTERS.includes(prop)) return (...args) => { return wrapAgent(target[prop](...args), tracingContext); }; if (WORKFLOW_GETTERS.includes(prop)) return (...args) => { return wrapWorkflow(target[prop](...args), tracingContext); }; const value = target[prop]; return typeof value === "function" ? value.bind(target) : value; } catch (error) { console.warn("Tracing: Failed to wrap method, falling back to original", error); const value = target[prop]; return typeof value === "function" ? value.bind(target) : value; } } }); } catch (error) { console.warn("Tracing: Failed to create proxy, using original Mastra instance", error); return mastra; } } /** * Creates a tracing-aware Agent proxy that automatically injects * tracing context into generation method calls */ function wrapAgent(agent, tracingContext) { if (!tracingContext.currentSpan || isNoOpSpan(tracingContext.currentSpan)) return agent; try { return new Proxy(agent, { get(target, prop) { try { if (AGENT_METHODS_TO_WRAP.includes(prop)) return (input, options = {}) => { return target[prop](input, { ...options, ...createObservabilityContext(tracingContext) }); }; const value = target[prop]; return typeof value === "function" ? value.bind(target) : value; } catch (error) { console.warn("Tracing: Failed to wrap agent method, falling back to original", error); const value = target[prop]; return typeof value === "function" ? value.bind(target) : value; } } }); } catch (error) { console.warn("Tracing: Failed to create agent proxy, using original instance", error); return agent; } } /** * Creates a tracing-aware Workflow proxy that automatically injects * tracing context into execution method calls */ function wrapWorkflow(workflow, tracingContext) { if (!tracingContext.currentSpan || isNoOpSpan(tracingContext.currentSpan)) return workflow; try { return new Proxy(workflow, { get(target, prop) { try { if (WORKFLOW_METHODS_TO_WRAP.includes(prop)) { if (prop === "createRun" || prop === "createRun") return async (options = {}) => { const run = await target[prop](options); return run ? wrapRun(run, tracingContext) : run; }; return (input, options = {}) => { return target[prop](input, { ...options, ...createObservabilityContext(tracingContext) }); }; } const value = target[prop]; return typeof value === "function" ? value.bind(target) : value; } catch (error) { console.warn("Tracing: Failed to wrap workflow method, falling back to original", error); const value = target[prop]; return typeof value === "function" ? value.bind(target) : value; } } }); } catch (error) { console.warn("Tracing: Failed to create workflow proxy, using original instance", error); return workflow; } } /** * Creates a tracing-aware Run proxy that automatically injects * tracing context into start method calls */ function wrapRun(run, tracingContext) { if (!tracingContext.currentSpan || isNoOpSpan(tracingContext.currentSpan)) return run; try { return new Proxy(run, { get(target, prop) { try { if (prop === "start") return (startOptions = {}) => { return target.start({ ...startOptions, ...createObservabilityContext(startOptions.tracingContext ?? tracingContext) }); }; const value = target[prop]; return typeof value === "function" ? value.bind(target) : value; } catch (error) { console.warn("Tracing: Failed to wrap run method, falling back to original", error); const value = target[prop]; return typeof value === "function" ? value.bind(target) : value; } } }); } catch (error) { console.warn("Tracing: Failed to create run proxy, using original instance", error); return run; } } //#endregion //#region src/observability/rag-ingestion.ts /** * RAG ingestion helpers. * * Provides a thin wrapper around `getOrCreateSpan` for starting a * `RAG_INGESTION` root span without reaching into observability internals. * * Two surfaces: * - `startRagIngestion(...)` — manual: returns `{ span, observabilityContext }`, * caller is responsible for `.end()` / `.error()`. * - `withRagIngestion(opts, fn)` — scoped: runs `fn(observabilityContext)`, * automatically attaches the return value as the span's `output` and * routes thrown errors to `span.error(...)`. * * ## Observability data * * Mastra emits raw span data (start/end timestamps, attributes, input, * output) — exporters and downstream consumers do their own aggregation. * The shapes below are designed to make the common derivations cheap: * * - **Duration**: every span has start/end, so per-operation latency * falls out for free. * - **Embedding cost**: `RAG_EMBEDDING` spans (and only `RAG_EMBEDDING` * spans) expose `attributes.usage` using the same `UsageStats` shape as * `MODEL_GENERATION`, so any existing LLM cost-extraction pipeline that * parses `usage.inputTokens` handles embeddings uniformly. Cost * dimensions are `{model, provider, mode}` (mode is `'ingest'` or * `'query'`). Token counts are deliberately NOT duplicated on the * `RAG_INGESTION` root — aggregating at the root would double-count * when summing child spans. Mirrors how `AGENT_RUN` does not carry * aggregated `MODEL_GENERATION` usage. * - **Vector store throughput**: `RAG_VECTOR_OPERATION` spans carry * `{operation, store, indexName}` as attributes; result counts live on * `output` (e.g. `output.returned`, `output.vectorCount`). * - **Ingestion roll-ups**: `RAG_INGESTION` (root) carries * `{vectorStore, indexName, embeddingModel, embeddingProvider}` as * attributes and aggregate `usage` summed across child embed calls. */ /** * Start a `RAG_INGESTION` root span. Caller is responsible for closing it * via `result.span?.end(...)` or `result.span?.error(...)`. * * Prefer `withRagIngestion` for the common try/catch/end flow. * * @example * ```ts * const { span, observabilityContext } = startRagIngestion({ * mastra, * name: 'docs ingestion', * attributes: { vectorStore: 'pgvector', indexName: 'docs' }, * }); * try { * const chunks = await doc.chunk({ observabilityContext }); * // ... * span?.end({ output: { chunkCount: chunks.length } }); * } catch (err) { * span?.error({ error: err as Error }); * throw err; * } * ``` */ function startRagIngestion(options) { const span = require_utils.getOrCreateSpan({ ...options, entityType: require_utils.EntityType.RAG_INGESTION, type: "rag_ingestion" }); return { span, observabilityContext: createObservabilityContext(span ? { currentSpan: span } : void 0) }; } /** * Run an async function inside a `RAG_INGESTION` root span. * * The callback receives an `ObservabilityContext` to thread into chunk, * embed, and vector-store calls. The return value is attached to the span * as `output`. Thrown errors are recorded via `span.error(...)` and * re-thrown. * * @example * ```ts * await withRagIngestion( * { * mastra, * name: 'docs ingestion', * attributes: { vectorStore: 'pgvector', indexName: 'docs' }, * }, * async (observabilityContext) => { * const chunks = await doc.chunk({ observabilityContext }); * const { embeddings } = await embed(chunks, { observabilityContext }); * await vectorStore.upsert({ * indexName: 'docs', * vectors: embeddings, * observabilityContext, * }); * return { chunkCount: chunks.length }; * }, * ); * ``` */ async function withRagIngestion(options, fn) { const { span, observabilityContext } = startRagIngestion(options); try { const result = await fn(observabilityContext); span?.end({ output: result }); return result; } catch (err) { span?.error({ error: err, endSpan: true }); throw err; } } //#endregion Object.defineProperty(exports, "DEFAULT_BLOCKED_LABELS", { enumerable: true, get: function() { return DEFAULT_BLOCKED_LABELS; } }); Object.defineProperty(exports, "NoOpObservability", { enumerable: true, get: function() { return NoOpObservability; } }); Object.defineProperty(exports, "SamplingStrategyType", { enumerable: true, get: function() { return SamplingStrategyType; } }); Object.defineProperty(exports, "createObservabilityContext", { enumerable: true, get: function() { return createObservabilityContext; } }); Object.defineProperty(exports, "noOpLoggerContext", { enumerable: true, get: function() { return noOpLoggerContext; } }); Object.defineProperty(exports, "noOpMetricsContext", { enumerable: true, get: function() { return noOpMetricsContext; } }); Object.defineProperty(exports, "noOpTracingContext", { enumerable: true, get: function() { return noOpTracingContext; } }); Object.defineProperty(exports, "resolveObservabilityContext", { enumerable: true, get: function() { return resolveObservabilityContext; } }); Object.defineProperty(exports, "startRagIngestion", { enumerable: true, get: function() { return startRagIngestion; } }); Object.defineProperty(exports, "withRagIngestion", { enumerable: true, get: function() { return withRagIngestion; } }); Object.defineProperty(exports, "wrapMastra", { enumerable: true, get: function() { return wrapMastra; } }); //# sourceMappingURL=observability-BzV5axz0.cjs.map