@agentmark-ai/shared-utils 0.3.0 → 0.3.2

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
@@ -298,7 +298,326 @@ ${typeMapping.join(",\n")}
298
298
  var isNewFormat = (frontmatter) => {
299
299
  return frontmatter["text_config"] || frontmatter["object_config"] || frontmatter["image_config"];
300
300
  };
301
- async function generateTypeDefinitions(prompts) {
301
+ function pythonPrimitiveType(schemaType) {
302
+ const map = {
303
+ string: "str",
304
+ number: "float",
305
+ integer: "int",
306
+ boolean: "bool",
307
+ null: "None"
308
+ };
309
+ return map[schemaType] || "Any";
310
+ }
311
+ function propToPascal(prop) {
312
+ return prop.split(/[-_]/).map((w) => w.charAt(0).toUpperCase() + w.slice(1)).join("");
313
+ }
314
+ function schemaToPythonType(schema, parentClass, propKey, collected) {
315
+ if (!schema) return "Any";
316
+ const union = schema.anyOf || schema.oneOf;
317
+ if (union) {
318
+ const nullIdx = union.findIndex((v) => v.type === "null");
319
+ const hasNull = nullIdx >= 0;
320
+ const others = union.filter((_, i) => i !== nullIdx);
321
+ if (hasNull && others.length === 1) {
322
+ return `${schemaToPythonType(others[0], parentClass, propKey, collected)} | None`;
323
+ }
324
+ const parts = others.map(
325
+ (v, i) => schemaToPythonType(v, parentClass, `${propKey}${i}`, collected)
326
+ );
327
+ if (hasNull) parts.push("None");
328
+ return parts.join(" | ");
329
+ }
330
+ if (Array.isArray(schema.type)) {
331
+ const hasNull = schema.type.includes("null");
332
+ const nonNull = schema.type.filter((t) => t !== "null");
333
+ if (nonNull.length === 1 && nonNull[0] === "object" && schema.properties) {
334
+ const clsName = `${parentClass}${propToPascal(propKey)}`;
335
+ collected.push(buildTypedDict(clsName, schema, collected));
336
+ return hasNull ? `${clsName} | None` : clsName;
337
+ }
338
+ if (nonNull.length === 1 && nonNull[0] === "array" && schema.items) {
339
+ const item = schemaToPythonType(
340
+ schema.items,
341
+ parentClass,
342
+ propKey,
343
+ collected
344
+ );
345
+ return hasNull ? `list[${item}] | None` : `list[${item}]`;
346
+ }
347
+ const base = nonNull.length === 1 ? pythonPrimitiveType(nonNull[0]) : nonNull.map(pythonPrimitiveType).join(" | ");
348
+ return hasNull ? `${base} | None` : base;
349
+ }
350
+ switch (schema.type) {
351
+ case "string":
352
+ case "number":
353
+ case "integer":
354
+ case "boolean":
355
+ case "null":
356
+ return pythonPrimitiveType(schema.type);
357
+ case "array": {
358
+ const item = schema.items ? schemaToPythonType(schema.items, parentClass, propKey, collected) : "Any";
359
+ return `list[${item}]`;
360
+ }
361
+ case "object": {
362
+ if (!schema.properties || Object.keys(schema.properties).length === 0) {
363
+ return "dict[str, Any]";
364
+ }
365
+ const clsName = `${parentClass}${propToPascal(propKey)}`;
366
+ collected.push(buildTypedDict(clsName, schema, collected));
367
+ return clsName;
368
+ }
369
+ default:
370
+ if (schema.properties) {
371
+ const clsName = `${parentClass}${propToPascal(propKey)}`;
372
+ collected.push(buildTypedDict(clsName, schema, collected));
373
+ return clsName;
374
+ }
375
+ return "Any";
376
+ }
377
+ }
378
+ function buildTypedDict(className, schema, collected) {
379
+ const props = schema.properties || {};
380
+ const requiredSet = new Set(schema.required || []);
381
+ const lines = [];
382
+ const desc = schema.description;
383
+ if (desc) {
384
+ lines.push(`class ${className}(TypedDict):`);
385
+ lines.push(` """${desc}"""`);
386
+ } else {
387
+ lines.push(`class ${className}(TypedDict):`);
388
+ }
389
+ const entries = Object.entries(props);
390
+ if (entries.length === 0) {
391
+ lines.push(" pass");
392
+ return lines.join("\n");
393
+ }
394
+ for (const [key, propSchema] of entries) {
395
+ const pyType = schemaToPythonType(
396
+ propSchema,
397
+ className,
398
+ key,
399
+ collected
400
+ );
401
+ const isRequired = requiredSet.has(key);
402
+ const annotation = isRequired ? pyType : `NotRequired[${pyType}]`;
403
+ const propDesc = propSchema.description;
404
+ if (propDesc) {
405
+ const prefix = ` ${key}: ${annotation} # `;
406
+ const maxDesc = 99 - prefix.length;
407
+ if (maxDesc > 10) {
408
+ const shortDesc = propDesc.length > maxDesc ? propDesc.substring(0, maxDesc - 3) + "..." : propDesc;
409
+ lines.push(`${prefix}${shortDesc}`);
410
+ } else {
411
+ lines.push(` ${key}: ${annotation}`);
412
+ }
413
+ } else {
414
+ lines.push(` ${key}: ${annotation}`);
415
+ }
416
+ }
417
+ return lines.join("\n");
418
+ }
419
+ function generatePythonToolTypes(tools) {
420
+ if (Object.keys(tools).length === 0) return null;
421
+ const collected = [];
422
+ for (const [toolName, schema] of Object.entries(tools)) {
423
+ const argsName = `${getToolInterfaceName(toolName)}Args`;
424
+ if (schema.parameters && schema.parameters.properties) {
425
+ collected.push(buildTypedDict(argsName, schema.parameters, collected));
426
+ } else {
427
+ collected.push(`class ${argsName}(TypedDict):
428
+ pass`);
429
+ }
430
+ }
431
+ const toolLines = Object.keys(tools).map((name) => ` ${name}: ${getToolInterfaceName(name)}Args`).join("\n");
432
+ collected.push(`class Tools(TypedDict):
433
+ ${toolLines}`);
434
+ return collected.join("\n\n\n");
435
+ }
436
+ function buildPythonHeader(body) {
437
+ const imports = ["TypedDict"];
438
+ if (body.includes("NotRequired[")) imports.push("NotRequired");
439
+ if (body.includes("Literal[")) imports.push("Literal");
440
+ if (/\bAny\b/.test(body)) imports.push("Any");
441
+ imports.sort();
442
+ return `# Auto-generated types from AgentMark
443
+ # Do not edit this file directly
444
+
445
+ from __future__ import annotations
446
+
447
+ from typing import ${imports.join(", ")}
448
+
449
+
450
+ `;
451
+ }
452
+ async function generatePythonTypeDefinitionsV1_0(prompts) {
453
+ const allClasses = [];
454
+ const typeMapping = [];
455
+ for (const prompt of prompts) {
456
+ const { path: promptPath, input_schema } = prompt;
457
+ const name = getInterfaceName(promptPath);
458
+ try {
459
+ let kind = "text";
460
+ let outputSchema = null;
461
+ let tools = {};
462
+ if ("text_config" in prompt) {
463
+ kind = "text";
464
+ tools = prompt.text_config.tools || {};
465
+ } else if ("object_config" in prompt) {
466
+ kind = "object";
467
+ tools = prompt.object_config.tools || {};
468
+ outputSchema = prompt.object_config.schema;
469
+ } else if ("image_config" in prompt) {
470
+ kind = "image";
471
+ tools = prompt.image_config.tools || {};
472
+ }
473
+ const inputCollected = [];
474
+ if (input_schema && input_schema.properties) {
475
+ inputCollected.push(
476
+ buildTypedDict(`${name}In`, input_schema, inputCollected)
477
+ );
478
+ } else {
479
+ inputCollected.push(`class ${name}In(TypedDict):
480
+ pass`);
481
+ }
482
+ allClasses.push(...inputCollected);
483
+ const outputCollected = [];
484
+ if (outputSchema && outputSchema.properties) {
485
+ outputCollected.push(
486
+ buildTypedDict(`${name}Out`, outputSchema, outputCollected)
487
+ );
488
+ } else if (kind === "object") {
489
+ outputCollected.push(`class ${name}Out(TypedDict):
490
+ pass`);
491
+ } else {
492
+ outputCollected.push(`${name}Out = str`);
493
+ }
494
+ allClasses.push(...outputCollected);
495
+ if (Object.keys(tools).length > 0) {
496
+ const toolOutput = generatePythonToolTypes(tools);
497
+ if (toolOutput) allClasses.push(toolOutput);
498
+ }
499
+ const toolsLine = Object.keys(tools).length > 0 ? "\n tools: NotRequired[list[str]]" : "";
500
+ allClasses.push(
501
+ `class ${name}(TypedDict):
502
+ kind: Literal['${kind}']
503
+ input: ${name}In
504
+ output: ${name}Out${toolsLine}`
505
+ );
506
+ typeMapping.push({ path: promptPath, name });
507
+ const withoutMdx = promptPath.replace(/\.mdx$/, "");
508
+ if (withoutMdx !== promptPath)
509
+ typeMapping.push({ path: withoutMdx, name });
510
+ const shortName = promptPath.replace(/\.prompt\.mdx$/, "");
511
+ if (shortName !== promptPath && shortName !== withoutMdx) {
512
+ typeMapping.push({ path: shortName, name });
513
+ }
514
+ } catch (error) {
515
+ console.error(`Error processing ${promptPath}:`, error);
516
+ allClasses.push(`class ${name}In(TypedDict):
517
+ pass`);
518
+ allClasses.push(`${name}Out = str`);
519
+ allClasses.push(
520
+ `class ${name}(TypedDict):
521
+ kind: Literal['text']
522
+ input: ${name}In
523
+ output: ${name}Out`
524
+ );
525
+ typeMapping.push({ path: promptPath, name });
526
+ const withoutMdx = promptPath.replace(/\.mdx$/, "");
527
+ if (withoutMdx !== promptPath)
528
+ typeMapping.push({ path: withoutMdx, name });
529
+ const shortName = promptPath.replace(/\.prompt\.mdx$/, "");
530
+ if (shortName !== promptPath && shortName !== withoutMdx) {
531
+ typeMapping.push({ path: shortName, name });
532
+ }
533
+ }
534
+ }
535
+ const mappingLines = typeMapping.map(({ path: path2, name }) => ` '${path2}': ${name}`).join(",\n");
536
+ allClasses.push(
537
+ `AgentmarkTypes = TypedDict('AgentmarkTypes', {
538
+ ${mappingLines},
539
+ })`
540
+ );
541
+ const body = allClasses.join("\n\n\n") + "\n";
542
+ return buildPythonHeader(body) + body;
543
+ }
544
+ async function generatePythonTypeDefinitionsV0(prompts) {
545
+ var _a, _b;
546
+ const allClasses = [];
547
+ const typeMapping = [];
548
+ for (const prompt of prompts) {
549
+ const { path: promptPath, metadata, input_schema } = prompt;
550
+ const name = getInterfaceName(promptPath);
551
+ try {
552
+ const inputCollected = [];
553
+ if (input_schema && input_schema.properties) {
554
+ inputCollected.push(
555
+ buildTypedDict(`${name}In`, input_schema, inputCollected)
556
+ );
557
+ } else {
558
+ inputCollected.push(`class ${name}In(TypedDict):
559
+ pass`);
560
+ }
561
+ allClasses.push(...inputCollected);
562
+ const outputSchema = (_b = (_a = metadata == null ? void 0 : metadata.model) == null ? void 0 : _a.settings) == null ? void 0 : _b.schema;
563
+ const outputCollected = [];
564
+ if (outputSchema && outputSchema.properties) {
565
+ outputCollected.push(
566
+ buildTypedDict(`${name}Out`, outputSchema, outputCollected)
567
+ );
568
+ } else {
569
+ outputCollected.push(`${name}Out = str`);
570
+ }
571
+ allClasses.push(...outputCollected);
572
+ allClasses.push(
573
+ `class ${name}(TypedDict):
574
+ input: ${name}In
575
+ output: ${name}Out`
576
+ );
577
+ typeMapping.push({ path: promptPath, name });
578
+ const withoutMdx = promptPath.replace(/\.mdx$/, "");
579
+ if (withoutMdx !== promptPath)
580
+ typeMapping.push({ path: withoutMdx, name });
581
+ const shortName = promptPath.replace(/\.prompt\.mdx$/, "");
582
+ if (shortName !== promptPath && shortName !== withoutMdx) {
583
+ typeMapping.push({ path: shortName, name });
584
+ }
585
+ } catch (error) {
586
+ console.error(`Error processing ${promptPath}:`, error);
587
+ allClasses.push(`class ${name}In(TypedDict):
588
+ pass`);
589
+ allClasses.push(`${name}Out = str`);
590
+ allClasses.push(
591
+ `class ${name}(TypedDict):
592
+ input: ${name}In
593
+ output: ${name}Out`
594
+ );
595
+ typeMapping.push({ path: promptPath, name });
596
+ const withoutMdx = promptPath.replace(/\.mdx$/, "");
597
+ if (withoutMdx !== promptPath)
598
+ typeMapping.push({ path: withoutMdx, name });
599
+ const shortName = promptPath.replace(/\.prompt\.mdx$/, "");
600
+ if (shortName !== promptPath && shortName !== withoutMdx) {
601
+ typeMapping.push({ path: shortName, name });
602
+ }
603
+ }
604
+ }
605
+ const mappingLines = typeMapping.map(({ path: path2, name }) => ` '${path2}': ${name}`).join(",\n");
606
+ allClasses.push(
607
+ `AgentmarkTypes = TypedDict('AgentmarkTypes', {
608
+ ${mappingLines},
609
+ })`
610
+ );
611
+ const body = allClasses.join("\n\n\n") + "\n";
612
+ return buildPythonHeader(body) + body;
613
+ }
614
+ async function generateTypeDefinitions(prompts, language = "typescript") {
615
+ if (language === "python") {
616
+ if (prompts[0].version === "1.0") {
617
+ return generatePythonTypeDefinitionsV1_0(prompts);
618
+ }
619
+ return generatePythonTypeDefinitionsV0(prompts);
620
+ }
302
621
  if (prompts[0].version === "1.0") {
303
622
  return generateTypeDefinitionsV1_0(prompts);
304
623
  }
@@ -505,9 +824,9 @@ var KNOWN_METADATA_FIELDS = /* @__PURE__ */ new Set([
505
824
  "dataset_path",
506
825
  "dataset_item_name",
507
826
  "dataset_expected_output",
827
+ "dataset_input",
508
828
  "prompt_name",
509
- "props",
510
- "commit_sha"
829
+ "props"
511
830
  ]);
512
831
  function parseMetadata(attributes, prefix = "agentmark.metadata.") {
513
832
  const result = {};
@@ -1292,10 +1611,12 @@ function parseAgentMarkAttributes(attributes, prefix = "agentmark.") {
1292
1611
  if (get("trace_name")) result.traceName = String(get("trace_name"));
1293
1612
  if (get("prompt_name")) result.promptName = String(get("prompt_name"));
1294
1613
  if (get("props")) result.props = String(get("props"));
1614
+ if (get("span.kind")) result.semanticKind = String(get("span.kind"));
1295
1615
  if (get("dataset_run_id")) result.datasetRunId = String(get("dataset_run_id"));
1296
1616
  if (get("dataset_run_name")) result.datasetRunName = String(get("dataset_run_name"));
1297
1617
  if (get("dataset_item_name")) result.datasetItemName = String(get("dataset_item_name"));
1298
1618
  if (get("dataset_expected_output")) result.datasetExpectedOutput = String(get("dataset_expected_output"));
1619
+ if (get("dataset_input")) result.datasetInput = String(get("dataset_input"));
1299
1620
  if (get("dataset_path")) result.datasetPath = String(get("dataset_path"));
1300
1621
  return result;
1301
1622
  }
@@ -1360,6 +1681,9 @@ var AgentMarkTransformer = class {
1360
1681
  if (operationName === OperationNames.CHAT || operationName === OperationNames.TEXT_COMPLETION) {
1361
1682
  return "GENERATION" /* GENERATION */;
1362
1683
  }
1684
+ if (operationName === OperationNames.EMBEDDINGS) {
1685
+ return "GENERATION" /* GENERATION */;
1686
+ }
1363
1687
  if (span.name === SpanNames.CHAT || span.name.startsWith(SpanNames.CHAT + " ")) {
1364
1688
  return "GENERATION" /* GENERATION */;
1365
1689
  }
@@ -1408,6 +1732,10 @@ var AgentMarkTransformer = class {
1408
1732
  if (result.inputTokens !== void 0 && result.outputTokens !== void 0) {
1409
1733
  result.totalTokens = result.inputTokens + result.outputTokens;
1410
1734
  }
1735
+ const costUsd = attributes["agentmark.usage.cost_usd"];
1736
+ if (typeof costUsd === "number" && costUsd > 0) {
1737
+ result.cost = costUsd;
1738
+ }
1411
1739
  const finishReasons = attributes[GenAIAttributes.RESPONSE_FINISH_REASONS];
1412
1740
  if (finishReasons) {
1413
1741
  try {
@@ -1453,6 +1781,22 @@ var AgentMarkTransformer = class {
1453
1781
  const responseOutput = attributes[GenAIAttributes.RESPONSE_OUTPUT];
1454
1782
  if (responseOutput && typeof responseOutput === "string") {
1455
1783
  result.output = responseOutput;
1784
+ try {
1785
+ result.outputObject = JSON.parse(responseOutput);
1786
+ } catch {
1787
+ }
1788
+ }
1789
+ const amInput = attributes["agentmark.input"];
1790
+ if (amInput && typeof amInput === "string" && !result.input) {
1791
+ result.input = [{ role: "user", content: amInput }];
1792
+ }
1793
+ const amOutput = attributes["agentmark.output"];
1794
+ if (amOutput && typeof amOutput === "string" && !result.output) {
1795
+ result.output = amOutput;
1796
+ try {
1797
+ result.outputObject = JSON.parse(amOutput);
1798
+ } catch {
1799
+ }
1456
1800
  }
1457
1801
  const toolName = attributes[GenAIAttributes.TOOL_NAME];
1458
1802
  const toolCallId = attributes[GenAIAttributes.TOOL_CALL_ID];
@@ -1494,6 +1838,194 @@ var AgentMarkTransformer = class {
1494
1838
  };
1495
1839
  var AGENTMARK_SCOPE_NAME = "agentmark";
1496
1840
 
1841
+ // src/normalizer/transformers/otel-genai/index.ts
1842
+ var Attrs = {
1843
+ // Standard OTel GenAI attributes
1844
+ SYSTEM: "gen_ai.system",
1845
+ PROVIDER_NAME: "gen_ai.provider.name",
1846
+ OPERATION_NAME: "gen_ai.operation.name",
1847
+ REQUEST_MODEL: "gen_ai.request.model",
1848
+ REQUEST_MAX_TOKENS: "gen_ai.request.max_tokens",
1849
+ REQUEST_TEMPERATURE: "gen_ai.request.temperature",
1850
+ RESPONSE_ID: "gen_ai.response.id",
1851
+ RESPONSE_MODEL: "gen_ai.response.model",
1852
+ RESPONSE_FINISH_REASONS: "gen_ai.response.finish_reasons",
1853
+ USAGE_INPUT_TOKENS: "gen_ai.usage.input_tokens",
1854
+ USAGE_OUTPUT_TOKENS: "gen_ai.usage.output_tokens",
1855
+ // v1.37.0+ content attributes
1856
+ INPUT_MESSAGES: "gen_ai.input.messages",
1857
+ OUTPUT_MESSAGES: "gen_ai.output.messages",
1858
+ SYSTEM_INSTRUCTIONS: "gen_ai.system_instructions",
1859
+ TOOL_DEFINITIONS: "gen_ai.tool.definitions",
1860
+ // Tool call attributes (v1.37.0+)
1861
+ TOOL_NAME: "gen_ai.tool.name",
1862
+ TOOL_CALL_ID: "gen_ai.tool.call.id",
1863
+ TOOL_CALL_ARGS: "gen_ai.tool.call.arguments",
1864
+ TOOL_CALL_RESULT: "gen_ai.tool.call.result"
1865
+ };
1866
+ function partsToText(parts) {
1867
+ return parts.filter((p) => p.type === "text" && p.content).map((p) => p.content).join("\n");
1868
+ }
1869
+ function normalizeMessages(raw) {
1870
+ try {
1871
+ const parsed = JSON.parse(raw);
1872
+ if (!Array.isArray(parsed) || parsed.length === 0) return null;
1873
+ const messages = [];
1874
+ for (const msg of parsed) {
1875
+ if (!msg.role) continue;
1876
+ if (msg.content && typeof msg.content === "string") {
1877
+ messages.push(msg);
1878
+ } else if (msg.parts && Array.isArray(msg.parts)) {
1879
+ const text = partsToText(msg.parts);
1880
+ if (text) {
1881
+ messages.push({ role: msg.role, content: text });
1882
+ }
1883
+ }
1884
+ }
1885
+ return messages.length > 0 ? messages : null;
1886
+ } catch {
1887
+ return null;
1888
+ }
1889
+ }
1890
+ function extractStructuredOutput(raw) {
1891
+ try {
1892
+ const parsed = JSON.parse(raw);
1893
+ if (!Array.isArray(parsed)) return null;
1894
+ for (const msg of parsed) {
1895
+ for (const part of msg.parts || []) {
1896
+ if (part.type === "tool_call" && part.arguments) {
1897
+ return {
1898
+ output: JSON.stringify(part.arguments),
1899
+ outputObject: part.arguments
1900
+ };
1901
+ }
1902
+ }
1903
+ }
1904
+ for (const msg of parsed) {
1905
+ const text = partsToText(msg.parts || []);
1906
+ if (text) return { output: text };
1907
+ }
1908
+ return null;
1909
+ } catch {
1910
+ return null;
1911
+ }
1912
+ }
1913
+ var OtelGenAiTransformer = class {
1914
+ classify(span, attributes) {
1915
+ if (span.name.startsWith("chat ")) return "GENERATION" /* GENERATION */;
1916
+ if (span.name.startsWith("invoke_agent") || span.name === "agent run") return "SPAN" /* SPAN */;
1917
+ if (span.name.startsWith("execute_tool") || span.name.startsWith("running ")) return "SPAN" /* SPAN */;
1918
+ if (attributes[Attrs.USAGE_INPUT_TOKENS] !== void 0) return "GENERATION" /* GENERATION */;
1919
+ return "SPAN" /* SPAN */;
1920
+ }
1921
+ transform(span, attributes) {
1922
+ const result = {};
1923
+ const model = attributes[Attrs.RESPONSE_MODEL] || attributes[Attrs.REQUEST_MODEL];
1924
+ if (model && typeof model === "string") {
1925
+ result.model = model;
1926
+ }
1927
+ const inputTokens = attributes[Attrs.USAGE_INPUT_TOKENS];
1928
+ const outputTokens = attributes[Attrs.USAGE_OUTPUT_TOKENS];
1929
+ if (typeof inputTokens === "number") result.inputTokens = inputTokens;
1930
+ if (typeof outputTokens === "number") result.outputTokens = outputTokens;
1931
+ if (result.inputTokens !== void 0 && result.outputTokens !== void 0) {
1932
+ result.totalTokens = result.inputTokens + result.outputTokens;
1933
+ }
1934
+ const finishReasons = attributes[Attrs.RESPONSE_FINISH_REASONS];
1935
+ if (Array.isArray(finishReasons) && finishReasons.length > 0) {
1936
+ result.finishReason = finishReasons[0];
1937
+ }
1938
+ const temperature = attributes[Attrs.REQUEST_TEMPERATURE];
1939
+ if (typeof temperature === "number") {
1940
+ result.settings = { ...result.settings, temperature };
1941
+ }
1942
+ const inputMessages = attributes[Attrs.INPUT_MESSAGES];
1943
+ if (inputMessages && typeof inputMessages === "string") {
1944
+ const messages = normalizeMessages(inputMessages);
1945
+ if (messages) result.input = messages;
1946
+ }
1947
+ const outputMessages = attributes[Attrs.OUTPUT_MESSAGES];
1948
+ if (outputMessages && typeof outputMessages === "string") {
1949
+ const extracted = extractStructuredOutput(outputMessages);
1950
+ if (extracted) {
1951
+ result.output = extracted.output;
1952
+ if (extracted.outputObject) {
1953
+ result.outputObject = extracted.outputObject;
1954
+ }
1955
+ }
1956
+ }
1957
+ const allMessages = attributes["pydantic_ai.all_messages"];
1958
+ if (allMessages && typeof allMessages === "string" && !result.input) {
1959
+ const messages = normalizeMessages(allMessages);
1960
+ if (messages) {
1961
+ const userMessages = messages.filter((m) => m.role === "user");
1962
+ if (userMessages.length > 0) {
1963
+ result.input = userMessages;
1964
+ }
1965
+ }
1966
+ }
1967
+ const finalResult = attributes["final_result"];
1968
+ if (finalResult && typeof finalResult === "string" && !result.output) {
1969
+ result.output = finalResult;
1970
+ try {
1971
+ result.outputObject = JSON.parse(finalResult);
1972
+ } catch {
1973
+ }
1974
+ }
1975
+ const propsStr = attributes["agentmark.props"];
1976
+ if (propsStr && typeof propsStr === "string" && !result.input) {
1977
+ try {
1978
+ const props = JSON.parse(propsStr);
1979
+ result.input = [{ role: "user", content: JSON.stringify(props) }];
1980
+ } catch {
1981
+ }
1982
+ }
1983
+ const outputStr = attributes["agentmark.output"];
1984
+ if (outputStr && typeof outputStr === "string" && !result.output) {
1985
+ result.output = outputStr;
1986
+ try {
1987
+ result.outputObject = JSON.parse(outputStr);
1988
+ } catch {
1989
+ }
1990
+ }
1991
+ const toolName = attributes[Attrs.TOOL_NAME];
1992
+ if (toolName && typeof toolName === "string") {
1993
+ result.name = toolName;
1994
+ const toolCall = {
1995
+ type: "tool-call",
1996
+ toolCallId: attributes[Attrs.TOOL_CALL_ID] || "",
1997
+ toolName,
1998
+ args: {}
1999
+ };
2000
+ const toolArgs = attributes[Attrs.TOOL_CALL_ARGS];
2001
+ if (toolArgs && typeof toolArgs === "string") {
2002
+ try {
2003
+ toolCall.args = JSON.parse(toolArgs);
2004
+ } catch {
2005
+ toolCall.args = { raw: toolArgs };
2006
+ }
2007
+ }
2008
+ const toolResult = attributes[Attrs.TOOL_CALL_RESULT];
2009
+ if (toolResult && typeof toolResult === "string") {
2010
+ toolCall.result = toolResult;
2011
+ }
2012
+ result.toolCalls = [toolCall];
2013
+ }
2014
+ const agentmarkAttrs = parseAgentMarkAttributes(attributes);
2015
+ Object.assign(result, agentmarkAttrs);
2016
+ const parsedMeta = parseMetadata(attributes);
2017
+ if (parsedMeta.metadata && Object.keys(parsedMeta.metadata).length > 0) {
2018
+ result.metadata = { ...result.metadata, ...parsedMeta.metadata };
2019
+ }
2020
+ const customMeta = extractCustomMetadata(attributes);
2021
+ if (Object.keys(customMeta).length > 0) {
2022
+ result.metadata = { ...result.metadata, ...customMeta };
2023
+ }
2024
+ return result;
2025
+ }
2026
+ };
2027
+ OtelGenAiTransformer.SCOPE_NAME = "pydantic-ai";
2028
+
1497
2029
  // src/normalizer/converters/otlp-converter.ts
1498
2030
  function convertOtlpValue(value) {
1499
2031
  var _a;
@@ -1574,6 +2106,83 @@ function extractResourceScopeSpan(resourceSpans) {
1574
2106
  return result;
1575
2107
  }
1576
2108
 
2109
+ // src/normalizer/resolvers/semantic-kind-resolver.ts
2110
+ var SEMANTIC_KINDS = ["function", "llm", "tool", "agent", "retrieval", "embedding", "guardrail"];
2111
+ var VALID_KINDS = new Set(SEMANTIC_KINDS);
2112
+ var OPENINFERENCE_MAP = {
2113
+ "CHAIN": "function",
2114
+ "LLM": "llm",
2115
+ "TOOL": "tool",
2116
+ "AGENT": "agent",
2117
+ "RETRIEVER": "retrieval",
2118
+ "EMBEDDING": "embedding",
2119
+ "GUARDRAIL": "guardrail",
2120
+ "RERANKER": "retrieval"
2121
+ };
2122
+ var FRAMEWORK_MAPPINGS = [
2123
+ {
2124
+ key: "ai.operationId",
2125
+ // Vercel AI SDK
2126
+ map: {
2127
+ "embed": "embedding",
2128
+ "ai.embed": "embedding",
2129
+ "generateText": "llm",
2130
+ "streamText": "llm",
2131
+ "generateObject": "llm",
2132
+ "streamObject": "llm"
2133
+ }
2134
+ },
2135
+ {
2136
+ key: "traceloop.span.kind",
2137
+ // Traceloop / OpenLLMetry
2138
+ map: { "LLM": "llm", "TOOL": "tool", "AGENT": "agent", "WORKFLOW": "function", "TASK": "function" }
2139
+ },
2140
+ {
2141
+ key: "langchain.run_type",
2142
+ // LangChain via OTLP
2143
+ map: { "llm": "llm", "chat_model": "llm", "retriever": "retrieval", "tool": "tool", "chain": "function", "embedding": "embedding" }
2144
+ },
2145
+ {
2146
+ key: "genkit:type",
2147
+ // Firebase Genkit
2148
+ map: { "model": "llm", "tool": "tool", "flow": "function", "retriever": "retrieval", "embedder": "embedding" }
2149
+ }
2150
+ ];
2151
+ function resolveSemanticKind(normalized, allAttributes) {
2152
+ if (normalized.semanticKind && VALID_KINDS.has(normalized.semanticKind)) {
2153
+ return normalized.semanticKind;
2154
+ }
2155
+ const oiKind = allAttributes["openinference.span.kind"];
2156
+ if (oiKind) {
2157
+ const mapped = OPENINFERENCE_MAP[String(oiKind).toUpperCase()];
2158
+ if (mapped) return mapped;
2159
+ }
2160
+ for (const { key, map } of FRAMEWORK_MAPPINGS) {
2161
+ const val = allAttributes[key];
2162
+ if (val) {
2163
+ const mapped = map[String(val)];
2164
+ if (mapped) return mapped;
2165
+ }
2166
+ }
2167
+ const opName = allAttributes["gen_ai.operation.name"];
2168
+ if (opName) {
2169
+ const op = String(opName).toLowerCase();
2170
+ if (op === "chat" || op === "text_completion" || op === "generate_content") return "llm";
2171
+ if (op === "embeddings") return "embedding";
2172
+ }
2173
+ if (normalized.type === "GENERATION" /* GENERATION */) {
2174
+ return "llm";
2175
+ }
2176
+ if (normalized.toolCalls && normalized.toolCalls.length > 0) {
2177
+ return "tool";
2178
+ }
2179
+ const name = (normalized.name || "").toLowerCase();
2180
+ if (/retriev|search|rag/i.test(name)) return "retrieval";
2181
+ if (/embed/i.test(name)) return "embedding";
2182
+ if (/guard|safety/i.test(name)) return "guardrail";
2183
+ return "function";
2184
+ }
2185
+
1577
2186
  // src/normalizer/type-classifier.ts
1578
2187
  var TypeClassifier = class {
1579
2188
  classify(span, attributes) {
@@ -1595,7 +2204,8 @@ var typeClassifier = new TypeClassifier();
1595
2204
  registry.register("ai", new AiSdkTransformer());
1596
2205
  registry.register("default-tracer", new MastraTransformer());
1597
2206
  registry.register("agentmark", new AgentMarkTransformer());
1598
- registry.setDefault(new AiSdkTransformer());
2207
+ registry.register("pydantic-ai", new OtelGenAiTransformer());
2208
+ registry.setDefault(new OtelGenAiTransformer());
1599
2209
  function normalizeSpan(resource, scope, span) {
1600
2210
  var _a, _b, _c;
1601
2211
  const allAttributes = {
@@ -1651,6 +2261,7 @@ function normalizeSpan(resource, scope, span) {
1651
2261
  }
1652
2262
  const agentMarkAttributes = parseAgentMarkAttributes(allAttributes);
1653
2263
  Object.assign(normalized, agentMarkAttributes);
2264
+ normalized.semanticKind = resolveSemanticKind(normalized, allAttributes);
1654
2265
  return normalized;
1655
2266
  }
1656
2267
  function normalizeOtlpSpans(resourceSpans) {
@@ -1669,6 +2280,8 @@ export {
1669
2280
  AiSdkTransformer,
1670
2281
  AgentMarkTransformer as ClaudeAgentTransformer,
1671
2282
  MastraTransformer,
2283
+ OtelGenAiTransformer,
2284
+ SEMANTIC_KINDS,
1672
2285
  SpanType,
1673
2286
  TransformerRegistry,
1674
2287
  TypeClassifier,
@@ -1688,6 +2301,7 @@ export {
1688
2301
  parseMetadata,
1689
2302
  parseTokens,
1690
2303
  registry,
2304
+ resolveSemanticKind,
1691
2305
  toFrontMatter,
1692
2306
  typeClassifier,
1693
2307
  verifySignature