@agentmark-ai/shared-utils 0.4.0 → 0.5.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.d.mts CHANGED
@@ -251,9 +251,11 @@ interface NormalizedSpan {
251
251
  datasetItemName?: string;
252
252
  datasetExpectedOutput?: string;
253
253
  datasetInput?: string;
254
+ experimentKey?: string;
254
255
  promptName?: string;
255
256
  props?: string;
256
257
  commitSha?: string;
258
+ sourceTreeHash?: string;
257
259
  metadata?: Record<string, string>;
258
260
  resourceAttributes: Record<string, any>;
259
261
  spanAttributes: Record<string, any>;
@@ -448,7 +450,7 @@ declare function parseAgentMarkAttributes(attributes: Record<string, any>, prefi
448
450
  declare const SEMANTIC_KINDS: readonly ["function", "llm", "tool", "agent", "retrieval", "embedding", "guardrail"];
449
451
  type SemanticKind = typeof SEMANTIC_KINDS[number];
450
452
  /**
451
- * Resolve the semantic kind of a span using an 8-level priority chain.
453
+ * Resolve the semantic kind of a span using a 9-level priority chain.
452
454
  *
453
455
  * Priority:
454
456
  * 1. normalized.semanticKind (from agentmark.span.kind attribute) — if valid
@@ -456,9 +458,11 @@ type SemanticKind = typeof SEMANTIC_KINDS[number];
456
458
  * 3. Framework-specific attributes (Vercel AI SDK, Traceloop, LangChain, Genkit)
457
459
  * 4. gen_ai.operation.name → llm/embedding
458
460
  * 5. Type = GENERATION → llm
459
- * 6. Has non-empty ToolCallstool
460
- * 7. Name-based heuristics
461
- * 8. Defaultfunction
461
+ * 6. Carries a model (gen_ai.request.model) llm — vendor-neutral generation
462
+ * signal; catches model calls the framework maps above don't name.
463
+ * 7. Has non-empty ToolCalls tool
464
+ * 8. Name-based heuristics
465
+ * 9. Default → function
462
466
  */
463
467
  declare function resolveSemanticKind(normalized: Partial<NormalizedSpan> & {
464
468
  type: SpanType;
@@ -558,43 +562,4 @@ declare function normalizeSpan(resource: OtelResource, scope: OtelScope, span: O
558
562
  */
559
563
  declare function normalizeOtlpSpans(resourceSpans: OtlpResourceSpans[]): NormalizedSpan[];
560
564
 
561
- /**
562
- * Compute a stable identifier for a dataset row, derived from its input.
563
- *
564
- * The identifier survives row reordering, additions, and deletions in the
565
- * dataset — anything that doesn't change the row's *content* keeps the same
566
- * id. This is the property regression-vs-baseline comparisons need: the
567
- * same logical test case in two different runs must produce the same name
568
- * so the baseline lookup can match them.
569
- *
570
- * Format: first 12 hex characters of the MD5 digest of the row's input,
571
- * canonicalised with sorted keys at every level. This matches the Python
572
- * runner's implementation byte-for-byte so cross-runtime comparisons
573
- * (TS-emitted baseline vs. Python-emitted PR run, or vice versa) produce
574
- * matching identifiers.
575
- *
576
- * Falls back to the positional index as a string when the row has no
577
- * input — in that case there is nothing stable to hash, so positional is
578
- * the best we can do.
579
- *
580
- * Note: we use MD5 here for compatibility with the existing Python
581
- * implementation. MD5 is not used as a security primitive — it's used as a
582
- * non-cryptographic content fingerprint, which is its appropriate role.
583
- */
584
- declare function computeDatasetItemName(input: unknown, fallbackIndex: number): string;
585
- /**
586
- * Stringify a value as canonical JSON with object keys sorted recursively.
587
- *
588
- * Differs from `JSON.stringify`:
589
- * - Object keys are sorted lexicographically at every level of nesting, so
590
- * `{a: 1, b: 2}` and `{b: 2, a: 1}` produce the same output.
591
- * - Non-serializable values (functions, symbols, bigints, undefined) are
592
- * coerced to strings via `String(value)`, matching Python's `default=str`
593
- * behavior in `json.dumps`.
594
- *
595
- * Exported for testing and for callers that need the same canonicalisation
596
- * for purposes other than item-name hashing.
597
- */
598
- declare function canonicalJsonStringify(value: unknown): string;
599
-
600
- export { AGENTMARK_SCOPE_NAME, AgentMarkTransformer, type AgentmarkConfig, type AgentmarkModelConfig, type AgentmarkModelSchema, type AgentmarkModelSettingsConfig, type AgentmarkModelSettingsSchema, AiSdkTransformer, type AiSdkVersion, type AttributeExtractor, AgentMarkTransformer as ClaudeAgentTransformer, type GenerateTypesLanguage, MastraTransformer, type McpServerConfig, type McpServers, type McpStdioServerConfig, type McpUrlServerConfig, type Message, type ModelSettingsTypeAspectRatio, type ModelSettingsTypeImageSize, type ModelSettingsTypeSelect, type ModelSettingsTypeSlider, type NormalizedSpan, type OtelEvent, OtelGenAiTransformer, type OtelLink, type OtelResource, type OtelScope, type OtelSpan, type OtlpAttribute, type OtlpAttributeValue, type OtlpEvent, type OtlpLink, type OtlpResource, type OtlpResourceSpans, type OtlpScope, type OtlpScopeSpans, type OtlpSpan, SEMANTIC_KINDS, type ScopeTransformer, type SemanticKind, SpanType, type StandardMessageContent, type StandardTextContent, type StandardToolCallContent, type StandardToolResultContent, type ToolCall, TransformerRegistry, TypeClassifier, canonicalJsonStringify, computeDatasetItemName, convertOtlpAttributes, createSignature, detectVersion, extractCustomMetadata, extractReasoningFromProviderMetadata, extractResourceScopeSpan, fetchPromptsFrontmatter, findPromptFiles, generateTypeDefinitions, generateUnique8CharString, normalizeOtlpSpans, normalizeSpan, parseAgentMarkAttributes, parseMetadata, parseTokens, registry, resolveSemanticKind, toFrontMatter, typeClassifier, verifySignature };
565
+ export { AGENTMARK_SCOPE_NAME, AgentMarkTransformer, type AgentmarkConfig, type AgentmarkModelConfig, type AgentmarkModelSchema, type AgentmarkModelSettingsConfig, type AgentmarkModelSettingsSchema, AiSdkTransformer, type AiSdkVersion, type AttributeExtractor, AgentMarkTransformer as ClaudeAgentTransformer, type GenerateTypesLanguage, MastraTransformer, type McpServerConfig, type McpServers, type McpStdioServerConfig, type McpUrlServerConfig, type Message, type ModelSettingsTypeAspectRatio, type ModelSettingsTypeImageSize, type ModelSettingsTypeSelect, type ModelSettingsTypeSlider, type NormalizedSpan, type OtelEvent, OtelGenAiTransformer, type OtelLink, type OtelResource, type OtelScope, type OtelSpan, type OtlpAttribute, type OtlpAttributeValue, type OtlpEvent, type OtlpLink, type OtlpResource, type OtlpResourceSpans, type OtlpScope, type OtlpScopeSpans, type OtlpSpan, SEMANTIC_KINDS, type ScopeTransformer, type SemanticKind, SpanType, type StandardMessageContent, type StandardTextContent, type StandardToolCallContent, type StandardToolResultContent, type ToolCall, TransformerRegistry, TypeClassifier, convertOtlpAttributes, createSignature, detectVersion, extractCustomMetadata, extractReasoningFromProviderMetadata, extractResourceScopeSpan, fetchPromptsFrontmatter, findPromptFiles, generateTypeDefinitions, generateUnique8CharString, normalizeOtlpSpans, normalizeSpan, parseAgentMarkAttributes, parseMetadata, parseTokens, registry, resolveSemanticKind, toFrontMatter, typeClassifier, verifySignature };
package/dist/index.d.ts CHANGED
@@ -251,9 +251,11 @@ interface NormalizedSpan {
251
251
  datasetItemName?: string;
252
252
  datasetExpectedOutput?: string;
253
253
  datasetInput?: string;
254
+ experimentKey?: string;
254
255
  promptName?: string;
255
256
  props?: string;
256
257
  commitSha?: string;
258
+ sourceTreeHash?: string;
257
259
  metadata?: Record<string, string>;
258
260
  resourceAttributes: Record<string, any>;
259
261
  spanAttributes: Record<string, any>;
@@ -448,7 +450,7 @@ declare function parseAgentMarkAttributes(attributes: Record<string, any>, prefi
448
450
  declare const SEMANTIC_KINDS: readonly ["function", "llm", "tool", "agent", "retrieval", "embedding", "guardrail"];
449
451
  type SemanticKind = typeof SEMANTIC_KINDS[number];
450
452
  /**
451
- * Resolve the semantic kind of a span using an 8-level priority chain.
453
+ * Resolve the semantic kind of a span using a 9-level priority chain.
452
454
  *
453
455
  * Priority:
454
456
  * 1. normalized.semanticKind (from agentmark.span.kind attribute) — if valid
@@ -456,9 +458,11 @@ type SemanticKind = typeof SEMANTIC_KINDS[number];
456
458
  * 3. Framework-specific attributes (Vercel AI SDK, Traceloop, LangChain, Genkit)
457
459
  * 4. gen_ai.operation.name → llm/embedding
458
460
  * 5. Type = GENERATION → llm
459
- * 6. Has non-empty ToolCallstool
460
- * 7. Name-based heuristics
461
- * 8. Defaultfunction
461
+ * 6. Carries a model (gen_ai.request.model) llm — vendor-neutral generation
462
+ * signal; catches model calls the framework maps above don't name.
463
+ * 7. Has non-empty ToolCalls tool
464
+ * 8. Name-based heuristics
465
+ * 9. Default → function
462
466
  */
463
467
  declare function resolveSemanticKind(normalized: Partial<NormalizedSpan> & {
464
468
  type: SpanType;
@@ -558,43 +562,4 @@ declare function normalizeSpan(resource: OtelResource, scope: OtelScope, span: O
558
562
  */
559
563
  declare function normalizeOtlpSpans(resourceSpans: OtlpResourceSpans[]): NormalizedSpan[];
560
564
 
561
- /**
562
- * Compute a stable identifier for a dataset row, derived from its input.
563
- *
564
- * The identifier survives row reordering, additions, and deletions in the
565
- * dataset — anything that doesn't change the row's *content* keeps the same
566
- * id. This is the property regression-vs-baseline comparisons need: the
567
- * same logical test case in two different runs must produce the same name
568
- * so the baseline lookup can match them.
569
- *
570
- * Format: first 12 hex characters of the MD5 digest of the row's input,
571
- * canonicalised with sorted keys at every level. This matches the Python
572
- * runner's implementation byte-for-byte so cross-runtime comparisons
573
- * (TS-emitted baseline vs. Python-emitted PR run, or vice versa) produce
574
- * matching identifiers.
575
- *
576
- * Falls back to the positional index as a string when the row has no
577
- * input — in that case there is nothing stable to hash, so positional is
578
- * the best we can do.
579
- *
580
- * Note: we use MD5 here for compatibility with the existing Python
581
- * implementation. MD5 is not used as a security primitive — it's used as a
582
- * non-cryptographic content fingerprint, which is its appropriate role.
583
- */
584
- declare function computeDatasetItemName(input: unknown, fallbackIndex: number): string;
585
- /**
586
- * Stringify a value as canonical JSON with object keys sorted recursively.
587
- *
588
- * Differs from `JSON.stringify`:
589
- * - Object keys are sorted lexicographically at every level of nesting, so
590
- * `{a: 1, b: 2}` and `{b: 2, a: 1}` produce the same output.
591
- * - Non-serializable values (functions, symbols, bigints, undefined) are
592
- * coerced to strings via `String(value)`, matching Python's `default=str`
593
- * behavior in `json.dumps`.
594
- *
595
- * Exported for testing and for callers that need the same canonicalisation
596
- * for purposes other than item-name hashing.
597
- */
598
- declare function canonicalJsonStringify(value: unknown): string;
599
-
600
- export { AGENTMARK_SCOPE_NAME, AgentMarkTransformer, type AgentmarkConfig, type AgentmarkModelConfig, type AgentmarkModelSchema, type AgentmarkModelSettingsConfig, type AgentmarkModelSettingsSchema, AiSdkTransformer, type AiSdkVersion, type AttributeExtractor, AgentMarkTransformer as ClaudeAgentTransformer, type GenerateTypesLanguage, MastraTransformer, type McpServerConfig, type McpServers, type McpStdioServerConfig, type McpUrlServerConfig, type Message, type ModelSettingsTypeAspectRatio, type ModelSettingsTypeImageSize, type ModelSettingsTypeSelect, type ModelSettingsTypeSlider, type NormalizedSpan, type OtelEvent, OtelGenAiTransformer, type OtelLink, type OtelResource, type OtelScope, type OtelSpan, type OtlpAttribute, type OtlpAttributeValue, type OtlpEvent, type OtlpLink, type OtlpResource, type OtlpResourceSpans, type OtlpScope, type OtlpScopeSpans, type OtlpSpan, SEMANTIC_KINDS, type ScopeTransformer, type SemanticKind, SpanType, type StandardMessageContent, type StandardTextContent, type StandardToolCallContent, type StandardToolResultContent, type ToolCall, TransformerRegistry, TypeClassifier, canonicalJsonStringify, computeDatasetItemName, convertOtlpAttributes, createSignature, detectVersion, extractCustomMetadata, extractReasoningFromProviderMetadata, extractResourceScopeSpan, fetchPromptsFrontmatter, findPromptFiles, generateTypeDefinitions, generateUnique8CharString, normalizeOtlpSpans, normalizeSpan, parseAgentMarkAttributes, parseMetadata, parseTokens, registry, resolveSemanticKind, toFrontMatter, typeClassifier, verifySignature };
565
+ export { AGENTMARK_SCOPE_NAME, AgentMarkTransformer, type AgentmarkConfig, type AgentmarkModelConfig, type AgentmarkModelSchema, type AgentmarkModelSettingsConfig, type AgentmarkModelSettingsSchema, AiSdkTransformer, type AiSdkVersion, type AttributeExtractor, AgentMarkTransformer as ClaudeAgentTransformer, type GenerateTypesLanguage, MastraTransformer, type McpServerConfig, type McpServers, type McpStdioServerConfig, type McpUrlServerConfig, type Message, type ModelSettingsTypeAspectRatio, type ModelSettingsTypeImageSize, type ModelSettingsTypeSelect, type ModelSettingsTypeSlider, type NormalizedSpan, type OtelEvent, OtelGenAiTransformer, type OtelLink, type OtelResource, type OtelScope, type OtelSpan, type OtlpAttribute, type OtlpAttributeValue, type OtlpEvent, type OtlpLink, type OtlpResource, type OtlpResourceSpans, type OtlpScope, type OtlpScopeSpans, type OtlpSpan, SEMANTIC_KINDS, type ScopeTransformer, type SemanticKind, SpanType, type StandardMessageContent, type StandardTextContent, type StandardToolCallContent, type StandardToolResultContent, type ToolCall, TransformerRegistry, TypeClassifier, convertOtlpAttributes, createSignature, detectVersion, extractCustomMetadata, extractReasoningFromProviderMetadata, extractResourceScopeSpan, fetchPromptsFrontmatter, findPromptFiles, generateTypeDefinitions, generateUnique8CharString, normalizeOtlpSpans, normalizeSpan, parseAgentMarkAttributes, parseMetadata, parseTokens, registry, resolveSemanticKind, toFrontMatter, typeClassifier, verifySignature };
package/dist/index.js CHANGED
@@ -40,8 +40,6 @@ __export(index_exports, {
40
40
  SpanType: () => SpanType,
41
41
  TransformerRegistry: () => TransformerRegistry,
42
42
  TypeClassifier: () => TypeClassifier,
43
- canonicalJsonStringify: () => canonicalJsonStringify,
44
- computeDatasetItemName: () => computeDatasetItemName,
45
43
  convertOtlpAttributes: () => convertOtlpAttributes,
46
44
  createSignature: () => createSignature,
47
45
  detectVersion: () => detectVersion,
@@ -138,53 +136,24 @@ function hexToBytes(hex) {
138
136
  }
139
137
 
140
138
  // src/serialize.ts
139
+ var import_js_yaml = __toESM(require("js-yaml"));
141
140
  function toFrontMatter(content) {
142
- function jsonToFrontMatter(json, indent = 0) {
143
- let frontMatter = "";
144
- const indentation = " ".repeat(indent);
145
- for (const key in json) {
146
- if (Object.prototype.hasOwnProperty.call(json, key)) {
147
- const value = json[key];
148
- if (typeof value === "object" && !Array.isArray(value)) {
149
- frontMatter += `${indentation}${key}:
150
- `;
151
- frontMatter += jsonToFrontMatter(value, indent + 1);
152
- } else if (Array.isArray(value)) {
153
- frontMatter += `${indentation}${key}:
154
- `;
155
- value.forEach((item) => {
156
- if (typeof item === "object") {
157
- frontMatter += `${indentation}-
158
- `;
159
- frontMatter += jsonToFrontMatter(item, indent + 2);
160
- } else {
161
- frontMatter += `${indentation}- ${item}
162
- `;
163
- }
164
- });
165
- } else {
166
- frontMatter += `${indentation}${key}: ${value}
167
- `;
168
- }
169
- }
170
- }
171
- return frontMatter;
172
- }
141
+ const body = Object.keys(content).length === 0 ? "" : import_js_yaml.default.dump(content, { lineWidth: -1, noRefs: true, skipInvalid: true });
173
142
  return `---
174
- ${jsonToFrontMatter(content)}---
143
+ ${body}---
175
144
  `;
176
145
  }
177
146
 
178
147
  // src/generate-types.ts
179
148
  var fs = __toESM(require("fs-extra"));
180
149
  var import_path = __toESM(require("path"));
181
- var import_js_yaml = __toESM(require("js-yaml"));
150
+ var import_js_yaml2 = __toESM(require("js-yaml"));
182
151
  function extractFrontmatter(content) {
183
152
  const match = content.match(/^---\r?\n([\s\S]*?)\r?\n---/);
184
153
  if (!match) {
185
154
  return { attributes: {} };
186
155
  }
187
- const attributes = import_js_yaml.default.load(match[1]);
156
+ const attributes = import_js_yaml2.default.load(match[1]);
188
157
  return { attributes: attributes || {} };
189
158
  }
190
159
  var _compile = null;
@@ -988,39 +957,50 @@ var AiSdkV4Strategy = class {
988
957
  return attributes["gen_ai.request.model"] || attributes["ai.model.id"];
989
958
  }
990
959
  extractInput(attributes) {
991
- let messagesValue;
992
- if (attributes["ai.prompt.messages"] !== void 0) {
993
- messagesValue = attributes["ai.prompt.messages"];
994
- } else if (attributes["ai.prompt"] !== void 0) {
995
- const promptValue = attributes["ai.prompt"];
996
- if (typeof promptValue === "string") {
997
- try {
998
- const parsed = JSON.parse(promptValue);
999
- messagesValue = parsed.messages || parsed;
1000
- } catch {
1001
- return void 0;
1002
- }
1003
- } else {
1004
- messagesValue = promptValue.messages || promptValue;
1005
- }
1006
- } else {
960
+ const raw = attributes["ai.prompt.messages"] !== void 0 ? attributes["ai.prompt.messages"] : attributes["ai.prompt"];
961
+ if (raw === void 0) {
1007
962
  return void 0;
1008
963
  }
1009
- let messages;
1010
- if (typeof messagesValue === "string") {
964
+ let value = raw;
965
+ if (typeof value === "string") {
1011
966
  try {
1012
- messages = JSON.parse(messagesValue);
967
+ value = JSON.parse(value);
1013
968
  } catch {
1014
- return void 0;
1015
969
  }
1016
- } else {
1017
- messages = messagesValue;
1018
970
  }
1019
- if (!Array.isArray(messages)) {
971
+ const messages = this.coerceToMessages(value);
972
+ if (!messages) {
1020
973
  return void 0;
1021
974
  }
1022
975
  return this.normalizeMessages(messages);
1023
976
  }
977
+ /**
978
+ * Coerce the shapes `ai.prompt` / `ai.prompt.messages` can take into a
979
+ * messages array: a raw array, `{ messages, system? }`,
980
+ * `{ prompt, system? }` (string prompt), or a bare string.
981
+ */
982
+ coerceToMessages(value) {
983
+ if (typeof value === "string") {
984
+ return [{ role: "user", content: value }];
985
+ }
986
+ if (Array.isArray(value)) {
987
+ return value;
988
+ }
989
+ if (value && typeof value === "object") {
990
+ if (Array.isArray(value.messages)) {
991
+ return typeof value.system === "string" ? [{ role: "system", content: value.system }, ...value.messages] : value.messages;
992
+ }
993
+ if (typeof value.prompt === "string") {
994
+ const messages = [];
995
+ if (typeof value.system === "string") {
996
+ messages.push({ role: "system", content: value.system });
997
+ }
998
+ messages.push({ role: "user", content: value.prompt });
999
+ return messages;
1000
+ }
1001
+ }
1002
+ return void 0;
1003
+ }
1024
1004
  extractOutput(attributes) {
1025
1005
  if (attributes["ai.result.text"] !== void 0) return attributes["ai.result.text"];
1026
1006
  if (attributes["ai.response.text"] !== void 0) return attributes["ai.response.text"];
@@ -1284,39 +1264,54 @@ var AiSdkV5Strategy = class {
1284
1264
  return attributes["gen_ai.request.model"] || attributes["ai.model.id"];
1285
1265
  }
1286
1266
  extractInput(attributes) {
1287
- let messagesValue;
1288
- if (attributes["ai.prompt.messages"] !== void 0) {
1289
- messagesValue = attributes["ai.prompt.messages"];
1290
- } else if (attributes["ai.prompt"] !== void 0) {
1291
- const promptValue = attributes["ai.prompt"];
1292
- if (typeof promptValue === "string") {
1293
- try {
1294
- const parsed = JSON.parse(promptValue);
1295
- messagesValue = parsed.messages || parsed;
1296
- } catch {
1297
- return void 0;
1298
- }
1299
- } else {
1300
- messagesValue = promptValue.messages || promptValue;
1301
- }
1302
- } else {
1267
+ const raw = attributes["ai.prompt.messages"] !== void 0 ? attributes["ai.prompt.messages"] : attributes["ai.prompt"];
1268
+ if (raw === void 0) {
1303
1269
  return void 0;
1304
1270
  }
1305
- let messages;
1306
- if (typeof messagesValue === "string") {
1271
+ let value = raw;
1272
+ if (typeof value === "string") {
1307
1273
  try {
1308
- messages = JSON.parse(messagesValue);
1274
+ value = JSON.parse(value);
1309
1275
  } catch {
1310
- return void 0;
1311
1276
  }
1312
- } else {
1313
- messages = messagesValue;
1314
1277
  }
1315
- if (!Array.isArray(messages)) {
1278
+ const messages = this.coerceToMessages(value);
1279
+ if (!messages) {
1316
1280
  return void 0;
1317
1281
  }
1318
1282
  return this.normalizeMessages(messages);
1319
1283
  }
1284
+ /**
1285
+ * Coerce the shapes `ai.prompt` / `ai.prompt.messages` can take into a
1286
+ * messages array:
1287
+ * - `[ ...messages ]` — ai.prompt.messages (leaf spans)
1288
+ * - `{ messages: [...], system? }` — ai.prompt with messages
1289
+ * - `{ prompt: "text", system? }` — ai.prompt for a string prompt
1290
+ * (the parent wrapper spans)
1291
+ * - `"text"` — a bare (non-JSON) prompt string
1292
+ */
1293
+ coerceToMessages(value) {
1294
+ if (typeof value === "string") {
1295
+ return [{ role: "user", content: value }];
1296
+ }
1297
+ if (Array.isArray(value)) {
1298
+ return value;
1299
+ }
1300
+ if (value && typeof value === "object") {
1301
+ if (Array.isArray(value.messages)) {
1302
+ return typeof value.system === "string" ? [{ role: "system", content: value.system }, ...value.messages] : value.messages;
1303
+ }
1304
+ if (typeof value.prompt === "string") {
1305
+ const messages = [];
1306
+ if (typeof value.system === "string") {
1307
+ messages.push({ role: "system", content: value.system });
1308
+ }
1309
+ messages.push({ role: "user", content: value.prompt });
1310
+ return messages;
1311
+ }
1312
+ }
1313
+ return void 0;
1314
+ }
1320
1315
  extractOutput(attributes) {
1321
1316
  if (attributes["ai.response.text"] !== void 0) return attributes["ai.response.text"];
1322
1317
  return void 0;
@@ -1657,6 +1652,8 @@ function parseAgentMarkAttributes(attributes, prefix = "agentmark.") {
1657
1652
  if (get("dataset_expected_output")) result.datasetExpectedOutput = String(get("dataset_expected_output"));
1658
1653
  if (get("dataset_input")) result.datasetInput = String(get("dataset_input"));
1659
1654
  if (get("dataset_path")) result.datasetPath = String(get("dataset_path"));
1655
+ if (get("experiment_key")) result.experimentKey = String(get("experiment_key"));
1656
+ if (get("source_tree_hash")) result.sourceTreeHash = String(get("source_tree_hash"));
1660
1657
  return result;
1661
1658
  }
1662
1659
 
@@ -2171,13 +2168,20 @@ var FRAMEWORK_MAPPINGS = [
2171
2168
  {
2172
2169
  key: "ai.operationId",
2173
2170
  // Vercel AI SDK
2171
+ // The AI SDK emits ai.operationId WITH the "ai." prefix (e.g.
2172
+ // "ai.generateText"); accept both prefixed and unprefixed so generation
2173
+ // wrappers resolve to "llm" instead of falling through to "function".
2174
2174
  map: {
2175
2175
  "embed": "embedding",
2176
2176
  "ai.embed": "embedding",
2177
2177
  "generateText": "llm",
2178
+ "ai.generateText": "llm",
2178
2179
  "streamText": "llm",
2180
+ "ai.streamText": "llm",
2179
2181
  "generateObject": "llm",
2180
- "streamObject": "llm"
2182
+ "ai.generateObject": "llm",
2183
+ "streamObject": "llm",
2184
+ "ai.streamObject": "llm"
2181
2185
  }
2182
2186
  },
2183
2187
  {
@@ -2221,6 +2225,9 @@ function resolveSemanticKind(normalized, allAttributes) {
2221
2225
  if (normalized.type === "GENERATION" /* GENERATION */) {
2222
2226
  return "llm";
2223
2227
  }
2228
+ if (normalized.model) {
2229
+ return "llm";
2230
+ }
2224
2231
  if (normalized.toolCalls && normalized.toolCalls.length > 0) {
2225
2232
  return "tool";
2226
2233
  }
@@ -2322,36 +2329,6 @@ function normalizeOtlpSpans(resourceSpans) {
2322
2329
  }
2323
2330
  return normalizedSpans;
2324
2331
  }
2325
-
2326
- // src/dataset-item-name.ts
2327
- var import_node_crypto = require("crypto");
2328
- function computeDatasetItemName(input, fallbackIndex) {
2329
- if (input === void 0 || input === null) {
2330
- return String(fallbackIndex);
2331
- }
2332
- const canonical = canonicalJsonStringify(input);
2333
- return (0, import_node_crypto.createHash)("md5").update(canonical).digest("hex").slice(0, 12);
2334
- }
2335
- function canonicalJsonStringify(value) {
2336
- if (value === null) return "null";
2337
- if (value === void 0) return JSON.stringify(String(void 0));
2338
- const t = typeof value;
2339
- if (t === "string" || t === "number" || t === "boolean") {
2340
- return JSON.stringify(value);
2341
- }
2342
- if (t === "bigint" || t === "function" || t === "symbol") {
2343
- return JSON.stringify(String(value));
2344
- }
2345
- if (Array.isArray(value)) {
2346
- return "[" + value.map(canonicalJsonStringify).join(",") + "]";
2347
- }
2348
- const obj = value;
2349
- const keys = Object.keys(obj).sort();
2350
- const parts = keys.map((k) => {
2351
- return JSON.stringify(k) + ":" + canonicalJsonStringify(obj[k]);
2352
- });
2353
- return "{" + parts.join(",") + "}";
2354
- }
2355
2332
  // Annotate the CommonJS export names for ESM import in node:
2356
2333
  0 && (module.exports = {
2357
2334
  AGENTMARK_SCOPE_NAME,
@@ -2364,8 +2341,6 @@ function canonicalJsonStringify(value) {
2364
2341
  SpanType,
2365
2342
  TransformerRegistry,
2366
2343
  TypeClassifier,
2367
- canonicalJsonStringify,
2368
- computeDatasetItemName,
2369
2344
  convertOtlpAttributes,
2370
2345
  createSignature,
2371
2346
  detectVersion,