@latitude-data/telemetry 1.0.4 → 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -4,6 +4,7 @@ var zod = require('zod');
4
4
  var otel = require('@opentelemetry/api');
5
5
  var semanticConventions = require('@opentelemetry/semantic-conventions');
6
6
  var incubating = require('@opentelemetry/semantic-conventions/incubating');
7
+ var uuid = require('uuid');
7
8
  var baggageSpanProcessor = require('@opentelemetry/baggage-span-processor');
8
9
  var contextAsyncHooks = require('@opentelemetry/context-async-hooks');
9
10
  var core = require('@opentelemetry/core');
@@ -94,7 +95,7 @@ const DEFAULT_REDACT_SPAN_PROCESSOR = () => new RedactSpanProcessor({
94
95
  attributes: [
95
96
  /^.*auth.*$/i,
96
97
  /^.*authorization.*$/i,
97
- /^(?!ai\.).*usage.*$/i,
98
+ /^(?!gen_ai\.).*usage.*$/i,
98
99
  /^(?!gen_ai\.).*token.*$/i,
99
100
  /^.*secret.*$/i,
100
101
  /^.*key.*$/i,
@@ -208,6 +209,7 @@ const compositeEvaluationConfiguration = baseEvaluationConfiguration.extend({
208
209
  evaluationUuids: zod.z.array(zod.z.string()),
209
210
  minThreshold: zod.z.number().optional(), // Threshold percentage
210
211
  maxThreshold: zod.z.number().optional(), // Threshold percentage
212
+ defaultTarget: zod.z.boolean().optional(), // Default for optimizations and distillations
211
213
  });
212
214
  const compositeEvaluationResultMetadata = baseEvaluationResultMetadata.extend({
213
215
  results: zod.z.record(zod.z.string(), // Evaluation uuid
@@ -275,7 +277,7 @@ const CompositeEvaluationSpecification = {
275
277
  };
276
278
 
277
279
  const humanEvaluationConfiguration = baseEvaluationConfiguration.extend({
278
- enableControls: zod.z.boolean().optional(),
280
+ enableControls: zod.z.boolean().optional(), // UI annotation controls
279
281
  criteria: zod.z.string().optional(),
280
282
  });
281
283
  const humanEvaluationResultMetadata = baseEvaluationResultMetadata.extend({
@@ -803,6 +805,9 @@ var SpanType;
803
805
  SpanType["Http"] = "http";
804
806
  SpanType["Unknown"] = "unknown";
805
807
  SpanType["Prompt"] = "prompt";
808
+ SpanType["Chat"] = "chat";
809
+ SpanType["External"] = "external";
810
+ SpanType["UnresolvedExternal"] = "unresolved_external";
806
811
  SpanType["Step"] = "step";
807
812
  })(SpanType || (SpanType = {}));
808
813
  const SPAN_SPECIFICATIONS = {
@@ -854,6 +859,24 @@ const SPAN_SPECIFICATIONS = {
854
859
  isGenAI: false,
855
860
  isHidden: false,
856
861
  },
862
+ [SpanType.Chat]: {
863
+ name: 'Chat',
864
+ description: 'A chat continuation span',
865
+ isGenAI: false,
866
+ isHidden: false,
867
+ },
868
+ [SpanType.External]: {
869
+ name: 'External',
870
+ description: 'An external capture span',
871
+ isGenAI: false,
872
+ isHidden: false,
873
+ },
874
+ [SpanType.UnresolvedExternal]: {
875
+ name: 'Unresolved External',
876
+ description: 'An external span that needs path resolution before storage',
877
+ isGenAI: false,
878
+ isHidden: true,
879
+ },
857
880
  [SpanType.Step]: {
858
881
  name: 'Step',
859
882
  description: 'A step span',
@@ -907,7 +930,16 @@ var InstrumentationScope;
907
930
  /* Note: non-standard OpenTelemetry semantic conventions used in Latitude */
908
931
  const ATTR_LATITUDE = 'latitude';
909
932
  const ATTR_LATITUDE_TYPE = `${ATTR_LATITUDE}.type`;
933
+ const ATTR_LATITUDE_DOCUMENT_UUID = `${ATTR_LATITUDE}.document_uuid`;
934
+ const ATTR_LATITUDE_PROMPT_PATH = `${ATTR_LATITUDE}.prompt_path`;
935
+ const ATTR_LATITUDE_COMMIT_UUID = `${ATTR_LATITUDE}.commit_uuid`;
936
+ const ATTR_LATITUDE_DOCUMENT_LOG_UUID = `${ATTR_LATITUDE}.document_log_uuid`;
937
+ const ATTR_LATITUDE_PROJECT_ID = `${ATTR_LATITUDE}.project_id`;
938
+ const ATTR_LATITUDE_EXPERIMENT_UUID = `${ATTR_LATITUDE}.experiment_uuid`;
939
+ const ATTR_LATITUDE_SOURCE = `${ATTR_LATITUDE}.source`;
940
+ const ATTR_LATITUDE_EXTERNAL_ID = `${ATTR_LATITUDE}.external_id`;
910
941
  const ATTR_LATITUDE_TEST_DEPLOYMENT_ID = `${ATTR_LATITUDE}.test_deployment_id`;
942
+ const ATTR_LATITUDE_PREVIOUS_TRACE_ID = `${ATTR_LATITUDE}.previous_trace_id`;
911
943
  const GEN_AI_TOOL_TYPE_VALUE_FUNCTION = 'function';
912
944
  const ATTR_GEN_AI_TOOL_CALL_ARGUMENTS = 'gen_ai.tool.call.arguments';
913
945
  const ATTR_GEN_AI_TOOL_RESULT_VALUE = 'gen_ai.tool.result.value';
@@ -1064,6 +1096,7 @@ var DocumentTriggerParameters;
1064
1096
  DocumentTriggerParameters["Body"] = "body";
1065
1097
  DocumentTriggerParameters["Attachments"] = "attachments";
1066
1098
  })(DocumentTriggerParameters || (DocumentTriggerParameters = {}));
1099
+ const DOCUMENT_PATH_REGEXP = /^([\w-]+\/)*([\w-.])+$/;
1067
1100
 
1068
1101
  class ManualInstrumentation {
1069
1102
  enabled;
@@ -1082,7 +1115,33 @@ class ManualInstrumentation {
1082
1115
  this.enabled = false;
1083
1116
  }
1084
1117
  resume(ctx) {
1085
- return otel.propagation.extract(otel__namespace.ROOT_CONTEXT, ctx);
1118
+ const parts = ctx.traceparent.split('-');
1119
+ if (parts.length !== 4) {
1120
+ return otel__namespace.ROOT_CONTEXT;
1121
+ }
1122
+ const [, traceId, spanId, flags] = parts;
1123
+ if (!traceId || !spanId) {
1124
+ return otel__namespace.ROOT_CONTEXT;
1125
+ }
1126
+ const spanContext = {
1127
+ traceId,
1128
+ spanId,
1129
+ traceFlags: parseInt(flags ?? '01', 16),
1130
+ isRemote: true,
1131
+ };
1132
+ let context = otel.trace.setSpanContext(otel__namespace.ROOT_CONTEXT, spanContext);
1133
+ if (ctx.baggage) {
1134
+ const baggageEntries = {};
1135
+ for (const pair of ctx.baggage.split(',')) {
1136
+ const [key, value] = pair.split('=');
1137
+ if (key && value) {
1138
+ baggageEntries[key] = { value: decodeURIComponent(value) };
1139
+ }
1140
+ }
1141
+ const baggage = otel.propagation.createBaggage(baggageEntries);
1142
+ context = otel.propagation.setBaggage(context, baggage);
1143
+ }
1144
+ return context;
1086
1145
  }
1087
1146
  capitalize(str) {
1088
1147
  if (str.length === 0)
@@ -1389,7 +1448,7 @@ class ManualInstrumentation {
1389
1448
  completion(ctx, options) {
1390
1449
  const start = options;
1391
1450
  const configuration = {
1392
- ...start.configuration,
1451
+ ...(start.configuration ?? {}),
1393
1452
  model: start.model,
1394
1453
  };
1395
1454
  let jsonConfiguration = '';
@@ -1400,14 +1459,15 @@ class ManualInstrumentation {
1400
1459
  jsonConfiguration = '{}';
1401
1460
  }
1402
1461
  const attrConfiguration = this.attribifyConfiguration('input', configuration);
1462
+ const input = start.input ?? [];
1403
1463
  let jsonInput = '';
1404
1464
  try {
1405
- jsonInput = JSON.stringify(start.input);
1465
+ jsonInput = JSON.stringify(input);
1406
1466
  }
1407
1467
  catch (error) {
1408
1468
  jsonInput = '[]';
1409
1469
  }
1410
- const attrInput = this.attribifyMessages('input', start.input);
1470
+ const attrInput = this.attribifyMessages('input', input);
1411
1471
  const span = this.span(ctx, start.name || `${start.provider} / ${start.model}`, SpanType.Completion, {
1412
1472
  attributes: {
1413
1473
  [incubating.ATTR_GEN_AI_SYSTEM]: start.provider,
@@ -1416,37 +1476,45 @@ class ManualInstrumentation {
1416
1476
  [ATTR_GEN_AI_REQUEST_MESSAGES]: jsonInput,
1417
1477
  ...attrInput,
1418
1478
  ...(start.attributes || {}),
1419
- ['latitude.commitUuid']: start.versionUuid,
1420
- ['latitude.documentUuid']: start.promptUuid,
1421
- ['latitude.experimentUuid']: start.experimentUuid,
1479
+ [ATTR_LATITUDE_COMMIT_UUID]: start.versionUuid,
1480
+ [ATTR_LATITUDE_DOCUMENT_UUID]: start.promptUuid,
1481
+ [ATTR_LATITUDE_EXPERIMENT_UUID]: start.experimentUuid,
1422
1482
  },
1423
1483
  });
1424
1484
  return {
1425
1485
  context: span.context,
1426
1486
  end: (options) => {
1427
- const end = options;
1487
+ const end = options ?? {};
1488
+ const output = end.output ?? [];
1428
1489
  let jsonOutput = '';
1429
1490
  try {
1430
- jsonOutput = JSON.stringify(end.output);
1491
+ jsonOutput = JSON.stringify(output);
1431
1492
  }
1432
1493
  catch (error) {
1433
1494
  jsonOutput = '[]';
1434
1495
  }
1435
- const attrOutput = this.attribifyMessages('output', end.output);
1436
- const inputTokens = end.tokens.prompt + end.tokens.cached;
1437
- const outputTokens = end.tokens.reasoning + end.tokens.completion;
1496
+ const attrOutput = this.attribifyMessages('output', output);
1497
+ const tokens = {
1498
+ prompt: end.tokens?.prompt ?? 0,
1499
+ cached: end.tokens?.cached ?? 0,
1500
+ reasoning: end.tokens?.reasoning ?? 0,
1501
+ completion: end.tokens?.completion ?? 0,
1502
+ };
1503
+ const inputTokens = tokens.prompt + tokens.cached;
1504
+ const outputTokens = tokens.reasoning + tokens.completion;
1505
+ const finishReason = end.finishReason ?? '';
1438
1506
  span.end({
1439
1507
  attributes: {
1440
1508
  [ATTR_GEN_AI_RESPONSE_MESSAGES]: jsonOutput,
1441
1509
  ...attrOutput,
1442
1510
  [incubating.ATTR_GEN_AI_USAGE_INPUT_TOKENS]: inputTokens,
1443
- [ATTR_GEN_AI_USAGE_PROMPT_TOKENS]: end.tokens.prompt,
1444
- [ATTR_GEN_AI_USAGE_CACHED_TOKENS]: end.tokens.cached,
1445
- [ATTR_GEN_AI_USAGE_REASONING_TOKENS]: end.tokens.reasoning,
1446
- [ATTR_GEN_AI_USAGE_COMPLETION_TOKENS]: end.tokens.completion,
1511
+ [ATTR_GEN_AI_USAGE_PROMPT_TOKENS]: tokens.prompt,
1512
+ [ATTR_GEN_AI_USAGE_CACHED_TOKENS]: tokens.cached,
1513
+ [ATTR_GEN_AI_USAGE_REASONING_TOKENS]: tokens.reasoning,
1514
+ [ATTR_GEN_AI_USAGE_COMPLETION_TOKENS]: tokens.completion,
1447
1515
  [incubating.ATTR_GEN_AI_USAGE_OUTPUT_TOKENS]: outputTokens,
1448
1516
  [incubating.ATTR_GEN_AI_RESPONSE_MODEL]: start.model,
1449
- [incubating.ATTR_GEN_AI_RESPONSE_FINISH_REASONS]: [end.finishReason],
1517
+ [incubating.ATTR_GEN_AI_RESPONSE_FINISH_REASONS]: [finishReason],
1450
1518
  ...(end.attributes || {}),
1451
1519
  },
1452
1520
  });
@@ -1544,16 +1612,18 @@ class ManualInstrumentation {
1544
1612
  const attributes = {
1545
1613
  [ATTR_GEN_AI_REQUEST_TEMPLATE]: template,
1546
1614
  [ATTR_GEN_AI_REQUEST_PARAMETERS]: jsonParameters,
1547
- ['latitude.commitUuid']: versionUuid || HEAD_COMMIT,
1548
- ['latitude.documentUuid']: promptUuid,
1549
- ['latitude.projectId']: projectId,
1550
- ...(documentLogUuid && { ['latitude.documentLogUuid']: documentLogUuid }),
1551
- ...(experimentUuid && { ['latitude.experimentUuid']: experimentUuid }),
1615
+ [ATTR_LATITUDE_COMMIT_UUID]: versionUuid || HEAD_COMMIT,
1616
+ [ATTR_LATITUDE_DOCUMENT_UUID]: promptUuid,
1617
+ [ATTR_LATITUDE_PROJECT_ID]: projectId,
1618
+ [ATTR_LATITUDE_DOCUMENT_LOG_UUID]: documentLogUuid,
1619
+ ...(experimentUuid && {
1620
+ [ATTR_LATITUDE_EXPERIMENT_UUID]: experimentUuid,
1621
+ }),
1552
1622
  ...(testDeploymentId && {
1553
1623
  [ATTR_LATITUDE_TEST_DEPLOYMENT_ID]: testDeploymentId,
1554
1624
  }),
1555
- ...(externalId && { ['latitude.externalId']: externalId }),
1556
- ...(source && { ['latitude.source']: source }),
1625
+ ...(externalId && { [ATTR_LATITUDE_EXTERNAL_ID]: externalId }),
1626
+ ...(source && { [ATTR_LATITUDE_SOURCE]: source }),
1557
1627
  ...(rest.attributes || {}),
1558
1628
  };
1559
1629
  return this.span(ctx, name || `prompt-${promptUuid}`, SpanType.Prompt, {
@@ -1563,24 +1633,58 @@ class ManualInstrumentation {
1563
1633
  step(ctx, options) {
1564
1634
  return this.span(ctx, 'step', SpanType.Step, options);
1565
1635
  }
1636
+ chat(ctx, { documentLogUuid, previousTraceId, source, name, ...rest }) {
1637
+ const attributes = {
1638
+ [ATTR_LATITUDE_DOCUMENT_LOG_UUID]: documentLogUuid,
1639
+ [ATTR_LATITUDE_PREVIOUS_TRACE_ID]: previousTraceId,
1640
+ ...(source && { [ATTR_LATITUDE_SOURCE]: source }),
1641
+ ...(rest.attributes || {}),
1642
+ };
1643
+ return this.span(ctx, name || 'chat', SpanType.Chat, { attributes });
1644
+ }
1645
+ external(ctx, { promptUuid, documentLogUuid, source, versionUuid, externalId, name, ...rest }) {
1646
+ const attributes = {
1647
+ [ATTR_LATITUDE_DOCUMENT_UUID]: promptUuid,
1648
+ [ATTR_LATITUDE_DOCUMENT_LOG_UUID]: documentLogUuid,
1649
+ [ATTR_LATITUDE_SOURCE]: source ?? LogSources.API,
1650
+ ...(versionUuid && { [ATTR_LATITUDE_COMMIT_UUID]: versionUuid }),
1651
+ ...(externalId && { [ATTR_LATITUDE_EXTERNAL_ID]: externalId }),
1652
+ ...(rest.attributes || {}),
1653
+ };
1654
+ return this.span(ctx, name || `external-${promptUuid}`, SpanType.External, {
1655
+ attributes,
1656
+ });
1657
+ }
1658
+ unresolvedExternal(ctx, { path, projectId, versionUuid, conversationUuid, name, ...rest }) {
1659
+ const attributes = {
1660
+ [ATTR_LATITUDE_PROMPT_PATH]: path,
1661
+ [ATTR_LATITUDE_PROJECT_ID]: projectId,
1662
+ ...(versionUuid && { [ATTR_LATITUDE_COMMIT_UUID]: versionUuid }),
1663
+ ...(conversationUuid && {
1664
+ [ATTR_LATITUDE_DOCUMENT_LOG_UUID]: conversationUuid,
1665
+ }),
1666
+ ...(rest.attributes || {}),
1667
+ };
1668
+ return this.span(ctx, name || `capture-${path}`, SpanType.UnresolvedExternal, { attributes });
1669
+ }
1566
1670
  }
1567
1671
 
1568
1672
  class LatitudeInstrumentation {
1569
1673
  options;
1570
- telemetry;
1674
+ manualTelemetry;
1571
1675
  constructor(tracer, options) {
1572
- this.telemetry = new ManualInstrumentation(tracer);
1676
+ this.manualTelemetry = new ManualInstrumentation(tracer);
1573
1677
  this.options = options;
1574
1678
  }
1575
1679
  isEnabled() {
1576
- return this.telemetry.isEnabled();
1680
+ return this.manualTelemetry.isEnabled();
1577
1681
  }
1578
1682
  enable() {
1579
1683
  this.options.module.instrument(this);
1580
- this.telemetry.enable();
1684
+ this.manualTelemetry.enable();
1581
1685
  }
1582
1686
  disable() {
1583
- this.telemetry.disable();
1687
+ this.manualTelemetry.disable();
1584
1688
  this.options.module.uninstrument();
1585
1689
  }
1586
1690
  countTokens(messages) {
@@ -1604,7 +1708,8 @@ class LatitudeInstrumentation {
1604
1708
  }
1605
1709
  async wrapRenderChain(fn, ...args) {
1606
1710
  const { prompt, parameters } = args[0];
1607
- const $prompt = this.telemetry.prompt(otel.context.active(), {
1711
+ const $prompt = this.manualTelemetry.prompt(otel.context.active(), {
1712
+ documentLogUuid: uuid.v4(),
1608
1713
  versionUuid: prompt.versionUuid,
1609
1714
  promptUuid: prompt.uuid,
1610
1715
  template: prompt.content,
@@ -1622,7 +1727,7 @@ class LatitudeInstrumentation {
1622
1727
  return result;
1623
1728
  }
1624
1729
  async wrapRenderStep(fn, ...args) {
1625
- const $step = this.telemetry.step(otel.context.active());
1730
+ const $step = this.manualTelemetry.step(otel.context.active());
1626
1731
  let result;
1627
1732
  try {
1628
1733
  result = await otel.context.with($step.context, async () => await fn(...args));
@@ -1640,7 +1745,7 @@ class LatitudeInstrumentation {
1640
1745
  }
1641
1746
  const { provider, config, messages } = args[0];
1642
1747
  const model = config.model || 'unknown';
1643
- const $completion = this.telemetry.completion(otel.context.active(), {
1748
+ const $completion = this.manualTelemetry.completion(otel.context.active(), {
1644
1749
  name: `${provider} / ${model}`,
1645
1750
  provider: provider,
1646
1751
  model: model,
@@ -1674,7 +1779,7 @@ class LatitudeInstrumentation {
1674
1779
  }
1675
1780
  async wrapRenderTool(fn, ...args) {
1676
1781
  const { toolRequest } = args[0];
1677
- const $tool = this.telemetry.tool(otel.context.active(), {
1782
+ const $tool = this.manualTelemetry.tool(otel.context.active(), {
1678
1783
  name: toolRequest.toolName,
1679
1784
  call: {
1680
1785
  id: toolRequest.toolCallId,
@@ -1699,10 +1804,165 @@ class LatitudeInstrumentation {
1699
1804
  }
1700
1805
  }
1701
1806
 
1807
+ var LatitudeErrorCodes;
1808
+ (function (LatitudeErrorCodes) {
1809
+ LatitudeErrorCodes["UnexpectedError"] = "UnexpectedError";
1810
+ LatitudeErrorCodes["OverloadedError"] = "OverloadedError";
1811
+ LatitudeErrorCodes["RateLimitError"] = "RateLimitError";
1812
+ LatitudeErrorCodes["UnauthorizedError"] = "UnauthorizedError";
1813
+ LatitudeErrorCodes["ForbiddenError"] = "ForbiddenError";
1814
+ LatitudeErrorCodes["BadRequestError"] = "BadRequestError";
1815
+ LatitudeErrorCodes["NotFoundError"] = "NotFoundError";
1816
+ LatitudeErrorCodes["ConflictError"] = "ConflictError";
1817
+ LatitudeErrorCodes["UnprocessableEntityError"] = "UnprocessableEntityError";
1818
+ LatitudeErrorCodes["NotImplementedError"] = "NotImplementedError";
1819
+ LatitudeErrorCodes["PaymentRequiredError"] = "PaymentRequiredError";
1820
+ LatitudeErrorCodes["AbortedError"] = "AbortedError";
1821
+ })(LatitudeErrorCodes || (LatitudeErrorCodes = {}));
1822
+ // NOTE: If you add a new error code, please add it to the pg enum in models/runErrors.ts
1823
+ var RunErrorCodes;
1824
+ (function (RunErrorCodes) {
1825
+ RunErrorCodes["AIProviderConfigError"] = "ai_provider_config_error";
1826
+ RunErrorCodes["AIRunError"] = "ai_run_error";
1827
+ RunErrorCodes["ChainCompileError"] = "chain_compile_error";
1828
+ RunErrorCodes["DefaultProviderExceededQuota"] = "default_provider_exceeded_quota_error";
1829
+ RunErrorCodes["DefaultProviderInvalidModel"] = "default_provider_invalid_model_error";
1830
+ RunErrorCodes["DocumentConfigError"] = "document_config_error";
1831
+ RunErrorCodes["ErrorGeneratingMockToolResult"] = "error_generating_mock_tool_result";
1832
+ RunErrorCodes["FailedToWakeUpIntegrationError"] = "failed_to_wake_up_integration_error";
1833
+ RunErrorCodes["InvalidResponseFormatError"] = "invalid_response_format_error";
1834
+ RunErrorCodes["MaxStepCountExceededError"] = "max_step_count_exceeded_error";
1835
+ RunErrorCodes["MissingProvider"] = "missing_provider_error";
1836
+ RunErrorCodes["RateLimit"] = "rate_limit_error";
1837
+ RunErrorCodes["Unknown"] = "unknown_error";
1838
+ RunErrorCodes["UnsupportedProviderResponseTypeError"] = "unsupported_provider_response_type_error";
1839
+ RunErrorCodes["PaymentRequiredError"] = "payment_required_error";
1840
+ RunErrorCodes["AbortError"] = "abort_error";
1841
+ // DEPRECATED, but do not delete
1842
+ RunErrorCodes["EvaluationRunMissingProviderLogError"] = "ev_run_missing_provider_log_error";
1843
+ RunErrorCodes["EvaluationRunMissingWorkspaceError"] = "ev_run_missing_workspace_error";
1844
+ RunErrorCodes["EvaluationRunResponseJsonFormatError"] = "ev_run_response_json_format_error";
1845
+ RunErrorCodes["EvaluationRunUnsupportedResultTypeError"] = "ev_run_unsupported_result_type_error";
1846
+ })(RunErrorCodes || (RunErrorCodes = {}));
1847
+ var ApiErrorCodes;
1848
+ (function (ApiErrorCodes) {
1849
+ ApiErrorCodes["HTTPException"] = "http_exception";
1850
+ ApiErrorCodes["InternalServerError"] = "internal_server_error";
1851
+ })(ApiErrorCodes || (ApiErrorCodes = {}));
1852
+
1853
+ class LatitudeError extends Error {
1854
+ statusCode = 500;
1855
+ name = LatitudeErrorCodes.UnexpectedError;
1856
+ headers = {};
1857
+ details;
1858
+ constructor(message, details, status, name) {
1859
+ super(message);
1860
+ this.details = details ?? {};
1861
+ this.statusCode = status ?? this.statusCode;
1862
+ this.name = name ?? this.constructor.name;
1863
+ }
1864
+ serialize() {
1865
+ return {
1866
+ name: this.name,
1867
+ code: this.name,
1868
+ status: this.statusCode,
1869
+ message: this.message,
1870
+ details: this.details,
1871
+ };
1872
+ }
1873
+ static deserialize(json) {
1874
+ return new LatitudeError(json.message, json.details, json.status, json.name);
1875
+ }
1876
+ }
1877
+ class BadRequestError extends LatitudeError {
1878
+ statusCode = 400;
1879
+ name = LatitudeErrorCodes.BadRequestError;
1880
+ }
1881
+
1702
1882
  const TRACES_URL = `${env.GATEWAY_BASE_URL}/api/v3/traces`;
1703
1883
  const SERVICE_NAME = process.env.npm_package_name || 'unknown';
1704
1884
  const SCOPE_VERSION = process.env.npm_package_version || 'unknown';
1705
1885
  const BACKGROUND = () => otel__namespace.ROOT_CONTEXT;
1886
+ class SpanFactory {
1887
+ telemetry;
1888
+ constructor(telemetry) {
1889
+ this.telemetry = telemetry;
1890
+ }
1891
+ tool(options, ctx) {
1892
+ return this.telemetry.tool(ctx ?? otel.context.active(), options);
1893
+ }
1894
+ completion(options, ctx) {
1895
+ return this.telemetry.completion(ctx ?? otel.context.active(), options);
1896
+ }
1897
+ embedding(options, ctx) {
1898
+ return this.telemetry.embedding(ctx ?? otel.context.active(), options);
1899
+ }
1900
+ retrieval(options, ctx) {
1901
+ return this.telemetry.retrieval(ctx ?? otel.context.active(), options);
1902
+ }
1903
+ reranking(options, ctx) {
1904
+ return this.telemetry.reranking(ctx ?? otel.context.active(), options);
1905
+ }
1906
+ http(options, ctx) {
1907
+ return this.telemetry.http(ctx ?? otel.context.active(), options);
1908
+ }
1909
+ prompt(options, ctx) {
1910
+ return this.telemetry.prompt(ctx ?? otel.context.active(), options);
1911
+ }
1912
+ step(options, ctx) {
1913
+ return this.telemetry.step(ctx ?? otel.context.active(), options);
1914
+ }
1915
+ chat(options, ctx) {
1916
+ return this.telemetry.chat(ctx ?? otel.context.active(), options);
1917
+ }
1918
+ external(options, ctx) {
1919
+ return this.telemetry.external(ctx ?? otel.context.active(), options);
1920
+ }
1921
+ }
1922
+ class ContextManager {
1923
+ telemetry;
1924
+ constructor(telemetry) {
1925
+ this.telemetry = telemetry;
1926
+ }
1927
+ resume(ctx) {
1928
+ return this.telemetry.resume(ctx);
1929
+ }
1930
+ active() {
1931
+ return otel.context.active();
1932
+ }
1933
+ }
1934
+ class InstrumentationManager {
1935
+ instrumentations;
1936
+ constructor(instrumentations) {
1937
+ this.instrumentations = instrumentations;
1938
+ }
1939
+ enable() {
1940
+ this.instrumentations.forEach((instrumentation) => {
1941
+ if (!instrumentation.isEnabled())
1942
+ instrumentation.enable();
1943
+ });
1944
+ }
1945
+ disable() {
1946
+ this.instrumentations.forEach((instrumentation) => {
1947
+ if (instrumentation.isEnabled())
1948
+ instrumentation.disable();
1949
+ });
1950
+ }
1951
+ }
1952
+ class TracerManager {
1953
+ nodeProvider;
1954
+ scopeVersion;
1955
+ constructor(nodeProvider, scopeVersion) {
1956
+ this.nodeProvider = nodeProvider;
1957
+ this.scopeVersion = scopeVersion;
1958
+ }
1959
+ get(scope) {
1960
+ return this.provider(scope).getTracer('');
1961
+ }
1962
+ provider(scope) {
1963
+ return new ScopedTracerProvider(`${SCOPE_LATITUDE}.${scope}`, this.scopeVersion, this.nodeProvider);
1964
+ }
1965
+ }
1706
1966
  class ScopedTracerProvider {
1707
1967
  scope;
1708
1968
  version;
@@ -1740,9 +2000,13 @@ exports.Instrumentation = void 0;
1740
2000
  })(exports.Instrumentation || (exports.Instrumentation = {}));
1741
2001
  class LatitudeTelemetry {
1742
2002
  options;
1743
- provider;
1744
- telemetry;
1745
- instrumentations;
2003
+ nodeProvider;
2004
+ manualInstrumentation;
2005
+ instrumentationsList;
2006
+ span;
2007
+ context;
2008
+ instrumentation;
2009
+ tracer;
1746
2010
  constructor(apiKey, options) {
1747
2011
  this.options = options || {};
1748
2012
  if (!this.options.exporter) {
@@ -1756,63 +2020,61 @@ class LatitudeTelemetry {
1756
2020
  new core.W3CBaggagePropagator(),
1757
2021
  ],
1758
2022
  }));
1759
- this.provider = new sdkTraceNode.NodeTracerProvider({
2023
+ this.nodeProvider = new sdkTraceNode.NodeTracerProvider({
1760
2024
  resource: new resources.Resource({ [semanticConventions.ATTR_SERVICE_NAME]: SERVICE_NAME }),
1761
2025
  });
1762
2026
  // Note: important, must run before the exporter span processors
1763
- this.provider.addSpanProcessor(new baggageSpanProcessor.BaggageSpanProcessor(baggageSpanProcessor.ALLOW_ALL_BAGGAGE_KEYS));
2027
+ this.nodeProvider.addSpanProcessor(new baggageSpanProcessor.BaggageSpanProcessor(baggageSpanProcessor.ALLOW_ALL_BAGGAGE_KEYS));
1764
2028
  if (this.options.processors) {
1765
2029
  this.options.processors.forEach((processor) => {
1766
- this.provider.addSpanProcessor(processor);
2030
+ this.nodeProvider.addSpanProcessor(processor);
1767
2031
  });
1768
2032
  }
1769
2033
  else {
1770
- this.provider.addSpanProcessor(DEFAULT_REDACT_SPAN_PROCESSOR());
2034
+ this.nodeProvider.addSpanProcessor(DEFAULT_REDACT_SPAN_PROCESSOR());
1771
2035
  }
1772
2036
  if (this.options.disableBatch) {
1773
- this.provider.addSpanProcessor(new sdkTraceNode.SimpleSpanProcessor(this.options.exporter));
2037
+ this.nodeProvider.addSpanProcessor(new sdkTraceNode.SimpleSpanProcessor(this.options.exporter));
1774
2038
  }
1775
2039
  else {
1776
- this.provider.addSpanProcessor(new sdkTraceNode.BatchSpanProcessor(this.options.exporter));
2040
+ this.nodeProvider.addSpanProcessor(new sdkTraceNode.BatchSpanProcessor(this.options.exporter));
1777
2041
  }
1778
- this.provider.register();
2042
+ this.nodeProvider.register();
1779
2043
  process.on('SIGTERM', async () => this.shutdown);
1780
2044
  process.on('SIGINT', async () => this.shutdown);
1781
- this.telemetry = null;
1782
- this.instrumentations = [];
2045
+ this.manualInstrumentation = null;
2046
+ this.instrumentationsList = [];
2047
+ this.tracer = new TracerManager(this.nodeProvider, SCOPE_VERSION);
1783
2048
  this.initInstrumentations();
1784
- this.instrument();
2049
+ this.instrumentation = new InstrumentationManager(this.instrumentationsList);
2050
+ this.instrumentation.enable();
2051
+ this.span = new SpanFactory(this.manualInstrumentation);
2052
+ this.context = new ContextManager(this.manualInstrumentation);
1785
2053
  }
1786
2054
  async flush() {
1787
- await this.provider.forceFlush();
2055
+ await this.nodeProvider.forceFlush();
1788
2056
  await this.options.exporter.forceFlush?.();
1789
2057
  }
1790
2058
  async shutdown() {
1791
2059
  await this.flush();
1792
- await this.provider.shutdown();
2060
+ await this.nodeProvider.shutdown();
1793
2061
  await this.options.exporter.shutdown?.();
1794
2062
  }
1795
- tracerProvider(instrumentation) {
1796
- return new ScopedTracerProvider(`${SCOPE_LATITUDE}.${instrumentation}`, SCOPE_VERSION, this.provider);
1797
- }
1798
- tracer(instrumentation) {
1799
- return this.tracerProvider(instrumentation).getTracer('');
1800
- }
1801
2063
  // TODO(tracing): auto instrument outgoing HTTP requests
1802
2064
  initInstrumentations() {
1803
- this.instrumentations = [];
1804
- const tracer = this.tracer(InstrumentationScope.Manual);
1805
- this.telemetry = new ManualInstrumentation(tracer);
1806
- this.instrumentations.push(this.telemetry);
2065
+ this.instrumentationsList = [];
2066
+ const tracer = this.tracer.get(InstrumentationScope.Manual);
2067
+ this.manualInstrumentation = new ManualInstrumentation(tracer);
2068
+ this.instrumentationsList.push(this.manualInstrumentation);
1807
2069
  const latitude = this.options.instrumentations?.latitude;
1808
2070
  if (latitude) {
1809
- const tracer = this.tracer(exports.Instrumentation.Latitude);
2071
+ const tracer = this.tracer.get(exports.Instrumentation.Latitude);
1810
2072
  const instrumentation = new LatitudeInstrumentation(tracer, typeof latitude === 'object' ? latitude : { module: latitude });
1811
- this.instrumentations.push(instrumentation);
2073
+ this.instrumentationsList.push(instrumentation);
1812
2074
  }
1813
2075
  const configureInstrumentation = (instrumentationType, InstrumentationConstructor, instrumentationOptions) => {
1814
2076
  const providerPkg = this.options.instrumentations?.[instrumentationType];
1815
- const provider = this.tracerProvider(instrumentationType);
2077
+ const provider = this.tracer.provider(instrumentationType);
1816
2078
  const instrumentation$1 = new InstrumentationConstructor(instrumentationOptions); // prettier-ignore
1817
2079
  instrumentation$1.setTracerProvider(provider);
1818
2080
  if (providerPkg) {
@@ -1822,7 +2084,7 @@ class LatitudeTelemetry {
1822
2084
  instrumentations: [instrumentation$1],
1823
2085
  tracerProvider: provider,
1824
2086
  });
1825
- this.instrumentations.push(instrumentation$1);
2087
+ this.instrumentationsList.push(instrumentation$1);
1826
2088
  };
1827
2089
  configureInstrumentation(exports.Instrumentation.Anthropic, instrumentationAnthropic.AnthropicInstrumentation); // prettier-ignore
1828
2090
  configureInstrumentation(exports.Instrumentation.AIPlatform, instrumentationVertexai.AIPlatformInstrumentation); // prettier-ignore
@@ -1834,44 +2096,23 @@ class LatitudeTelemetry {
1834
2096
  configureInstrumentation(exports.Instrumentation.TogetherAI, instrumentationTogether.TogetherInstrumentation, { enrichTokens: true }); // prettier-ignore
1835
2097
  configureInstrumentation(exports.Instrumentation.VertexAI, instrumentationVertexai.VertexAIInstrumentation); // prettier-ignore
1836
2098
  }
1837
- instrument() {
1838
- this.instrumentations.forEach((instrumentation) => {
1839
- if (!instrumentation.isEnabled())
1840
- instrumentation.enable();
1841
- });
1842
- }
1843
- uninstrument() {
1844
- this.instrumentations.forEach((instrumentation) => {
1845
- if (instrumentation.isEnabled())
1846
- instrumentation.disable();
1847
- });
1848
- }
1849
- resume(ctx) {
1850
- return this.telemetry.resume(ctx);
1851
- }
1852
- tool(ctx, options) {
1853
- return this.telemetry.tool(ctx, options);
1854
- }
1855
- completion(ctx, options) {
1856
- return this.telemetry.completion(ctx, options);
1857
- }
1858
- embedding(ctx, options) {
1859
- return this.telemetry.embedding(ctx, options);
1860
- }
1861
- retrieval(ctx, options) {
1862
- return this.telemetry.retrieval(ctx, options);
1863
- }
1864
- reranking(ctx, options) {
1865
- return this.telemetry.reranking(ctx, options);
1866
- }
1867
- http(ctx, options) {
1868
- return this.telemetry.http(ctx, options);
1869
- }
1870
- prompt(ctx, options) {
1871
- return this.telemetry.prompt(ctx, options);
1872
- }
1873
- step(ctx, options) {
1874
- return this.telemetry.step(ctx, options);
2099
+ async capture(options, fn) {
2100
+ if (!DOCUMENT_PATH_REGEXP.test(options.path)) {
2101
+ throw new BadRequestError("Invalid path, no spaces. Only letters, numbers, '.', '-' and '_'");
2102
+ }
2103
+ const span = this.manualInstrumentation.unresolvedExternal(otel__namespace.ROOT_CONTEXT, options);
2104
+ try {
2105
+ const result = await otel.context.with(span.context, () => fn(span.context));
2106
+ span.end();
2107
+ return result;
2108
+ }
2109
+ catch (error) {
2110
+ span.fail(error);
2111
+ throw error;
2112
+ }
2113
+ finally {
2114
+ await this.flush();
2115
+ }
1875
2116
  }
1876
2117
  }
1877
2118