@mastra/core
Version:
484 lines (483 loc) • 15.2 kB
JavaScript
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