@agentmark-ai/shared-utils 0.5.1 → 0.6.1

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.mjs CHANGED
@@ -1609,8 +1609,77 @@ var GenAIAttributes = {
1609
1609
  TOOL_NAME: "gen_ai.tool.name",
1610
1610
  TOOL_CALL_ID: "gen_ai.tool.call.id",
1611
1611
  TOOL_INPUT: "gen_ai.tool.input",
1612
- TOOL_OUTPUT: "gen_ai.tool.output"
1612
+ TOOL_OUTPUT: "gen_ai.tool.output",
1613
+ // Spec replacement for the deprecated gen_ai.system attribute.
1614
+ PROVIDER_NAME: "gen_ai.provider.name",
1615
+ // Vendor-namespaced IO keys (dual-emitted by observe()/setInput()/
1616
+ // setOutput() alongside the deprecated gen_ai.request.input /
1617
+ // gen_ai.response.output during the OTel GenAI spec migration).
1618
+ AM_REQUEST_INPUT: "agentmark.request.input",
1619
+ AM_RESPONSE_OUTPUT: "agentmark.response.output",
1620
+ // Standard OTel GenAI semconv content keys (spec status: Development).
1621
+ // Accepted as fallbacks so spec-conformant instrumentation routed to
1622
+ // this transformer doesn't silently lose IO data on ingest.
1623
+ // https://opentelemetry.io/docs/specs/semconv/gen-ai/gen-ai-spans/
1624
+ INPUT_MESSAGES: "gen_ai.input.messages",
1625
+ OUTPUT_MESSAGES: "gen_ai.output.messages",
1626
+ SYSTEM_INSTRUCTIONS: "gen_ai.system_instructions",
1627
+ // Legacy (pre-1.27) OTel GenAI content keys.
1628
+ LEGACY_PROMPT: "gen_ai.prompt",
1629
+ LEGACY_COMPLETION: "gen_ai.completion",
1630
+ // Legacy OTel GenAI usage keys (pre input_tokens/output_tokens rename).
1631
+ USAGE_PROMPT_TOKENS: "gen_ai.usage.prompt_tokens",
1632
+ USAGE_COMPLETION_TOKENS: "gen_ai.usage.completion_tokens"
1613
1633
  };
