UNPKG

@genkit-ai/core

Version:

Genkit AI framework core libraries.

1 lines 14.2 kB
{"version":3,"sources":["../../src/tracing/instrumentation.ts"],"sourcesContent":["/**\n * Copyright 2024 Google LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport {\n ROOT_CONTEXT,\n SpanStatusCode,\n trace,\n type Span as ApiSpan,\n type Link,\n} from '@opentelemetry/api';\nimport { performance } from 'node:perf_hooks';\nimport type { HasRegistry, Registry } from '../registry.js';\nimport { ensureBasicTelemetryInstrumentation } from '../tracing.js';\nimport type { PathMetadata, SpanMetadata, TraceMetadata } from './types.js';\n\nexport const spanMetadataAlsKey = 'core.tracing.instrumentation.span';\nexport const traceMetadataAlsKey = 'core.tracing.instrumentation.trace';\n\nexport const ATTR_PREFIX = 'genkit';\n/** @hidden */\nexport const SPAN_TYPE_ATTR = ATTR_PREFIX + ':type';\nconst TRACER_NAME = 'genkit-tracer';\nconst TRACER_VERSION = 'v1';\n\n/**\n * @hidden\n */\nexport async function newTrace<T>(\n registry: Registry | HasRegistry,\n opts: {\n name: string;\n labels?: Record<string, string>;\n links?: Link[];\n },\n fn: (metadata: SpanMetadata, rootSpan: ApiSpan) => Promise<T>\n) {\n registry = (registry as HasRegistry).registry\n ? (registry as HasRegistry).registry\n : (registry as Registry);\n\n await ensureBasicTelemetryInstrumentation();\n const traceMetadata: TraceMetadata = registry.asyncStore.getStore(\n traceMetadataAlsKey\n ) || {\n paths: new Set<PathMetadata>(),\n timestamp: performance.now(),\n featureName: opts.name,\n };\n return await registry.asyncStore.run(traceMetadataAlsKey, traceMetadata, () =>\n runInNewSpan(\n registry,\n {\n metadata: {\n name: opts.name,\n },\n labels: opts.labels,\n links: opts.links,\n },\n async (metadata, otSpan) => {\n return await fn(metadata, otSpan);\n }\n )\n );\n}\n\n/**\n * Runs the provided function in a new span.\n *\n * @hidden\n */\nexport async function runInNewSpan<T>(\n registry: Registry | HasRegistry,\n opts: {\n metadata: SpanMetadata;\n labels?: Record<string, string>;\n links?: Link[];\n },\n fn: (metadata: SpanMetadata, otSpan: ApiSpan, isRoot: boolean) => Promise<T>\n): Promise<T> {\n await ensureBasicTelemetryInstrumentation();\n const resolvedRegistry = (registry as HasRegistry).registry\n ? (registry as HasRegistry).registry\n : (registry as Registry);\n\n const tracer = trace.getTracer(TRACER_NAME, TRACER_VERSION);\n const parentStep =\n resolvedRegistry.asyncStore.getStore<SpanMetadata>(spanMetadataAlsKey);\n const isInRoot = parentStep?.isRoot === true;\n if (!parentStep) opts.metadata.isRoot ||= true;\n return await tracer.startActiveSpan(\n opts.metadata.name,\n { links: opts.links, root: opts.metadata.isRoot },\n async (otSpan) => {\n if (opts.labels) otSpan.setAttributes(opts.labels);\n try {\n opts.metadata.path = buildPath(\n opts.metadata.name,\n parentStep?.path || '',\n opts.labels\n );\n\n const output = await resolvedRegistry.asyncStore.run(\n spanMetadataAlsKey,\n opts.metadata,\n () => fn(opts.metadata, otSpan, isInRoot)\n );\n if (opts.metadata.state !== 'error') {\n opts.metadata.state = 'success';\n }\n\n recordPath(resolvedRegistry, opts.metadata);\n return output;\n } catch (e) {\n recordPath(resolvedRegistry, opts.metadata, e);\n opts.metadata.state = 'error';\n otSpan.setStatus({\n code: SpanStatusCode.ERROR,\n message: getErrorMessage(e),\n });\n if (e instanceof Error) {\n otSpan.recordException(e);\n }\n\n // Mark the first failing span as the source of failure. Prevent parent\n // spans that catch re-thrown exceptions from also claiming to be the\n // source.\n if (typeof e === 'object') {\n if (!(e as any).ignoreFailedSpan) {\n opts.metadata.isFailureSource = true;\n }\n (e as any).ignoreFailedSpan = true;\n }\n\n throw e;\n } finally {\n otSpan.setAttributes(metadataToAttributes(opts.metadata));\n otSpan.end();\n }\n }\n );\n}\n\n/**\n * Creates a new child span and attaches it to a previously created trace. This\n * is useful, for example, for adding deferred user engagement metadata.\n *\n * @hidden\n */\nexport async function appendSpan(\n traceId: string,\n parentSpanId: string,\n metadata: SpanMetadata,\n labels?: Record<string, string>\n) {\n await ensureBasicTelemetryInstrumentation();\n\n const tracer = trace.getTracer(TRACER_NAME, TRACER_VERSION);\n\n const spanContext = trace.setSpanContext(ROOT_CONTEXT, {\n traceId: traceId,\n traceFlags: 1, // sampled\n spanId: parentSpanId,\n });\n\n // TODO(abrook): add explicit start time to align with parent\n const span = tracer.startSpan(metadata.name, {}, spanContext);\n span.setAttributes(metadataToAttributes(metadata));\n if (labels) {\n span.setAttributes(labels);\n }\n span.end();\n}\n\nfunction getErrorMessage(e: any): string {\n if (e instanceof Error) {\n return e.message;\n }\n return `${e}`;\n}\n\nfunction metadataToAttributes(metadata: SpanMetadata): Record<string, string> {\n const out = {} as Record<string, string>;\n Object.keys(metadata).forEach((key) => {\n if (\n key === 'metadata' &&\n typeof metadata[key] === 'object' &&\n metadata.metadata\n ) {\n Object.entries(metadata.metadata).forEach(([metaKey, value]) => {\n out[ATTR_PREFIX + ':metadata:' + metaKey] = value;\n });\n } else if (key === 'input' || typeof metadata[key] === 'object') {\n out[ATTR_PREFIX + ':' + key] = JSON.stringify(metadata[key]);\n } else {\n out[ATTR_PREFIX + ':' + key] = metadata[key];\n }\n });\n return out;\n}\n\n/**\n * Sets provided attribute value in the current span.\n *\n * @hidden\n */\nexport function setCustomMetadataAttribute(\n registry: Registry,\n key: string,\n value: string\n) {\n const currentStep = getCurrentSpan(registry);\n if (!currentStep) {\n return;\n }\n if (!currentStep.metadata) {\n currentStep.metadata = {};\n }\n currentStep.metadata[key] = value;\n}\n\n/**\n * Sets provided attribute values in the current span.\n *\n * @hidden\n */\nexport function setCustomMetadataAttributes(\n registry: Registry,\n values: Record<string, string>\n) {\n const currentStep = getCurrentSpan(registry);\n if (!currentStep) {\n return;\n }\n if (!currentStep.metadata) {\n currentStep.metadata = {};\n }\n for (const [key, value] of Object.entries(values)) {\n currentStep.metadata[key] = value;\n }\n}\n\n/**\n * Converts a fully annotated path to a friendly display version for logs\n *\n * @hidden\n */\nexport function toDisplayPath(path: string): string {\n const pathPartRegex = /\\{([^\\,}]+),[^\\}]+\\}/g;\n return Array.from(path.matchAll(pathPartRegex), (m) => m[1]).join(' > ');\n}\n\nfunction getCurrentSpan(registry: Registry): SpanMetadata {\n const step = registry.asyncStore.getStore<SpanMetadata>(spanMetadataAlsKey);\n if (!step) {\n throw new Error('running outside step context');\n }\n return step;\n}\n\nfunction buildPath(\n name: string,\n parentPath: string,\n labels?: Record<string, string>\n) {\n const stepType =\n labels && labels['genkit:type']\n ? `,t:${labels['genkit:metadata:subtype'] === 'flow' ? 'flow' : labels['genkit:type']}`\n : '';\n return parentPath + `/{${name}${stepType}}`;\n}\n\nfunction recordPath(registry: Registry, spanMeta: SpanMetadata, err?: any) {\n const path = spanMeta.path || '';\n const decoratedPath = decoratePathWithSubtype(spanMeta);\n // Only add the path if a child has not already been added. In the event that\n // an error is rethrown, we don't want to add each step in the unwind.\n const paths = Array.from(\n registry.asyncStore.getStore<TraceMetadata>(traceMetadataAlsKey)?.paths ||\n new Set<PathMetadata>()\n );\n const status = err ? 'failure' : 'success';\n if (!paths.some((p) => p.path.startsWith(path) && p.status === status)) {\n const now = performance.now();\n const start =\n registry.asyncStore.getStore<TraceMetadata>(traceMetadataAlsKey)\n ?.timestamp || now;\n registry.asyncStore\n .getStore<TraceMetadata>(traceMetadataAlsKey)\n ?.paths?.add({\n path: decoratedPath,\n error: err?.name,\n latency: now - start,\n status,\n });\n }\n spanMeta.path = decoratedPath;\n}\n\nfunction decoratePathWithSubtype(metadata: SpanMetadata): string {\n if (!metadata.path) {\n return '';\n }\n\n const pathComponents = metadata.path.split('}/{');\n\n if (pathComponents.length == 1) {\n return metadata.path;\n }\n\n const stepSubtype =\n metadata.metadata && metadata.metadata['subtype']\n ? `,s:${metadata.metadata['subtype']}`\n : '';\n const root = `${pathComponents.slice(0, -1).join('}/{')}}/`;\n const decoratedStep = `{${pathComponents.at(-1)?.slice(0, -1)}${stepSubtype}}`;\n return root + decoratedStep;\n}\n"],"mappings":"AAgBA;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,OAGK;AACP,SAAS,mBAAmB;AAE5B,SAAS,2CAA2C;AAG7C,MAAM,qBAAqB;AAC3B,MAAM,sBAAsB;AAE5B,MAAM,cAAc;AAEpB,MAAM,iBAAiB,cAAc;AAC5C,MAAM,cAAc;AACpB,MAAM,iBAAiB;AAKvB,eAAsB,SACpB,UACA,MAKA,IACA;AACA,aAAY,SAAyB,WAChC,SAAyB,WACzB;AAEL,QAAM,oCAAoC;AAC1C,QAAM,gBAA+B,SAAS,WAAW;AAAA,IACvD;AAAA,EACF,KAAK;AAAA,IACH,OAAO,oBAAI,IAAkB;AAAA,IAC7B,WAAW,YAAY,IAAI;AAAA,IAC3B,aAAa,KAAK;AAAA,EACpB;AACA,SAAO,MAAM,SAAS,WAAW;AAAA,IAAI;AAAA,IAAqB;AAAA,IAAe,MACvE;AAAA,MACE;AAAA,MACA;AAAA,QACE,UAAU;AAAA,UACR,MAAM,KAAK;AAAA,QACb;AAAA,QACA,QAAQ,KAAK;AAAA,QACb,OAAO,KAAK;AAAA,MACd;AAAA,MACA,OAAO,UAAU,WAAW;AAC1B,eAAO,MAAM,GAAG,UAAU,MAAM;AAAA,MAClC;AAAA,IACF;AAAA,EACF;AACF;AAOA,eAAsB,aACpB,UACA,MAKA,IACY;AACZ,QAAM,oCAAoC;AAC1C,QAAM,mBAAoB,SAAyB,WAC9C,SAAyB,WACzB;AAEL,QAAM,SAAS,MAAM,UAAU,aAAa,cAAc;AAC1D,QAAM,aACJ,iBAAiB,WAAW,SAAuB,kBAAkB;AACvE,QAAM,WAAW,YAAY,WAAW;AACxC,MAAI,CAAC,WAAY,MAAK,SAAS,WAAW;AAC1C,SAAO,MAAM,OAAO;AAAA,IAClB,KAAK,SAAS;AAAA,IACd,EAAE,OAAO,KAAK,OAAO,MAAM,KAAK,SAAS,OAAO;AAAA,IAChD,OAAO,WAAW;AAChB,UAAI,KAAK,OAAQ,QAAO,cAAc,KAAK,MAAM;AACjD,UAAI;AACF,aAAK,SAAS,OAAO;AAAA,UACnB,KAAK,SAAS;AAAA,UACd,YAAY,QAAQ;AAAA,UACpB,KAAK;AAAA,QACP;AAEA,cAAM,SAAS,MAAM,iBAAiB,WAAW;AAAA,UAC/C;AAAA,UACA,KAAK;AAAA,UACL,MAAM,GAAG,KAAK,UAAU,QAAQ,QAAQ;AAAA,QAC1C;AACA,YAAI,KAAK,SAAS,UAAU,SAAS;AACnC,eAAK,SAAS,QAAQ;AAAA,QACxB;AAEA,mBAAW,kBAAkB,KAAK,QAAQ;AAC1C,eAAO;AAAA,MACT,SAAS,GAAG;AACV,mBAAW,kBAAkB,KAAK,UAAU,CAAC;AAC7C,aAAK,SAAS,QAAQ;AACtB,eAAO,UAAU;AAAA,UACf,MAAM,eAAe;AAAA,UACrB,SAAS,gBAAgB,CAAC;AAAA,QAC5B,CAAC;AACD,YAAI,aAAa,OAAO;AACtB,iBAAO,gBAAgB,CAAC;AAAA,QAC1B;AAKA,YAAI,OAAO,MAAM,UAAU;AACzB,cAAI,CAAE,EAAU,kBAAkB;AAChC,iBAAK,SAAS,kBAAkB;AAAA,UAClC;AACA,UAAC,EAAU,mBAAmB;AAAA,QAChC;AAEA,cAAM;AAAA,MACR,UAAE;AACA,eAAO,cAAc,qBAAqB,KAAK,QAAQ,CAAC;AACxD,eAAO,IAAI;AAAA,MACb;AAAA,IACF;AAAA,EACF;AACF;AAQA,eAAsB,WACpB,SACA,cACA,UACA,QACA;AACA,QAAM,oCAAoC;AAE1C,QAAM,SAAS,MAAM,UAAU,aAAa,cAAc;AAE1D,QAAM,cAAc,MAAM,eAAe,cAAc;AAAA,IACrD;AAAA,IACA,YAAY;AAAA;AAAA,IACZ,QAAQ;AAAA,EACV,CAAC;AAGD,QAAM,OAAO,OAAO,UAAU,SAAS,MAAM,CAAC,GAAG,WAAW;AAC5D,OAAK,cAAc,qBAAqB,QAAQ,CAAC;AACjD,MAAI,QAAQ;AACV,SAAK,cAAc,MAAM;AAAA,EAC3B;AACA,OAAK,IAAI;AACX;AAEA,SAAS,gBAAgB,GAAgB;AACvC,MAAI,aAAa,OAAO;AACtB,WAAO,EAAE;AAAA,EACX;AACA,SAAO,GAAG,CAAC;AACb;AAEA,SAAS,qBAAqB,UAAgD;AAC5E,QAAM,MAAM,CAAC;AACb,SAAO,KAAK,QAAQ,EAAE,QAAQ,CAAC,QAAQ;AACrC,QACE,QAAQ,cACR,OAAO,SAAS,GAAG,MAAM,YACzB,SAAS,UACT;AACA,aAAO,QAAQ,SAAS,QAAQ,EAAE,QAAQ,CAAC,CAAC,SAAS,KAAK,MAAM;AAC9D,YAAI,cAAc,eAAe,OAAO,IAAI;AAAA,MAC9C,CAAC;AAAA,IACH,WAAW,QAAQ,WAAW,OAAO,SAAS,GAAG,MAAM,UAAU;AAC/D,UAAI,cAAc,MAAM,GAAG,IAAI,KAAK,UAAU,SAAS,GAAG,CAAC;AAAA,IAC7D,OAAO;AACL,UAAI,cAAc,MAAM,GAAG,IAAI,SAAS,GAAG;AAAA,IAC7C;AAAA,EACF,CAAC;AACD,SAAO;AACT;AAOO,SAAS,2BACd,UACA,KACA,OACA;AACA,QAAM,cAAc,eAAe,QAAQ;AAC3C,MAAI,CAAC,aAAa;AAChB;AAAA,EACF;AACA,MAAI,CAAC,YAAY,UAAU;AACzB,gBAAY,WAAW,CAAC;AAAA,EAC1B;AACA,cAAY,SAAS,GAAG,IAAI;AAC9B;AAOO,SAAS,4BACd,UACA,QACA;AACA,QAAM,cAAc,eAAe,QAAQ;AAC3C,MAAI,CAAC,aAAa;AAChB;AAAA,EACF;AACA,MAAI,CAAC,YAAY,UAAU;AACzB,gBAAY,WAAW,CAAC;AAAA,EAC1B;AACA,aAAW,CAAC,KAAK,KAAK,KAAK,OAAO,QAAQ,MAAM,GAAG;AACjD,gBAAY,SAAS,GAAG,IAAI;AAAA,EAC9B;AACF;AAOO,SAAS,cAAc,MAAsB;AAClD,QAAM,gBAAgB;AACtB,SAAO,MAAM,KAAK,KAAK,SAAS,aAAa,GAAG,CAAC,MAAM,EAAE,CAAC,CAAC,EAAE,KAAK,KAAK;AACzE;AAEA,SAAS,eAAe,UAAkC;AACxD,QAAM,OAAO,SAAS,WAAW,SAAuB,kBAAkB;AAC1E,MAAI,CAAC,MAAM;AACT,UAAM,IAAI,MAAM,8BAA8B;AAAA,EAChD;AACA,SAAO;AACT;AAEA,SAAS,UACP,MACA,YACA,QACA;AACA,QAAM,WACJ,UAAU,OAAO,aAAa,IAC1B,MAAM,OAAO,yBAAyB,MAAM,SAAS,SAAS,OAAO,aAAa,CAAC,KACnF;AACN,SAAO,aAAa,KAAK,IAAI,GAAG,QAAQ;AAC1C;AAEA,SAAS,WAAW,UAAoB,UAAwB,KAAW;AACzE,QAAM,OAAO,SAAS,QAAQ;AAC9B,QAAM,gBAAgB,wBAAwB,QAAQ;AAGtD,QAAM,QAAQ,MAAM;AAAA,IAClB,SAAS,WAAW,SAAwB,mBAAmB,GAAG,SAChE,oBAAI,IAAkB;AAAA,EAC1B;AACA,QAAM,SAAS,MAAM,YAAY;AACjC,MAAI,CAAC,MAAM,KAAK,CAAC,MAAM,EAAE,KAAK,WAAW,IAAI,KAAK,EAAE,WAAW,MAAM,GAAG;AACtE,UAAM,MAAM,YAAY,IAAI;AAC5B,UAAM,QACJ,SAAS,WAAW,SAAwB,mBAAmB,GAC3D,aAAa;AACnB,aAAS,WACN,SAAwB,mBAAmB,GAC1C,OAAO,IAAI;AAAA,MACX,MAAM;AAAA,MACN,OAAO,KAAK;AAAA,MACZ,SAAS,MAAM;AAAA,MACf;AAAA,IACF,CAAC;AAAA,EACL;AACA,WAAS,OAAO;AAClB;AAEA,SAAS,wBAAwB,UAAgC;AAC/D,MAAI,CAAC,SAAS,MAAM;AAClB,WAAO;AAAA,EACT;AAEA,QAAM,iBAAiB,SAAS,KAAK,MAAM,KAAK;AAEhD,MAAI,eAAe,UAAU,GAAG;AAC9B,WAAO,SAAS;AAAA,EAClB;AAEA,QAAM,cACJ,SAAS,YAAY,SAAS,SAAS,SAAS,IAC5C,MAAM,SAAS,SAAS,SAAS,CAAC,KAClC;AACN,QAAM,OAAO,GAAG,eAAe,MAAM,GAAG,EAAE,EAAE,KAAK,KAAK,CAAC;AACvD,QAAM,gBAAgB,IAAI,eAAe,GAAG,EAAE,GAAG,MAAM,GAAG,EAAE,CAAC,GAAG,WAAW;AAC3E,SAAO,OAAO;AAChB;","names":[]}