@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.js CHANGED
@@ -1,8 +1,9 @@
1
1
  import { z } from 'zod';
2
2
  import * as otel from '@opentelemetry/api';
3
- import { propagation, trace, context } from '@opentelemetry/api';
3
+ import { trace, propagation, context } from '@opentelemetry/api';
4
4
  import { ATTR_HTTP_REQUEST_METHOD, ATTR_HTTP_RESPONSE_STATUS_CODE, ATTR_SERVICE_NAME } from '@opentelemetry/semantic-conventions';
5
5
  import { ATTR_GEN_AI_OPERATION_NAME, ATTR_GEN_AI_TOOL_CALL_ID, ATTR_GEN_AI_TOOL_TYPE, ATTR_GEN_AI_TOOL_NAME, ATTR_GEN_AI_SYSTEM, ATTR_GEN_AI_RESPONSE_FINISH_REASONS, ATTR_GEN_AI_RESPONSE_MODEL, ATTR_GEN_AI_USAGE_OUTPUT_TOKENS, ATTR_GEN_AI_USAGE_INPUT_TOKENS } from '@opentelemetry/semantic-conventions/incubating';
6
+ import { v4 } from 'uuid';
6
7
  import { BaggageSpanProcessor, ALLOW_ALL_BAGGAGE_KEYS } from '@opentelemetry/baggage-span-processor';
7
8
  import { AsyncLocalStorageContextManager } from '@opentelemetry/context-async-hooks';
8
9
  import { CompositePropagator, W3CTraceContextPropagator, W3CBaggagePropagator } from '@opentelemetry/core';