1634
+ function parseLooseInput(raw) {
1635
+ try {
1636
+ const parsed = JSON.parse(raw);
1637
+ if (Array.isArray(parsed) && parsed.length > 0) {
1638
+ const isMessagesArray = parsed.every(
1639
+ (item) => item && typeof item === "object" && "role" in item && "content" in item
1640
+ );
1641
+ if (isMessagesArray) {
1642
+ return parsed;
1643
+ }
1644
+ }
1645
+ } catch {
1646
+ }
1647
+ return [{ role: "user", content: raw }];
1648
+ }
1649
+ function specPartsToText(parts) {
1650
+ return parts.filter((p) => p && p.type === "text" && typeof p.content === "string").map((p) => p.content).join("\n");
1651
+ }
1652
+ function parseSpecMessages(raw) {
1653
+ try {
1654
+ const parsed = JSON.parse(raw);
1655
+ if (!Array.isArray(parsed) || parsed.length === 0) return null;
1656
+ const messages = [];
1657
+ for (const msg of parsed) {
1658
+ if (!msg || !msg.role) continue;
1659
+ if (typeof msg.content === "string") {
1660
+ messages.push({ role: msg.role, content: msg.content });
1661
+ } else if (Array.isArray(msg.parts)) {
1662
+ const text = specPartsToText(msg.parts);
1663
+ if (text) messages.push({ role: msg.role, content: text });
1664
+ }
1665
+ }
1666
+ return messages.length > 0 ? messages : null;
1667
+ } catch {
1668
+ return null;
1669
+ }
1670
+ }
1671
+ function parseSystemInstructions(raw) {
1672
+ try {
1673
+ const parsed = JSON.parse(raw);
1674
+ if (Array.isArray(parsed)) {
1675
+ const text = specPartsToText(parsed);
1676
+ return text || null;
1677
+ }
1678
+ if (typeof parsed === "string") return parsed || null;
1679
+ } catch {
1680
+ }
1681
+ return raw || null;
1682
+ }
1614
1683
  var SpanNames = {
1615
1684
  // OTEL GenAI standard operation names (these can be followed by model/tool/agent name)
1616
1685
  CHAT: "chat",
@@ -1673,7 +1742,7 @@ var AgentMarkTransformer = class {
1673
1742
  if (span.name === SpanNames.CONVERSATION || span.name === SpanNames.SUBAGENT) {
1674
1743
  return "SPAN" /* SPAN */;
1675
1744
  }
1676
- if (attributes[GenAIAttributes.SYSTEM] === "anthropic") {
1745
+ if (attributes[GenAIAttributes.SYSTEM] === "anthropic" || attributes[GenAIAttributes.PROVIDER_NAME] === "anthropic") {
1677
1746
  if (attributes[GenAIAttributes.USAGE_INPUT_TOKENS] !== void 0 && attributes[GenAIAttributes.RESPONSE_OUTPUT] !== void 0) {
1678
1747
  return "GENERATION" /* GENERATION */;
1679
1748
  }
@@ -1684,7 +1753,7 @@ var AgentMarkTransformer = class {
1684
1753
  * Transform the span and extract normalized fields from GenAI attributes.
1685
1754
  */
1686
1755
  transform(_span, attributes) {
1687
- var _a;
1756
+ var _a, _b, _c, _d, _e, _f, _g, _h;
1688
1757
  const result = {};
1689
1758
  const responseModel = attributes[GenAIAttributes.RESPONSE_MODEL];
1690
1759
  const requestModel = attributes[GenAIAttributes.REQUEST_MODEL];
@@ -1693,8 +1762,8 @@ var AgentMarkTransformer = class {
1693
1762
  } else if (requestModel) {
1694
1763
  result.model = String(requestModel);
1695
1764
  }
1696
- const inputTokens = attributes[GenAIAttributes.USAGE_INPUT_TOKENS];
1697
- const outputTokens = attributes[GenAIAttributes.USAGE_OUTPUT_TOKENS];
1765
+ const inputTokens = (_a = attributes[GenAIAttributes.USAGE_INPUT_TOKENS]) != null ? _a : attributes[GenAIAttributes.USAGE_PROMPT_TOKENS];
1766
+ const outputTokens = (_b = attributes[GenAIAttributes.USAGE_OUTPUT_TOKENS]) != null ? _b : attributes[GenAIAttributes.USAGE_COMPLETION_TOKENS];
1698
1767
  if (typeof inputTokens === "number") {
1699
1768
  result.inputTokens = inputTokens;
1700
1769
  }
@@ -1730,37 +1799,72 @@ var AgentMarkTransformer = class {
1730
1799
  result.settings.temperature = temperature;
1731
1800
  }
1732
1801
  }
1733
- const requestInput = attributes[GenAIAttributes.REQUEST_INPUT];
1802
+ const requestInput = (_c = attributes[GenAIAttributes.REQUEST_INPUT]) != null ? _c : attributes[GenAIAttributes.AM_REQUEST_INPUT];
1803
+ const hasAgentmarkInput = Boolean(
1804
+ (_d = attributes["agentmark.input"]) != null ? _d : attributes["agentmark.props"]
1805
+ );
1734
1806
  if (requestInput && typeof requestInput === "string") {
1735
- try {
1736
- const parsed = JSON.parse(requestInput);
1737
- if (Array.isArray(parsed) && parsed.length > 0) {
1738
- const isMessagesArray = parsed.every(
1739
- (item) => item && typeof item === "object" && "role" in item && "content" in item
1740
- );
1741
- if (isMessagesArray) {
1742
- result.input = parsed;
1743
- } else {
1744
- result.input = [{ role: "user", content: requestInput }];
1745
- }
1746
- } else {
1747
- result.input = [{ role: "user", content: requestInput }];
1807
+ result.input = parseLooseInput(requestInput);
1808
+ } else if (!hasAgentmarkInput) {
1809
+ const inputMessages = attributes[GenAIAttributes.INPUT_MESSAGES];
1810
+ if (inputMessages && typeof inputMessages === "string") {
1811
+ const messages = parseSpecMessages(inputMessages);
1812
+ if (messages) result.input = messages;
1813
+ }
1814
+ if (!result.input) {
1815
+ const legacyPrompt = attributes[GenAIAttributes.LEGACY_PROMPT];
1816
+ if (legacyPrompt && typeof legacyPrompt === "string") {
1817
+ result.input = parseLooseInput(legacyPrompt);
1748
1818
  }
1749
- } catch {
1750
- result.input = [{ role: "user", content: requestInput }];
1751
1819
  }
1752
1820
  }
1753
- const responseOutput = attributes[GenAIAttributes.RESPONSE_OUTPUT];
1821
+ const systemInstructions = attributes[GenAIAttributes.SYSTEM_INSTRUCTIONS];
1822
+ if (systemInstructions && typeof systemInstructions === "string" && !hasAgentmarkInput) {
1823
+ const text = parseSystemInstructions(systemInstructions);
1824
+ if (text && (!result.input || ((_e = result.input[0]) == null ? void 0 : _e.role) !== "system")) {
1825
+ result.input = [{ role: "system", content: text }, ...(_f = result.input) != null ? _f : []];
1826
+ }
1827
+ }
1828
+ const responseOutput = (_g = attributes[GenAIAttributes.RESPONSE_OUTPUT]) != null ? _g : attributes[GenAIAttributes.AM_RESPONSE_OUTPUT];
1829
+ const hasAgentmarkOutput = Boolean(attributes["agentmark.output"]);
1754
1830
  if (responseOutput && typeof responseOutput === "string") {
1755
1831
  result.output = responseOutput;
1756
1832
  try {
1757
1833
  result.outputObject = JSON.parse(responseOutput);
1758
1834
  } catch {
1759
1835
  }
1836
+ } else if (!hasAgentmarkOutput) {
1837
+ const outputMessages = attributes[GenAIAttributes.OUTPUT_MESSAGES];
1838
+ if (outputMessages && typeof outputMessages === "string") {
1839
+ const messages = parseSpecMessages(outputMessages);
1840
+ if (messages) {
1841
+ result.output = messages.map((m) => m.content).join("\n");
1842
+ }
1843
+ }
1844
+ if (result.output === void 0) {
1845
+ const legacyCompletion = attributes[GenAIAttributes.LEGACY_COMPLETION];
1846
+ if (legacyCompletion && typeof legacyCompletion === "string") {
1847
+ result.output = legacyCompletion;
1848
+ try {
1849
+ result.outputObject = JSON.parse(legacyCompletion);
1850
+ } catch {
1851
+ }
1852
+ }
1853
+ }
1760
1854
  }
1761
- const amInput = (_a = attributes["agentmark.input"]) != null ? _a : attributes["agentmark.props"];
1855
+ const amInput = (_h = attributes["agentmark.input"]) != null ? _h : attributes["agentmark.props"];
1762
1856
  if (amInput && typeof amInput === "string" && !result.input) {
1763
- result.input = [{ role: "user", content: amInput }];
1857
+ let parsedMessages = null;
1858
+ try {
1859
+ const parsed = JSON.parse(amInput);
1860
+ if (Array.isArray(parsed) && parsed.length > 0 && parsed.every(
1861
+ (item) => item && typeof item === "object" && "role" in item && "content" in item
1862
+ )) {
1863
+ parsedMessages = parsed;
1864
+ }
1865
+ } catch {
1866
+ }
1867
+ result.input = parsedMessages != null ? parsedMessages : [{ role: "user", content: amInput }];
1764
1868
  }
1765
1869
  const amOutput = attributes["agentmark.output"];
1766
1870
  if (amOutput && typeof amOutput === "string" && !result.output) {
@@ -1826,7 +1930,7 @@ var Attrs = {
1826
1930
  USAGE_OUTPUT_TOKENS: "gen_ai.usage.output_tokens",
1827
1931
  // v1.37.0+ content attributes (canonical OTel GenAI semantic conventions).
1828
1932
  // SDKs that emit the AgentMark-scoped equivalents (`gen_ai.request.input`
1829
- // / `gen_ai.response.output` — used by `claude-agent-sdk-v0-adapter`) are
1933
+ // / `gen_ai.response.output`) are
1830
1934
  // also accepted here as fallbacks so an SDK picking either key set
1831
1935
  // doesn't silently lose IO data on ingest. The canonical pair always wins
1832
1936
  // when both are present.
@@ -1836,6 +1940,13 @@ var Attrs = {
1836
1940
  RESPONSE_OUTPUT_FALLBACK: "gen_ai.response.output",
1837
1941
  SYSTEM_INSTRUCTIONS: "gen_ai.system_instructions",
1838
1942
  TOOL_DEFINITIONS: "gen_ai.tool.definitions",
1943
+ // Legacy (pre-1.27) OTel GenAI content keys, accepted as last-resort
1944
+ // fallbacks below the canonical and AgentMark-scoped keys.
1945
+ LEGACY_PROMPT: "gen_ai.prompt",
1946
+ LEGACY_COMPLETION: "gen_ai.completion",
1947
+ // Legacy OTel GenAI usage keys (pre input_tokens/output_tokens rename).
1948
+ USAGE_PROMPT_TOKENS: "gen_ai.usage.prompt_tokens",
1949
+ USAGE_COMPLETION_TOKENS: "gen_ai.usage.completion_tokens",
1839
1950
  // Tool call attributes (v1.37.0+)
1840
1951
  TOOL_NAME: "gen_ai.tool.name",
1841
1952
  TOOL_CALL_ID: "gen_ai.tool.call.id",
@@ -1898,14 +2009,14 @@ var OtelGenAiTransformer = class {
1898
2009
  return "SPAN" /* SPAN */;
1899
2010
  }
1900
2011
  transform(span, attributes) {
1901
- var _a, _b;
2012
+ var _a, _b, _c, _d, _e, _f, _g, _h;
1902
2013
  const result = {};
1903
2014
  const model = attributes[Attrs.RESPONSE_MODEL] || attributes[Attrs.REQUEST_MODEL];
1904
2015
  if (model && typeof model === "string") {
1905
2016
  result.model = model;
1906
2017
  }
1907
- const inputTokens = attributes[Attrs.USAGE_INPUT_TOKENS];
1908
- const outputTokens = attributes[Attrs.USAGE_OUTPUT_TOKENS];
2018
+ const inputTokens = (_a = attributes[Attrs.USAGE_INPUT_TOKENS]) != null ? _a : attributes[Attrs.USAGE_PROMPT_TOKENS];
2019
+ const outputTokens = (_b = attributes[Attrs.USAGE_OUTPUT_TOKENS]) != null ? _b : attributes[Attrs.USAGE_COMPLETION_TOKENS];
1909
2020
  if (typeof inputTokens === "number") result.inputTokens = inputTokens;
1910
2021
  if (typeof outputTokens === "number") result.outputTokens = outputTokens;
1911
2022
  if (result.inputTokens !== void 0 && result.outputTokens !== void 0) {
@@ -1919,12 +2030,29 @@ var OtelGenAiTransformer = class {
1919
2030
  if (typeof temperature === "number") {
1920
2031
  result.settings = { ...result.settings, temperature };
1921
2032
  }
1922
- const inputMessages = (_a = attributes[Attrs.INPUT_MESSAGES]) != null ? _a : attributes[Attrs.REQUEST_INPUT_FALLBACK];
2033
+ const inputMessages = (_d = (_c = attributes[Attrs.INPUT_MESSAGES]) != null ? _c : attributes[Attrs.REQUEST_INPUT_FALLBACK]) != null ? _d : attributes[Attrs.LEGACY_PROMPT];
1923
2034
  if (inputMessages && typeof inputMessages === "string") {
1924
2035
  const messages = normalizeMessages(inputMessages);
1925
2036
  if (messages) result.input = messages;
1926
2037
  }
1927
- const outputMessages = (_b = attributes[Attrs.OUTPUT_MESSAGES]) != null ? _b : attributes[Attrs.RESPONSE_OUTPUT_FALLBACK];
2038
+ const systemInstructions = attributes[Attrs.SYSTEM_INSTRUCTIONS];
2039
+ if (systemInstructions && typeof systemInstructions === "string") {
2040
+ let text = null;
2041
+ try {
2042
+ const parsed = JSON.parse(systemInstructions);
2043
+ if (Array.isArray(parsed)) {
2044
+ text = partsToText(parsed) || null;
2045
+ } else if (typeof parsed === "string") {
2046
+ text = parsed || null;
2047
+ }
2048
+ } catch {
2049
+ text = systemInstructions;
2050
+ }
2051
+ if (text && (!result.input || ((_e = result.input[0]) == null ? void 0 : _e.role) !== "system")) {
2052
+ result.input = [{ role: "system", content: text }, ...(_f = result.input) != null ? _f : []];
2053
+ }
2054
+ }
2055
+ const outputMessages = (_h = (_g = attributes[Attrs.OUTPUT_MESSAGES]) != null ? _g : attributes[Attrs.RESPONSE_OUTPUT_FALLBACK]) != null ? _h : attributes[Attrs.LEGACY_COMPLETION];
1928
2056
  if (outputMessages && typeof outputMessages === "string") {
1929
2057
  const extracted = extractStructuredOutput(outputMessages);
1930
2058
  if (extracted) {
@@ -1932,6 +2060,8 @@ var OtelGenAiTransformer = class {
1932
2060
  if (extracted.outputObject) {
1933
2061
  result.outputObject = extracted.outputObject;
1934
2062
  }
2063
+ } else {
2064
+ result.output = outputMessages;
1935
2065
  }
1936
2066
  }
1937
2067
  const allMessages = attributes["pydantic_ai.all_messages"];
@@ -2176,7 +2306,7 @@ function resolveSemanticKind(normalized, allAttributes) {
2176
2306
  // src/normalizer/type-classifier.ts
2177
2307
  var TypeClassifier = class {
2178
2308
  classify(span, attributes) {
2179
- if (attributes["gen_ai.system"] || attributes["gen_ai.request.model"] || attributes["gen_ai.operation.name"]) {
2309
+ if (attributes["gen_ai.system"] || attributes["gen_ai.provider.name"] || attributes["gen_ai.request.model"] || attributes["gen_ai.operation.name"]) {
2180
2310
  return "GENERATION" /* GENERATION */;
2181
2311
  }
2182
2312
  if (attributes["ai.response.text"] || attributes["ai.result.text"] || attributes["ai.response.toolCalls"] || attributes["ai.result.toolCalls"]) {
@@ -2196,6 +2326,28 @@ registry.register("default-tracer", new MastraTransformer());
2196
2326
  registry.register("agentmark", new AgentMarkTransformer());
2197
2327
  registry.register("pydantic-ai", new OtelGenAiTransformer());
2198
2328
  registry.setDefault(new OtelGenAiTransformer());
2329
+ var OTLP_STATUS_CODE_MAP = {
2330
+ "0": "0",
2331
+ STATUS_CODE_UNSET: "0",
2332
+ UNSET: "0",
2333
+ Unset: "0",
2334
+ "1": "1",
2335
+ STATUS_CODE_OK: "1",
2336
+ OK: "1",
2337
+ Ok: "1",
2338
+ "2": "2",
2339
+ STATUS_CODE_ERROR: "2",
2340
+ ERROR: "2",
2341
+ Error: "2"
2342
+ };
2343
+ function normalizeOtlpStatusCode(raw) {
2344
+ var _a;
2345
+ if (raw === void 0 || raw === null || raw === "") {
2346
+ return "0";
2347
+ }
2348
+ const key = String(raw);
2349
+ return (_a = OTLP_STATUS_CODE_MAP[key]) != null ? _a : key;
2350
+ }
2199
2351
  function normalizeSpan(resource, scope, span) {
2200
2352
  var _a, _b, _c;
2201
2353
  const allAttributes = {
@@ -2228,7 +2380,7 @@ function normalizeSpan(resource, scope, span) {
2228
2380
  name: span.name,
2229
2381
  kind: span.kind.toString(),
2230
2382
  serviceName: (_a = resource.attributes) == null ? void 0 : _a["service.name"],
2231
- statusCode: ((_b = span.status) == null ? void 0 : _b.code.toString()) || "0",
2383
+ statusCode: normalizeOtlpStatusCode((_b = span.status) == null ? void 0 : _b.code),
2232
2384
  statusMessage: (_c = span.status) == null ? void 0 : _c.message,
2233
2385
  // Raw Data
2234
2386
  resourceAttributes: resource.attributes || {},
@@ -2251,6 +2403,9 @@ function normalizeSpan(resource, scope, span) {
2251
2403
  }
2252
2404
  const agentMarkAttributes = parseAgentMarkAttributes(allAttributes);
2253
2405
  Object.assign(normalized, agentMarkAttributes);
2406
+ if (!normalized.sessionId && allAttributes["gen_ai.conversation.id"]) {
2407
+ normalized.sessionId = String(allAttributes["gen_ai.conversation.id"]);
2408
+ }
2254
2409
  normalized.semanticKind = resolveSemanticKind(normalized, allAttributes);
2255
2410
  return normalized;
2256
2411
  }
@@ -2264,6 +2419,30 @@ function normalizeOtlpSpans(resourceSpans) {
2264
2419
  }
2265
2420
  return normalizedSpans;
2266
2421
  }
2422
+
2423
+ // src/trace-io.ts
2424
+ function present(v) {
2425
+ if (v === null || v === void 0) return false;
2426
+ if (typeof v === "string") return v.length > 0;
2427
+ return true;
2428
+ }
2429
+ function deriveTraceIO(spans) {
2430
+ var _a, _b;
2431
+ const rootSpan = (_a = spans.find((s) => !s.parentId)) != null ? _a : spans[0];
2432
+ const generationSpans = spans.filter((s) => s.type === "GENERATION").sort((a, b) => {
2433
+ var _a2, _b2;
2434
+ const ta = (_a2 = a.timestamp) != null ? _a2 : 0;
2435
+ const tb = (_b2 = b.timestamp) != null ? _b2 : 0;
2436
+ return ta < tb ? -1 : ta > tb ? 1 : 0;
2437
+ });
2438
+ const input = present(rootSpan == null ? void 0 : rootSpan.input) ? rootSpan == null ? void 0 : rootSpan.input : (_b = generationSpans.find((s) => present(s.input))) == null ? void 0 : _b.input;
2439
+ const lastGenWithOutput = [...generationSpans].reverse().find((s) => present(s.output));
2440
+ const output = present(rootSpan == null ? void 0 : rootSpan.output) ? rootSpan == null ? void 0 : rootSpan.output : lastGenWithOutput == null ? void 0 : lastGenWithOutput.output;
2441
+ return {
2442
+ ...present(input) ? { input } : {},
2443
+ ...present(output) ? { output } : {}
2444
+ };
2445
+ }
2267
2446
  export {
2268
2447
  AGENTMARK_SCOPE_NAME,
2269
2448
  AgentMarkTransformer,
@@ -2277,6 +2456,7 @@ export {
2277
2456
  TypeClassifier,
2278
2457
  convertOtlpAttributes,
2279
2458
  createSignature,
2459
+ deriveTraceIO,
2280
2460
  detectVersion,
2281
2461
  extractCustomMetadata,
2282
2462
  extractReasoningFromProviderMetadata,
@@ -2286,6 +2466,7 @@ export {
2286
2466
  generateTypeDefinitions,
2287
2467
  generateUnique8CharString,
2288
2468
  normalizeOtlpSpans,
2469
+ normalizeOtlpStatusCode,
2289
2470
  normalizeSpan,
2290
2471
  parseAgentMarkAttributes,
2291
2472
  parseMetadata,