@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.d.mts +48 -3
- package/dist/index.d.ts +48 -3
- package/dist/index.js +621 -4
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +618 -4
- package/dist/index.mjs.map +1 -1
- package/package.json +3 -2
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
|
-
|
|
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.
|
|
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
|