@agentmark-ai/shared-utils 0.3.3 → 0.5.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.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;
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;
package/dist/index.js CHANGED
@@ -986,39 +986,50 @@ var AiSdkV4Strategy = class {
986
986
  return attributes["gen_ai.request.model"] || attributes["ai.model.id"];
987
987
  }
988
988
  extractInput(attributes) {
989
- let messagesValue;
990
- if (attributes["ai.prompt.messages"] !== void 0) {
991
- messagesValue = attributes["ai.prompt.messages"];
992
- } else if (attributes["ai.prompt"] !== void 0) {
993
- const promptValue = attributes["ai.prompt"];
994
- if (typeof promptValue === "string") {
995
- try {
996
- const parsed = JSON.parse(promptValue);
997
- messagesValue = parsed.messages || parsed;
998
- } catch {
999
- return void 0;
1000
- }
1001
- } else {
1002
- messagesValue = promptValue.messages || promptValue;
1003
- }
1004
- } else {
989
+ const raw = attributes["ai.prompt.messages"] !== void 0 ? attributes["ai.prompt.messages"] : attributes["ai.prompt"];
990
+ if (raw === void 0) {
1005
991
  return void 0;
1006
992
  }
1007
- let messages;
1008
- if (typeof messagesValue === "string") {
993
+ let value = raw;
994
+ if (typeof value === "string") {
1009
995
  try {
1010
- messages = JSON.parse(messagesValue);
996
+ value = JSON.parse(value);
1011
997
  } catch {
1012
- return void 0;
1013
998
  }
1014
- } else {
1015
- messages = messagesValue;
1016
999
  }
1017
- if (!Array.isArray(messages)) {
1000
+ const messages = this.coerceToMessages(value);
1001
+ if (!messages) {
1018
1002
  return void 0;
1019
1003
  }
1020
1004
  return this.normalizeMessages(messages);
1021
1005
  }
1006
+ /**
1007
+ * Coerce the shapes `ai.prompt` / `ai.prompt.messages` can take into a
1008
+ * messages array: a raw array, `{ messages, system? }`,
1009
+ * `{ prompt, system? }` (string prompt), or a bare string.
1010
+ */
1011
+ coerceToMessages(value) {
1012
+ if (typeof value === "string") {
1013
+ return [{ role: "user", content: value }];
1014
+ }
1015
+ if (Array.isArray(value)) {
1016
+ return value;
1017
+ }
1018
+ if (value && typeof value === "object") {
1019
+ if (Array.isArray(value.messages)) {
1020
+ return typeof value.system === "string" ? [{ role: "system", content: value.system }, ...value.messages] : value.messages;
1021
+ }
1022
+ if (typeof value.prompt === "string") {
1023
+ const messages = [];
1024
+ if (typeof value.system === "string") {
1025
+ messages.push({ role: "system", content: value.system });
1026
+ }
1027
+ messages.push({ role: "user", content: value.prompt });
1028
+ return messages;
1029
+ }
1030
+ }
1031
+ return void 0;
1032
+ }
1022
1033
  extractOutput(attributes) {
1023
1034
  if (attributes["ai.result.text"] !== void 0) return attributes["ai.result.text"];
1024
1035
  if (attributes["ai.response.text"] !== void 0) return attributes["ai.response.text"];
@@ -1282,39 +1293,54 @@ var AiSdkV5Strategy = class {
1282
1293
  return attributes["gen_ai.request.model"] || attributes["ai.model.id"];
1283
1294
  }
1284
1295
  extractInput(attributes) {
1285
- let messagesValue;
1286
- if (attributes["ai.prompt.messages"] !== void 0) {
1287
- messagesValue = attributes["ai.prompt.messages"];
1288
- } else if (attributes["ai.prompt"] !== void 0) {
1289
- const promptValue = attributes["ai.prompt"];
1290
- if (typeof promptValue === "string") {
1291
- try {
1292
- const parsed = JSON.parse(promptValue);
1293
- messagesValue = parsed.messages || parsed;
1294
- } catch {
1295
- return void 0;
1296
- }
1297
- } else {
1298
- messagesValue = promptValue.messages || promptValue;
1299
- }
1300
- } else {
1296
+ const raw = attributes["ai.prompt.messages"] !== void 0 ? attributes["ai.prompt.messages"] : attributes["ai.prompt"];
1297
+ if (raw === void 0) {
1301
1298
  return void 0;
1302
1299
  }
1303
- let messages;
1304
- if (typeof messagesValue === "string") {
1300
+ let value = raw;
1301
+ if (typeof value === "string") {
1305
1302
  try {
1306
- messages = JSON.parse(messagesValue);
1303
+ value = JSON.parse(value);
1307
1304
  } catch {
1308
- return void 0;
1309
1305
  }
1310
- } else {
1311
- messages = messagesValue;
1312
1306
  }
1313
- if (!Array.isArray(messages)) {
1307
+ const messages = this.coerceToMessages(value);
1308
+ if (!messages) {
1314
1309
  return void 0;
1315
1310
  }
1316
1311
  return this.normalizeMessages(messages);
1317
1312
  }
1313
+ /**
1314
+ * Coerce the shapes `ai.prompt` / `ai.prompt.messages` can take into a
1315
+ * messages array:
1316
+ * - `[ ...messages ]` — ai.prompt.messages (leaf spans)
1317
+ * - `{ messages: [...], system? }` — ai.prompt with messages
1318
+ * - `{ prompt: "text", system? }` — ai.prompt for a string prompt
1319
+ * (the parent wrapper spans)
1320
+ * - `"text"` — a bare (non-JSON) prompt string
1321
+ */
1322
+ coerceToMessages(value) {
1323
+ if (typeof value === "string") {
1324
+ return [{ role: "user", content: value }];
1325
+ }
1326
+ if (Array.isArray(value)) {
1327
+ return value;
1328
+ }
1329
+ if (value && typeof value === "object") {
1330
+ if (Array.isArray(value.messages)) {
1331
+ return typeof value.system === "string" ? [{ role: "system", content: value.system }, ...value.messages] : value.messages;
1332
+ }
1333
+ if (typeof value.prompt === "string") {
1334
+ const messages = [];
1335
+ if (typeof value.system === "string") {
1336
+ messages.push({ role: "system", content: value.system });
1337
+ }
1338
+ messages.push({ role: "user", content: value.prompt });
1339
+ return messages;
1340
+ }
1341
+ }
1342
+ return void 0;
1343
+ }
1318
1344
  extractOutput(attributes) {
1319
1345
  if (attributes["ai.response.text"] !== void 0) return attributes["ai.response.text"];
1320
1346
  return void 0;
@@ -1655,6 +1681,8 @@ function parseAgentMarkAttributes(attributes, prefix = "agentmark.") {
1655
1681
  if (get("dataset_expected_output")) result.datasetExpectedOutput = String(get("dataset_expected_output"));
1656
1682
  if (get("dataset_input")) result.datasetInput = String(get("dataset_input"));
1657
1683
  if (get("dataset_path")) result.datasetPath = String(get("dataset_path"));
1684
+ if (get("experiment_key")) result.experimentKey = String(get("experiment_key"));
1685
+ if (get("source_tree_hash")) result.sourceTreeHash = String(get("source_tree_hash"));
1658
1686
  return result;
1659
1687
  }
1660
1688
 
@@ -2169,13 +2197,20 @@ var FRAMEWORK_MAPPINGS = [
2169
2197
  {
2170
2198
  key: "ai.operationId",
2171
2199
  // Vercel AI SDK
2200
+ // The AI SDK emits ai.operationId WITH the "ai." prefix (e.g.
2201
+ // "ai.generateText"); accept both prefixed and unprefixed so generation
2202
+ // wrappers resolve to "llm" instead of falling through to "function".
2172
2203
  map: {
2173
2204
  "embed": "embedding",
2174
2205
  "ai.embed": "embedding",
2175
2206
  "generateText": "llm",
2207
+ "ai.generateText": "llm",
2176
2208
  "streamText": "llm",
2209
+ "ai.streamText": "llm",
2177
2210
  "generateObject": "llm",
2178
- "streamObject": "llm"
2211
+ "ai.generateObject": "llm",
2212
+ "streamObject": "llm",
2213
+ "ai.streamObject": "llm"
2179
2214
  }
2180
2215
  },
2181
2216
  {
@@ -2219,6 +2254,9 @@ function resolveSemanticKind(normalized, allAttributes) {
2219
2254
  if (normalized.type === "GENERATION" /* GENERATION */) {
2220
2255
  return "llm";
2221
2256
  }
2257
+ if (normalized.model) {
2258
+ return "llm";
2259
+ }
2222
2260
  if (normalized.toolCalls && normalized.toolCalls.length > 0) {
2223
2261
  return "tool";
2224
2262
  }