@@ -74,7 +75,7 @@ const DEFAULT_REDACT_SPAN_PROCESSOR = () => new RedactSpanProcessor({
74
75
  attributes: [
75
76
  /^.*auth.*$/i,
76
77
  /^.*authorization.*$/i,
77
- /^(?!ai\.).*usage.*$/i,
78
+ /^(?!gen_ai\.).*usage.*$/i,
78
79
  /^(?!gen_ai\.).*token.*$/i,
79
80
  /^.*secret.*$/i,
80
81
  /^.*key.*$/i,
@@ -188,6 +189,7 @@ const compositeEvaluationConfiguration = baseEvaluationConfiguration.extend({
188
189
  evaluationUuids: z.array(z.string()),
189
190
  minThreshold: z.number().optional(), // Threshold percentage
190
191
  maxThreshold: z.number().optional(), // Threshold percentage
192
+ defaultTarget: z.boolean().optional(), // Default for optimizations and distillations
191
193
  });
192
194
  const compositeEvaluationResultMetadata = baseEvaluationResultMetadata.extend({
193
195
  results: z.record(z.string(), // Evaluation uuid
@@ -255,7 +257,7 @@ const CompositeEvaluationSpecification = {
255
257
  };
256
258
 
257
259
  const humanEvaluationConfiguration = baseEvaluationConfiguration.extend({
258
- enableControls: z.boolean().optional(),
260
+ enableControls: z.boolean().optional(), // UI annotation controls
259
261
  criteria: z.string().optional(),
260
262
  });
261
263
  const humanEvaluationResultMetadata = baseEvaluationResultMetadata.extend({
@@ -783,6 +785,9 @@ var SpanType;
783
785
  SpanType["Http"] = "http";
784
786
  SpanType["Unknown"] = "unknown";
785
787
  SpanType["Prompt"] = "prompt";
788
+ SpanType["Chat"] = "chat";
789
+ SpanType["External"] = "external";
790
+ SpanType["UnresolvedExternal"] = "unresolved_external";
786
791
  SpanType["Step"] = "step";
787
792
  })(SpanType || (SpanType = {}));
788
793
  const SPAN_SPECIFICATIONS = {
@@ -834,6 +839,24 @@ const SPAN_SPECIFICATIONS = {
834
839
  isGenAI: false,
835
840
  isHidden: false,
836
841
  },
842
+ [SpanType.Chat]: {
843
+ name: 'Chat',
844
+ description: 'A chat continuation span',
845
+ isGenAI: false,
846
+ isHidden: false,
847
+ },
848
+ [SpanType.External]: {
849
+ name: 'External',
850
+ description: 'An external capture span',
851
+ isGenAI: false,
852
+ isHidden: false,
853
+ },
854
+ [SpanType.UnresolvedExternal]: {
855
+ name: 'Unresolved External',
856
+ description: 'An external span that needs path resolution before storage',
857
+ isGenAI: false,
858
+ isHidden: true,
859
+ },
837
860
  [SpanType.Step]: {
838
861
  name: 'Step',
839
862
  description: 'A step span',
@@ -887,7 +910,16 @@ var InstrumentationScope;
887
910
  /* Note: non-standard OpenTelemetry semantic conventions used in Latitude */
888
911
  const ATTR_LATITUDE = 'latitude';
889
912
  const ATTR_LATITUDE_TYPE = `${ATTR_LATITUDE}.type`;
913
+ const ATTR_LATITUDE_DOCUMENT_UUID = `${ATTR_LATITUDE}.document_uuid`;
914
+ const ATTR_LATITUDE_PROMPT_PATH = `${ATTR_LATITUDE}.prompt_path`;
915
+ const ATTR_LATITUDE_COMMIT_UUID = `${ATTR_LATITUDE}.commit_uuid`;
916
+ const ATTR_LATITUDE_DOCUMENT_LOG_UUID = `${ATTR_LATITUDE}.document_log_uuid`;
917
+ const ATTR_LATITUDE_PROJECT_ID = `${ATTR_LATITUDE}.project_id`;
918
+ const ATTR_LATITUDE_EXPERIMENT_UUID = `${ATTR_LATITUDE}.experiment_uuid`;
919
+ const ATTR_LATITUDE_SOURCE = `${ATTR_LATITUDE}.source`;
920
+ const ATTR_LATITUDE_EXTERNAL_ID = `${ATTR_LATITUDE}.external_id`;
890
921
  const ATTR_LATITUDE_TEST_DEPLOYMENT_ID = `${ATTR_LATITUDE}.test_deployment_id`;
922
+ const ATTR_LATITUDE_PREVIOUS_TRACE_ID = `${ATTR_LATITUDE}.previous_trace_id`;
891
923
  const GEN_AI_TOOL_TYPE_VALUE_FUNCTION = 'function';
892
924
  const ATTR_GEN_AI_TOOL_CALL_ARGUMENTS = 'gen_ai.tool.call.arguments';
893
925
  const ATTR_GEN_AI_TOOL_RESULT_VALUE = 'gen_ai.tool.result.value';
@@ -1044,6 +1076,7 @@ var DocumentTriggerParameters;
1044
1076
  DocumentTriggerParameters["Body"] = "body";
1045
1077
  DocumentTriggerParameters["Attachments"] = "attachments";
1046
1078
  })(DocumentTriggerParameters || (DocumentTriggerParameters = {}));
1079
+ const DOCUMENT_PATH_REGEXP = /^([\w-]+\/)*([\w-.])+$/;
1047
1080
 
1048
1081
  class ManualInstrumentation {
1049
1082
  enabled;
@@ -1062,7 +1095,33 @@ class ManualInstrumentation {
1062
1095
  this.enabled = false;
1063
1096
  }
1064
1097
  resume(ctx) {
1065
- return propagation.extract(otel.ROOT_CONTEXT, ctx);
1098
+ const parts = ctx.traceparent.split('-');
1099
+ if (parts.length !== 4) {
1100
+ return otel.ROOT_CONTEXT;
1101
+ }
1102
+ const [, traceId, spanId, flags] = parts;
1103
+ if (!traceId || !spanId) {
1104
+ return otel.ROOT_CONTEXT;
1105
+ }
1106
+ const spanContext = {
1107
+ traceId,
1108
+ spanId,
1109
+ traceFlags: parseInt(flags ?? '01', 16),
1110
+ isRemote: true,
1111
+ };
1112
+ let context = trace.setSpanContext(otel.ROOT_CONTEXT, spanContext);
1113
+ if (ctx.baggage) {
1114
+ const baggageEntries = {};
1115
+ for (const pair of ctx.baggage.split(',')) {
1116
+ const [key, value] = pair.split('=');
1117
+ if (key && value) {
1118
+ baggageEntries[key] = { value: decodeURIComponent(value) };
1119
+ }
1120
+ }
1121
+ const baggage = propagation.createBaggage(baggageEntries);
1122
+ context = propagation.setBaggage(context, baggage);
1123
+ }
1124
+ return context;
1066
1125
  }
1067
1126
  capitalize(str) {
1068
1127
  if (str.length === 0)
@@ -1369,7 +1428,7 @@ class ManualInstrumentation {
1369
1428
  completion(ctx, options) {
1370
1429
  const start = options;
1371
1430
  const configuration = {
1372
- ...start.configuration,
1431
+ ...(start.configuration ?? {}),
1373
1432
  model: start.model,
1374
1433
  };
1375
1434
  let jsonConfiguration = '';
@@ -1380,14 +1439,15 @@ class ManualInstrumentation {
1380
1439
  jsonConfiguration = '{}';
1381
1440
  }
1382
1441
  const attrConfiguration = this.attribifyConfiguration('input', configuration);
1442
+ const input = start.input ?? [];
1383
1443
  let jsonInput = '';
1384
1444
  try {
1385
- jsonInput = JSON.stringify(start.input);
1445
+ jsonInput = JSON.stringify(input);
1386
1446
  }
1387
1447
  catch (error) {
1388
1448
  jsonInput = '[]';
1389
1449
  }
1390
- const attrInput = this.attribifyMessages('input', start.input);
1450
+ const attrInput = this.attribifyMessages('input', input);
1391
1451
  const span = this.span(ctx, start.name || `${start.provider} / ${start.model}`, SpanType.Completion, {
1392
1452
  attributes: {
1393
1453
  [ATTR_GEN_AI_SYSTEM]: start.provider,
@@ -1396,37 +1456,45 @@ class ManualInstrumentation {
1396
1456
  [ATTR_GEN_AI_REQUEST_MESSAGES]: jsonInput,
1397
1457
  ...attrInput,
1398
1458
  ...(start.attributes || {}),
1399
- ['latitude.commitUuid']: start.versionUuid,
1400
- ['latitude.documentUuid']: start.promptUuid,
1401
- ['latitude.experimentUuid']: start.experimentUuid,
1459
+ [ATTR_LATITUDE_COMMIT_UUID]: start.versionUuid,
1460
+ [ATTR_LATITUDE_DOCUMENT_UUID]: start.promptUuid,
1461
+ [ATTR_LATITUDE_EXPERIMENT_UUID]: start.experimentUuid,
1402
1462
  },
1403
1463
  });
1404
1464
  return {
1405
1465
  context: span.context,
1406
1466
  end: (options) => {
1407
- const end = options;
1467
+ const end = options ?? {};
1468
+ const output = end.output ?? [];
1408
1469
  let jsonOutput = '';
1409
1470
  try {
1410
- jsonOutput = JSON.stringify(end.output);
1471
+ jsonOutput = JSON.stringify(output);
1411
1472
  }
1412
1473
  catch (error) {
1413
1474
  jsonOutput = '[]';
1414
1475
  }
1415
- const attrOutput = this.attribifyMessages('output', end.output);
1416
- const inputTokens = end.tokens.prompt + end.tokens.cached;
1417
- const outputTokens = end.tokens.reasoning + end.tokens.completion;
1476
+ const attrOutput = this.attribifyMessages('output', output);
1477
+ const tokens = {
1478
+ prompt: end.tokens?.prompt ?? 0,
1479
+ cached: end.tokens?.cached ?? 0,
1480
+ reasoning: end.tokens?.reasoning ?? 0,
1481
+ completion: end.tokens?.completion ?? 0,
1482
+ };
1483
+ const inputTokens = tokens.prompt + tokens.cached;
1484
+ const outputTokens = tokens.reasoning + tokens.completion;
1485
+ const finishReason = end.finishReason ?? '';
1418
1486
  span.end({
1419
1487
  attributes: {
1420
1488
  [ATTR_GEN_AI_RESPONSE_MESSAGES]: jsonOutput,
1421
1489
  ...attrOutput,
1422
1490
  [ATTR_GEN_AI_USAGE_INPUT_TOKENS]: inputTokens,
1423
- [ATTR_GEN_AI_USAGE_PROMPT_TOKENS]: end.tokens.prompt,
1424
- [ATTR_GEN_AI_USAGE_CACHED_TOKENS]: end.tokens.cached,
1425
- [ATTR_GEN_AI_USAGE_REASONING_TOKENS]: end.tokens.reasoning,
1426
- [ATTR_GEN_AI_USAGE_COMPLETION_TOKENS]: end.tokens.completion,
1491
+ [ATTR_GEN_AI_USAGE_PROMPT_TOKENS]: tokens.prompt,
1492
+ [ATTR_GEN_AI_USAGE_CACHED_TOKENS]: tokens.cached,
1493
+ [ATTR_GEN_AI_USAGE_REASONING_TOKENS]: tokens.reasoning,
1494
+ [ATTR_GEN_AI_USAGE_COMPLETION_TOKENS]: tokens.completion,
1427
1495
  [ATTR_GEN_AI_USAGE_OUTPUT_TOKENS]: outputTokens,
1428
1496
  [ATTR_GEN_AI_RESPONSE_MODEL]: start.model,
1429
- [ATTR_GEN_AI_RESPONSE_FINISH_REASONS]: [end.finishReason],
1497
+ [ATTR_GEN_AI_RESPONSE_FINISH_REASONS]: [finishReason],
1430
1498
  ...(end.attributes || {}),
1431
1499
  },
1432
1500
  });
@@ -1524,16 +1592,18 @@ class ManualInstrumentation {
1524
1592
  const attributes = {
1525
1593
  [ATTR_GEN_AI_REQUEST_TEMPLATE]: template,
1526
1594
  [ATTR_GEN_AI_REQUEST_PARAMETERS]: jsonParameters,
1527
- ['latitude.commitUuid']: versionUuid || HEAD_COMMIT,
1528
- ['latitude.documentUuid']: promptUuid,
1529
- ['latitude.projectId']: projectId,
1530
- ...(documentLogUuid && { ['latitude.documentLogUuid']: documentLogUuid }),
1531
- ...(experimentUuid && { ['latitude.experimentUuid']: experimentUuid }),
1595
+ [ATTR_LATITUDE_COMMIT_UUID]: versionUuid || HEAD_COMMIT,
1596
+ [ATTR_LATITUDE_DOCUMENT_UUID]: promptUuid,
1597
+ [ATTR_LATITUDE_PROJECT_ID]: projectId,
1598
+ [ATTR_LATITUDE_DOCUMENT_LOG_UUID]: documentLogUuid,
1599
+ ...(experimentUuid && {
1600
+ [ATTR_LATITUDE_EXPERIMENT_UUID]: experimentUuid,
1601
+ }),
1532
1602
  ...(testDeploymentId && {
1533
1603
  [ATTR_LATITUDE_TEST_DEPLOYMENT_ID]: testDeploymentId,
1534
1604
  }),
1535
- ...(externalId && { ['latitude.externalId']: externalId }),
1536
- ...(source && { ['latitude.source']: source }),
1605
+ ...(externalId && { [ATTR_LATITUDE_EXTERNAL_ID]: externalId }),
1606
+ ...(source && { [ATTR_LATITUDE_SOURCE]: source }),
1537
1607
  ...(rest.attributes || {}),
1538
1608
  };
1539
1609
  return this.span(ctx, name || `prompt-${promptUuid}`, SpanType.Prompt, {
@@ -1543,24 +1613,58 @@ class ManualInstrumentation {
1543
1613
  step(ctx, options) {
1544
1614
  return this.span(ctx, 'step', SpanType.Step, options);
1545
1615
  }
1616
+ chat(ctx, { documentLogUuid, previousTraceId, source, name, ...rest }) {
1617
+ const attributes = {
1618
+ [ATTR_LATITUDE_DOCUMENT_LOG_UUID]: documentLogUuid,
1619
+ [ATTR_LATITUDE_PREVIOUS_TRACE_ID]: previousTraceId,
1620
+ ...(source && { [ATTR_LATITUDE_SOURCE]: source }),
1621
+ ...(rest.attributes || {}),
1622
+ };
1623
+ return this.span(ctx, name || 'chat', SpanType.Chat, { attributes });
1624
+ }
1625
+ external(ctx, { promptUuid, documentLogUuid, source, versionUuid, externalId, name, ...rest }) {
1626
+ const attributes = {
1627
+ [ATTR_LATITUDE_DOCUMENT_UUID]: promptUuid,
1628
+ [ATTR_LATITUDE_DOCUMENT_LOG_UUID]: documentLogUuid,
1629
+ [ATTR_LATITUDE_SOURCE]: source ?? LogSources.API,
1630
+ ...(versionUuid && { [ATTR_LATITUDE_COMMIT_UUID]: versionUuid }),
1631
+ ...(externalId && { [ATTR_LATITUDE_EXTERNAL_ID]: externalId }),
1632
+ ...(rest.attributes || {}),
1633
+ };
1634
+ return this.span(ctx, name || `external-${promptUuid}`, SpanType.External, {
1635
+ attributes,
1636
+ });
1637
+ }
1638
+ unresolvedExternal(ctx, { path, projectId, versionUuid, conversationUuid, name, ...rest }) {
1639
+ const attributes = {
1640
+ [ATTR_LATITUDE_PROMPT_PATH]: path,
1641
+ [ATTR_LATITUDE_PROJECT_ID]: projectId,
1642
+ ...(versionUuid && { [ATTR_LATITUDE_COMMIT_UUID]: versionUuid }),
1643
+ ...(conversationUuid && {
1644
+ [ATTR_LATITUDE_DOCUMENT_LOG_UUID]: conversationUuid,
1645
+ }),
1646
+ ...(rest.attributes || {}),
1647
+ };
1648
+ return this.span(ctx, name || `capture-${path}`, SpanType.UnresolvedExternal, { attributes });
1649
+ }
1546
1650
  }
1547
1651
 
1548
1652
  class LatitudeInstrumentation {
1549
1653
  options;
1550
- telemetry;
1654
+ manualTelemetry;
1551
1655
  constructor(tracer, options) {
1552
- this.telemetry = new ManualInstrumentation(tracer);
1656
+ this.manualTelemetry = new ManualInstrumentation(tracer);
1553
1657
  this.options = options;
1554
1658
  }
1555
1659
  isEnabled() {
1556
- return this.telemetry.isEnabled();
1660
+ return this.manualTelemetry.isEnabled();
1557
1661
  }
1558
1662
  enable() {
1559
1663
  this.options.module.instrument(this);
1560
- this.telemetry.enable();
1664
+ this.manualTelemetry.enable();
1561
1665
  }
1562
1666
  disable() {
1563
- this.telemetry.disable();
1667
+ this.manualTelemetry.disable();
1564
1668
  this.options.module.uninstrument();
1565
1669
  }
1566
1670
  countTokens(messages) {
@@ -1584,7 +1688,8 @@ class LatitudeInstrumentation {
1584
1688
  }
1585
1689
  async wrapRenderChain(fn, ...args) {
1586
1690
  const { prompt, parameters } = args[0];
1587
- const $prompt = this.telemetry.prompt(context.active(), {
1691
+ const $prompt = this.manualTelemetry.prompt(context.active(), {
1692
+ documentLogUuid: v4(),
1588
1693
  versionUuid: prompt.versionUuid,
1589
1694
  promptUuid: prompt.uuid,
1590
1695
  template: prompt.content,
@@ -1602,7 +1707,7 @@ class LatitudeInstrumentation {
1602
1707
  return result;
1603
1708
  }
1604
1709
  async wrapRenderStep(fn, ...args) {
1605
- const $step = this.telemetry.step(context.active());
1710
+ const $step = this.manualTelemetry.step(context.active());
1606
1711
  let result;
1607
1712
  try {
1608
1713
  result = await context.with($step.context, async () => await fn(...args));
@@ -1620,7 +1725,7 @@ class LatitudeInstrumentation {
1620
1725
  }
1621
1726
  const { provider, config, messages } = args[0];
1622
1727
  const model = config.model || 'unknown';
1623
- const $completion = this.telemetry.completion(context.active(), {
1728
+ const $completion = this.manualTelemetry.completion(context.active(), {
1624
1729
  name: `${provider} / ${model}`,
1625
1730
  provider: provider,
1626
1731
  model: model,
@@ -1654,7 +1759,7 @@ class LatitudeInstrumentation {
1654
1759
  }
1655
1760
  async wrapRenderTool(fn, ...args) {
1656
1761
  const { toolRequest } = args[0];
1657
- const $tool = this.telemetry.tool(context.active(), {
1762
+ const $tool = this.manualTelemetry.tool(context.active(), {
1658
1763
  name: toolRequest.toolName,
1659
1764
  call: {
1660
1765
  id: toolRequest.toolCallId,
@@ -1679,10 +1784,165 @@ class LatitudeInstrumentation {
1679
1784
  }
1680
1785
  }
1681
1786
 
1787
+ var LatitudeErrorCodes;
1788
+ (function (LatitudeErrorCodes) {
1789
+ LatitudeErrorCodes["UnexpectedError"] = "UnexpectedError";
1790
+ LatitudeErrorCodes["OverloadedError"] = "OverloadedError";
1791
+ LatitudeErrorCodes["RateLimitError"] = "RateLimitError";
1792
+ LatitudeErrorCodes["UnauthorizedError"] = "UnauthorizedError";
1793
+ LatitudeErrorCodes["ForbiddenError"] = "ForbiddenError";
1794
+ LatitudeErrorCodes["BadRequestError"] = "BadRequestError";
1795
+ LatitudeErrorCodes["NotFoundError"] = "NotFoundError";
1796
+ LatitudeErrorCodes["ConflictError"] = "ConflictError";
1797
+ LatitudeErrorCodes["UnprocessableEntityError"] = "UnprocessableEntityError";
1798
+ LatitudeErrorCodes["NotImplementedError"] = "NotImplementedError";
1799
+ LatitudeErrorCodes["PaymentRequiredError"] = "PaymentRequiredError";
1800
+ LatitudeErrorCodes["AbortedError"] = "AbortedError";
1801
+ })(LatitudeErrorCodes || (LatitudeErrorCodes = {}));
1802
+ // NOTE: If you add a new error code, please add it to the pg enum in models/runErrors.ts
1803
+ var RunErrorCodes;
1804
+ (function (RunErrorCodes) {
1805
+ RunErrorCodes["AIProviderConfigError"] = "ai_provider_config_error";
1806
+ RunErrorCodes["AIRunError"] = "ai_run_error";
1807
+ RunErrorCodes["ChainCompileError"] = "chain_compile_error";
1808
+ RunErrorCodes["DefaultProviderExceededQuota"] = "default_provider_exceeded_quota_error";
1809
+ RunErrorCodes["DefaultProviderInvalidModel"] = "default_provider_invalid_model_error";
1810
+ RunErrorCodes["DocumentConfigError"] = "document_config_error";
1811
+ RunErrorCodes["ErrorGeneratingMockToolResult"] = "error_generating_mock_tool_result";
1812
+ RunErrorCodes["FailedToWakeUpIntegrationError"] = "failed_to_wake_up_integration_error";
1813
+ RunErrorCodes["InvalidResponseFormatError"] = "invalid_response_format_error";
1814
+ RunErrorCodes["MaxStepCountExceededError"] = "max_step_count_exceeded_error";
1815
+ RunErrorCodes["MissingProvider"] = "missing_provider_error";
1816
+ RunErrorCodes["RateLimit"] = "rate_limit_error";
1817
+ RunErrorCodes["Unknown"] = "unknown_error";
1818
+ RunErrorCodes["UnsupportedProviderResponseTypeError"] = "unsupported_provider_response_type_error";
1819
+ RunErrorCodes["PaymentRequiredError"] = "payment_required_error";
1820
+ RunErrorCodes["AbortError"] = "abort_error";
1821
+ // DEPRECATED, but do not delete
1822
+ RunErrorCodes["EvaluationRunMissingProviderLogError"] = "ev_run_missing_provider_log_error";
1823
+ RunErrorCodes["EvaluationRunMissingWorkspaceError"] = "ev_run_missing_workspace_error";
1824
+ RunErrorCodes["EvaluationRunResponseJsonFormatError"] = "ev_run_response_json_format_error";
1825
+ RunErrorCodes["EvaluationRunUnsupportedResultTypeError"] = "ev_run_unsupported_result_type_error";
1826
+ })(RunErrorCodes || (RunErrorCodes = {}));
1827
+ var ApiErrorCodes;
1828
+ (function (ApiErrorCodes) {
1829
+ ApiErrorCodes["HTTPException"] = "http_exception";
1830
+ ApiErrorCodes["InternalServerError"] = "internal_server_error";
1831
+ })(ApiErrorCodes || (ApiErrorCodes = {}));
1832
+
1833
+ class LatitudeError extends Error {
1834
+ statusCode = 500;
1835
+ name = LatitudeErrorCodes.UnexpectedError;
1836
+ headers = {};
1837
+ details;
1838
+ constructor(message, details, status, name) {
1839
+ super(message);
1840
+ this.details = details ?? {};
1841
+ this.statusCode = status ?? this.statusCode;
1842
+ this.name = name ?? this.constructor.name;
1843
+ }
1844
+ serialize() {
1845
+ return {
1846
+ name: this.name,
1847
+ code: this.name,
1848
+ status: this.statusCode,
1849
+ message: this.message,
1850
+ details: this.details,
1851
+ };
1852
+ }
1853
+ static deserialize(json) {
1854
+ return new LatitudeError(json.message, json.details, json.status, json.name);
1855
+ }
1856
+ }
1857
+ class BadRequestError extends LatitudeError {
1858
+ statusCode = 400;
1859
+ name = LatitudeErrorCodes.BadRequestError;
1860
+ }
1861
+
1682
1862
  const TRACES_URL = `${env.GATEWAY_BASE_URL}/api/v3/traces`;
1683
1863
  const SERVICE_NAME = process.env.npm_package_name || 'unknown';
1684
1864
  const SCOPE_VERSION = process.env.npm_package_version || 'unknown';
1685
1865
  const BACKGROUND = () => otel.ROOT_CONTEXT;
1866
+ class SpanFactory {
1867
+ telemetry;
1868
+ constructor(telemetry) {
1869
+ this.telemetry = telemetry;
1870
+ }
1871
+ tool(options, ctx) {
1872
+ return this.telemetry.tool(ctx ?? context.active(), options);
1873
+ }
1874
+ completion(options, ctx) {
1875
+ return this.telemetry.completion(ctx ?? context.active(), options);
1876
+ }
1877
+ embedding(options, ctx) {
1878
+ return this.telemetry.embedding(ctx ?? context.active(), options);
1879
+ }
1880
+ retrieval(options, ctx) {
1881
+ return this.telemetry.retrieval(ctx ?? context.active(), options);
1882
+ }
1883
+ reranking(options, ctx) {
1884
+ return this.telemetry.reranking(ctx ?? context.active(), options);
1885
+ }
1886
+ http(options, ctx) {
1887
+ return this.telemetry.http(ctx ?? context.active(), options);
1888
+ }
1889
+ prompt(options, ctx) {
1890
+ return this.telemetry.prompt(ctx ?? context.active(), options);
1891
+ }
1892
+ step(options, ctx) {
1893
+ return this.telemetry.step(ctx ?? context.active(), options);
1894
+ }
1895
+ chat(options, ctx) {
1896
+ return this.telemetry.chat(ctx ?? context.active(), options);
1897
+ }
1898
+ external(options, ctx) {
1899
+ return this.telemetry.external(ctx ?? context.active(), options);
1900
+ }
1901
+ }
1902
+ class ContextManager {
1903
+ telemetry;
1904
+ constructor(telemetry) {
1905
+ this.telemetry = telemetry;
1906
+ }
1907
+ resume(ctx) {
1908
+ return this.telemetry.resume(ctx);
1909
+ }
1910
+ active() {
1911
+ return context.active();
1912
+ }
1913
+ }
1914
+ class InstrumentationManager {
1915
+ instrumentations;
1916
+ constructor(instrumentations) {
1917
+ this.instrumentations = instrumentations;
1918
+ }
1919
+ enable() {
1920
+ this.instrumentations.forEach((instrumentation) => {
1921
+ if (!instrumentation.isEnabled())
1922
+ instrumentation.enable();
1923
+ });
1924
+ }
1925
+ disable() {
1926
+ this.instrumentations.forEach((instrumentation) => {
1927
+ if (instrumentation.isEnabled())
1928
+ instrumentation.disable();
1929
+ });
1930
+ }
1931
+ }
1932
+ class TracerManager {
1933
+ nodeProvider;
1934
+ scopeVersion;
1935
+ constructor(nodeProvider, scopeVersion) {
1936
+ this.nodeProvider = nodeProvider;
1937
+ this.scopeVersion = scopeVersion;
1938
+ }
1939
+ get(scope) {
1940
+ return this.provider(scope).getTracer('');
1941
+ }
1942
+ provider(scope) {
1943
+ return new ScopedTracerProvider(`${SCOPE_LATITUDE}.${scope}`, this.scopeVersion, this.nodeProvider);
1944
+ }
1945
+ }
1686
1946
  class ScopedTracerProvider {
1687
1947
  scope;
1688
1948
  version;
@@ -1720,9 +1980,13 @@ var Instrumentation;
1720
1980
  })(Instrumentation || (Instrumentation = {}));
1721
1981
  class LatitudeTelemetry {
1722
1982
  options;
1723
- provider;
1724
- telemetry;
1725
- instrumentations;
1983
+ nodeProvider;
1984
+ manualInstrumentation;
1985
+ instrumentationsList;
1986
+ span;
1987
+ context;
1988
+ instrumentation;
1989
+ tracer;
1726
1990
  constructor(apiKey, options) {
1727
1991
  this.options = options || {};
1728
1992
  if (!this.options.exporter) {
@@ -1736,63 +2000,61 @@ class LatitudeTelemetry {
1736
2000
  new W3CBaggagePropagator(),
1737
2001
  ],
1738
2002
  }));
1739
- this.provider = new NodeTracerProvider({
2003
+ this.nodeProvider = new NodeTracerProvider({
1740
2004
  resource: new Resource({ [ATTR_SERVICE_NAME]: SERVICE_NAME }),
1741
2005
  });
1742
2006
  // Note: important, must run before the exporter span processors
1743
- this.provider.addSpanProcessor(new BaggageSpanProcessor(ALLOW_ALL_BAGGAGE_KEYS));
2007
+ this.nodeProvider.addSpanProcessor(new BaggageSpanProcessor(ALLOW_ALL_BAGGAGE_KEYS));
1744
2008
  if (this.options.processors) {
1745
2009
  this.options.processors.forEach((processor) => {
1746
- this.provider.addSpanProcessor(processor);
2010
+ this.nodeProvider.addSpanProcessor(processor);
1747
2011
  });
1748
2012
  }
1749
2013
  else {
1750
- this.provider.addSpanProcessor(DEFAULT_REDACT_SPAN_PROCESSOR());
2014
+ this.nodeProvider.addSpanProcessor(DEFAULT_REDACT_SPAN_PROCESSOR());
1751
2015
  }
1752
2016
  if (this.options.disableBatch) {
1753
- this.provider.addSpanProcessor(new SimpleSpanProcessor(this.options.exporter));
2017
+ this.nodeProvider.addSpanProcessor(new SimpleSpanProcessor(this.options.exporter));
1754
2018
  }
1755
2019
  else {
1756
- this.provider.addSpanProcessor(new BatchSpanProcessor(this.options.exporter));
2020
+ this.nodeProvider.addSpanProcessor(new BatchSpanProcessor(this.options.exporter));
1757
2021
  }
1758
- this.provider.register();
2022
+ this.nodeProvider.register();
1759
2023
  process.on('SIGTERM', async () => this.shutdown);
1760
2024
  process.on('SIGINT', async () => this.shutdown);
1761
- this.telemetry = null;
1762
- this.instrumentations = [];
2025
+ this.manualInstrumentation = null;
2026
+ this.instrumentationsList = [];
2027
+ this.tracer = new TracerManager(this.nodeProvider, SCOPE_VERSION);
1763
2028
  this.initInstrumentations();
1764
- this.instrument();
2029
+ this.instrumentation = new InstrumentationManager(this.instrumentationsList);
2030
+ this.instrumentation.enable();
2031
+ this.span = new SpanFactory(this.manualInstrumentation);
2032
+ this.context = new ContextManager(this.manualInstrumentation);
1765
2033
  }
1766
2034
  async flush() {
1767
- await this.provider.forceFlush();
2035
+ await this.nodeProvider.forceFlush();
1768
2036
  await this.options.exporter.forceFlush?.();
1769
2037
  }
1770
2038
  async shutdown() {
1771
2039
  await this.flush();
1772
- await this.provider.shutdown();
2040
+ await this.nodeProvider.shutdown();
1773
2041
  await this.options.exporter.shutdown?.();
1774
2042
  }
1775
- tracerProvider(instrumentation) {
1776
- return new ScopedTracerProvider(`${SCOPE_LATITUDE}.${instrumentation}`, SCOPE_VERSION, this.provider);
1777
- }
1778
- tracer(instrumentation) {
1779
- return this.tracerProvider(instrumentation).getTracer('');
1780
- }
1781
2043
  // TODO(tracing): auto instrument outgoing HTTP requests
1782
2044
  initInstrumentations() {
1783
- this.instrumentations = [];
1784
- const tracer = this.tracer(InstrumentationScope.Manual);
1785
- this.telemetry = new ManualInstrumentation(tracer);
1786
- this.instrumentations.push(this.telemetry);
2045
+ this.instrumentationsList = [];
2046
+ const tracer = this.tracer.get(InstrumentationScope.Manual);
2047
+ this.manualInstrumentation = new ManualInstrumentation(tracer);
2048
+ this.instrumentationsList.push(this.manualInstrumentation);
1787
2049
  const latitude = this.options.instrumentations?.latitude;
1788
2050
  if (latitude) {
1789
- const tracer = this.tracer(Instrumentation.Latitude);
2051
+ const tracer = this.tracer.get(Instrumentation.Latitude);
1790
2052
  const instrumentation = new LatitudeInstrumentation(tracer, typeof latitude === 'object' ? latitude : { module: latitude });
1791
- this.instrumentations.push(instrumentation);
2053
+ this.instrumentationsList.push(instrumentation);
1792
2054
  }
1793
2055
  const configureInstrumentation = (instrumentationType, InstrumentationConstructor, instrumentationOptions) => {
1794
2056
  const providerPkg = this.options.instrumentations?.[instrumentationType];
1795
- const provider = this.tracerProvider(instrumentationType);
2057
+ const provider = this.tracer.provider(instrumentationType);
1796
2058
  const instrumentation = new InstrumentationConstructor(instrumentationOptions); // prettier-ignore
1797
2059
  instrumentation.setTracerProvider(provider);
1798
2060
  if (providerPkg) {
@@ -1802,7 +2064,7 @@ class LatitudeTelemetry {
1802
2064
  instrumentations: [instrumentation],
1803
2065
  tracerProvider: provider,
1804
2066
  });
1805
- this.instrumentations.push(instrumentation);
2067
+ this.instrumentationsList.push(instrumentation);
1806
2068
  };
1807
2069
  configureInstrumentation(Instrumentation.Anthropic, AnthropicInstrumentation); // prettier-ignore
1808
2070
  configureInstrumentation(Instrumentation.AIPlatform, AIPlatformInstrumentation); // prettier-ignore
@@ -1814,44 +2076,23 @@ class LatitudeTelemetry {
1814
2076
  configureInstrumentation(Instrumentation.TogetherAI, TogetherInstrumentation, { enrichTokens: true }); // prettier-ignore
1815
2077
  configureInstrumentation(Instrumentation.VertexAI, VertexAIInstrumentation); // prettier-ignore
1816
2078
  }
1817
- instrument() {
1818
- this.instrumentations.forEach((instrumentation) => {
1819
- if (!instrumentation.isEnabled())
1820
- instrumentation.enable();
1821
- });
1822
- }
1823
- uninstrument() {
1824
- this.instrumentations.forEach((instrumentation) => {
1825
- if (instrumentation.isEnabled())
1826
- instrumentation.disable();
1827
- });
1828
- }
1829
- resume(ctx) {
1830
- return this.telemetry.resume(ctx);
1831
- }
1832
- tool(ctx, options) {
1833
- return this.telemetry.tool(ctx, options);
1834
- }
1835
- completion(ctx, options) {
1836
- return this.telemetry.completion(ctx, options);
1837
- }
1838
- embedding(ctx, options) {
1839
- return this.telemetry.embedding(ctx, options);
1840
- }
1841
- retrieval(ctx, options) {
1842
- return this.telemetry.retrieval(ctx, options);
1843
- }
1844
- reranking(ctx, options) {
1845
- return this.telemetry.reranking(ctx, options);
1846
- }
1847
- http(ctx, options) {
1848
- return this.telemetry.http(ctx, options);
1849
- }
1850
- prompt(ctx, options) {
1851
- return this.telemetry.prompt(ctx, options);
1852
- }
1853
- step(ctx, options) {
1854
- return this.telemetry.step(ctx, options);
2079
+ async capture(options, fn) {
2080
+ if (!DOCUMENT_PATH_REGEXP.test(options.path)) {
2081
+ throw new BadRequestError("Invalid path, no spaces. Only letters, numbers, '.', '-' and '_'");
2082
+ }
2083
+ const span = this.manualInstrumentation.unresolvedExternal(otel.ROOT_CONTEXT, options);
2084
+ try {
2085
+ const result = await context.with(span.context, () => fn(span.context));
2086
+ span.end();
2087
+ return result;
2088
+ }
2089
+ catch (error) {
2090
+ span.fail(error);
2091
+ throw error;
2092
+ }
2093
+ finally {
2094
+ await this.flush();
2095
+ }
1855
2096
  }
1856
2097
  }
1857
2098