openlit 1.11.0 → 1.13.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/README.md +1 -1
- package/dist/config.d.ts +12 -4
- package/dist/config.js +7 -17
- package/dist/config.js.map +1 -1
- package/dist/evals/llm/anthropic.js +10 -6
- package/dist/evals/llm/anthropic.js.map +1 -1
- package/dist/evals/llm/openai.js +9 -5
- package/dist/evals/llm/openai.js.map +1 -1
- package/dist/features/vault.js +1 -1
- package/dist/features/vault.js.map +1 -1
- package/dist/helpers.d.ts +93 -1
- package/dist/helpers.js +271 -8
- package/dist/helpers.js.map +1 -1
- package/dist/index.d.ts +6 -5
- package/dist/index.js +95 -50
- package/dist/index.js.map +1 -1
- package/dist/instrumentation/__tests__/anthropic-wrapper.test.js +215 -27
- package/dist/instrumentation/__tests__/anthropic-wrapper.test.js.map +1 -1
- package/dist/instrumentation/__tests__/base-wrapper.test.js +19 -23
- package/dist/instrumentation/__tests__/base-wrapper.test.js.map +1 -1
- package/dist/instrumentation/__tests__/bedrock-trace-comparison.test.d.ts +1 -0
- package/dist/instrumentation/__tests__/bedrock-trace-comparison.test.js +422 -0
- package/dist/instrumentation/__tests__/bedrock-trace-comparison.test.js.map +1 -0
- package/dist/instrumentation/__tests__/chroma-trace-comparison.test.js +1 -1
- package/dist/instrumentation/__tests__/chroma-trace-comparison.test.js.map +1 -1
- package/dist/instrumentation/__tests__/cohere-wrapper.test.js +150 -25
- package/dist/instrumentation/__tests__/cohere-wrapper.test.js.map +1 -1
- package/dist/instrumentation/__tests__/google-ai-trace-comparison.test.js +152 -33
- package/dist/instrumentation/__tests__/google-ai-trace-comparison.test.js.map +1 -1
- package/dist/instrumentation/__tests__/groq-trace-comparison.test.js +391 -45
- package/dist/instrumentation/__tests__/groq-trace-comparison.test.js.map +1 -1
- package/dist/instrumentation/__tests__/huggingface-trace-comparison.test.d.ts +2 -2
- package/dist/instrumentation/__tests__/huggingface-trace-comparison.test.js +323 -31
- package/dist/instrumentation/__tests__/huggingface-trace-comparison.test.js.map +1 -1
- package/dist/instrumentation/__tests__/langchain-wrapper.test.d.ts +1 -0
- package/dist/instrumentation/__tests__/langchain-wrapper.test.js +282 -0
- package/dist/instrumentation/__tests__/langchain-wrapper.test.js.map +1 -0
- package/dist/instrumentation/__tests__/milvus-trace-comparison.test.js +1 -1
- package/dist/instrumentation/__tests__/milvus-trace-comparison.test.js.map +1 -1
- package/dist/instrumentation/__tests__/mistral-trace-comparison.test.d.ts +0 -3
- package/dist/instrumentation/__tests__/mistral-trace-comparison.test.js +275 -68
- package/dist/instrumentation/__tests__/mistral-trace-comparison.test.js.map +1 -1
- package/dist/instrumentation/__tests__/openai-wrapper.test.js +7 -9
- package/dist/instrumentation/__tests__/openai-wrapper.test.js.map +1 -1
- package/dist/instrumentation/__tests__/qdrant-trace-comparison.test.js +1 -1
- package/dist/instrumentation/__tests__/qdrant-trace-comparison.test.js.map +1 -1
- package/dist/instrumentation/__tests__/replicate-trace-comparison.test.d.ts +2 -1
- package/dist/instrumentation/__tests__/replicate-trace-comparison.test.js +209 -21
- package/dist/instrumentation/__tests__/replicate-trace-comparison.test.js.map +1 -1
- package/dist/instrumentation/__tests__/together-trace-comparison.test.js +231 -51
- package/dist/instrumentation/__tests__/together-trace-comparison.test.js.map +1 -1
- package/dist/instrumentation/__tests__/vercel-ai-trace-comparison.test.d.ts +8 -0
- package/dist/instrumentation/__tests__/vercel-ai-trace-comparison.test.js +446 -0
- package/dist/instrumentation/__tests__/vercel-ai-trace-comparison.test.js.map +1 -0
- package/dist/instrumentation/anthropic/index.d.ts +2 -3
- package/dist/instrumentation/anthropic/index.js.map +1 -1
- package/dist/instrumentation/anthropic/wrapper.d.ts +1 -3
- package/dist/instrumentation/anthropic/wrapper.js +211 -91
- package/dist/instrumentation/anthropic/wrapper.js.map +1 -1
- package/dist/instrumentation/azure-ai-inference/index.d.ts +11 -0
- package/dist/instrumentation/azure-ai-inference/index.js +76 -0
- package/dist/instrumentation/azure-ai-inference/index.js.map +1 -0
- package/dist/instrumentation/azure-ai-inference/wrapper.d.ts +42 -0
- package/dist/instrumentation/azure-ai-inference/wrapper.js +515 -0
- package/dist/instrumentation/azure-ai-inference/wrapper.js.map +1 -0
- package/dist/instrumentation/base-wrapper.d.ts +2 -1
- package/dist/instrumentation/base-wrapper.js +35 -23
- package/dist/instrumentation/base-wrapper.js.map +1 -1
- package/dist/instrumentation/bedrock/wrapper.d.ts +21 -3
- package/dist/instrumentation/bedrock/wrapper.js +318 -265
- package/dist/instrumentation/bedrock/wrapper.js.map +1 -1
- package/dist/instrumentation/chroma/wrapper.js +1 -1
- package/dist/instrumentation/chroma/wrapper.js.map +1 -1
- package/dist/instrumentation/claude-agent-sdk/index.d.ts +23 -0
- package/dist/instrumentation/claude-agent-sdk/index.js +83 -0
- package/dist/instrumentation/claude-agent-sdk/index.js.map +1 -0
- package/dist/instrumentation/claude-agent-sdk/wrapper.d.ts +13 -0
- package/dist/instrumentation/claude-agent-sdk/wrapper.js +1031 -0
- package/dist/instrumentation/claude-agent-sdk/wrapper.js.map +1 -0
- package/dist/instrumentation/cohere/index.d.ts +2 -3
- package/dist/instrumentation/cohere/index.js.map +1 -1
- package/dist/instrumentation/cohere/wrapper.d.ts +1 -1
- package/dist/instrumentation/cohere/wrapper.js +215 -56
- package/dist/instrumentation/cohere/wrapper.js.map +1 -1
- package/dist/instrumentation/cursor-sdk/index.d.ts +21 -0
- package/dist/instrumentation/cursor-sdk/index.js +58 -0
- package/dist/instrumentation/cursor-sdk/index.js.map +1 -0
- package/dist/instrumentation/cursor-sdk/wrapper.d.ts +17 -0
- package/dist/instrumentation/cursor-sdk/wrapper.js +689 -0
- package/dist/instrumentation/cursor-sdk/wrapper.js.map +1 -0
- package/dist/instrumentation/google-adk/index.d.ts +57 -0
- package/dist/instrumentation/google-adk/index.js +371 -0
- package/dist/instrumentation/google-adk/index.js.map +1 -0
- package/dist/instrumentation/google-adk/utils.d.ts +45 -0
- package/dist/instrumentation/google-adk/utils.js +663 -0
- package/dist/instrumentation/google-adk/utils.js.map +1 -0
- package/dist/instrumentation/google-adk/wrapper.d.ts +11 -0
- package/dist/instrumentation/google-adk/wrapper.js +391 -0
- package/dist/instrumentation/google-adk/wrapper.js.map +1 -0
- package/dist/instrumentation/google-ai/wrapper.d.ts +7 -4
- package/dist/instrumentation/google-ai/wrapper.js +197 -61
- package/dist/instrumentation/google-ai/wrapper.js.map +1 -1
- package/dist/instrumentation/groq/wrapper.js +137 -65
- package/dist/instrumentation/groq/wrapper.js.map +1 -1
- package/dist/instrumentation/huggingface/wrapper.js +241 -39
- package/dist/instrumentation/huggingface/wrapper.js.map +1 -1
- package/dist/instrumentation/index.d.ts +2 -2
- package/dist/instrumentation/index.js +66 -6
- package/dist/instrumentation/index.js.map +1 -1
- package/dist/instrumentation/langchain/index.d.ts +0 -7
- package/dist/instrumentation/langchain/index.js +2 -20
- package/dist/instrumentation/langchain/index.js.map +1 -1
- package/dist/instrumentation/langchain/wrapper.d.ts +35 -0
- package/dist/instrumentation/langchain/wrapper.js +1098 -184
- package/dist/instrumentation/langchain/wrapper.js.map +1 -1
- package/dist/instrumentation/langgraph/index.d.ts +12 -0
- package/dist/instrumentation/langgraph/index.js +99 -0
- package/dist/instrumentation/langgraph/index.js.map +1 -0
- package/dist/instrumentation/langgraph/wrapper.d.ts +20 -0
- package/dist/instrumentation/langgraph/wrapper.js +619 -0
- package/dist/instrumentation/langgraph/wrapper.js.map +1 -0
- package/dist/instrumentation/llamaindex/index.d.ts +31 -6
- package/dist/instrumentation/llamaindex/index.js +180 -61
- package/dist/instrumentation/llamaindex/index.js.map +1 -1
- package/dist/instrumentation/llamaindex/wrapper.d.ts +15 -3
- package/dist/instrumentation/llamaindex/wrapper.js +670 -179
- package/dist/instrumentation/llamaindex/wrapper.js.map +1 -1
- package/dist/instrumentation/milvus/wrapper.js +1 -1
- package/dist/instrumentation/milvus/wrapper.js.map +1 -1
- package/dist/instrumentation/mistral/wrapper.js +154 -79
- package/dist/instrumentation/mistral/wrapper.js.map +1 -1
- package/dist/instrumentation/ollama/index.js +33 -4
- package/dist/instrumentation/ollama/index.js.map +1 -1
- package/dist/instrumentation/ollama/wrapper.d.ts +28 -2
- package/dist/instrumentation/ollama/wrapper.js +432 -48
- package/dist/instrumentation/ollama/wrapper.js.map +1 -1
- package/dist/instrumentation/openai/index.d.ts +2 -3
- package/dist/instrumentation/openai/index.js.map +1 -1
- package/dist/instrumentation/openai/wrapper.js +293 -194
- package/dist/instrumentation/openai/wrapper.js.map +1 -1
- package/dist/instrumentation/openai-agents/index.d.ts +20 -0
- package/dist/instrumentation/openai-agents/index.js +174 -0
- package/dist/instrumentation/openai-agents/index.js.map +1 -0
- package/dist/instrumentation/openai-agents/processor.d.ts +35 -0
- package/dist/instrumentation/openai-agents/processor.js +249 -0
- package/dist/instrumentation/openai-agents/processor.js.map +1 -0
- package/dist/instrumentation/openai-agents/utils.d.ts +20 -0
- package/dist/instrumentation/openai-agents/utils.js +624 -0
- package/dist/instrumentation/openai-agents/utils.js.map +1 -0
- package/dist/instrumentation/pinecone/wrapper.js +2 -2
- package/dist/instrumentation/pinecone/wrapper.js.map +1 -1
- package/dist/instrumentation/qdrant/wrapper.js +1 -1
- package/dist/instrumentation/qdrant/wrapper.js.map +1 -1
- package/dist/instrumentation/replicate/wrapper.js +103 -21
- package/dist/instrumentation/replicate/wrapper.js.map +1 -1
- package/dist/instrumentation/strands/index.d.ts +21 -0
- package/dist/instrumentation/strands/index.js +83 -0
- package/dist/instrumentation/strands/index.js.map +1 -0
- package/dist/instrumentation/strands/processor.d.ts +45 -0
- package/dist/instrumentation/strands/processor.js +545 -0
- package/dist/instrumentation/strands/processor.js.map +1 -0
- package/dist/instrumentation/strands/utils.d.ts +24 -0
- package/dist/instrumentation/strands/utils.js +360 -0
- package/dist/instrumentation/strands/utils.js.map +1 -0
- package/dist/instrumentation/together/wrapper.js +125 -51
- package/dist/instrumentation/together/wrapper.js.map +1 -1
- package/dist/instrumentation/vercel-ai/wrapper.d.ts +28 -2
- package/dist/instrumentation/vercel-ai/wrapper.js +314 -164
- package/dist/instrumentation/vercel-ai/wrapper.js.map +1 -1
- package/dist/llm/anthropic.js +10 -6
- package/dist/llm/anthropic.js.map +1 -1
- package/dist/llm/openai.js +9 -5
- package/dist/llm/openai.js.map +1 -1
- package/dist/otel/__tests__/metrics.test.js +16 -27
- package/dist/otel/__tests__/metrics.test.js.map +1 -1
- package/dist/otel/events.d.ts +11 -0
- package/dist/otel/events.js +74 -0
- package/dist/otel/events.js.map +1 -0
- package/dist/otel/metrics.d.ts +5 -6
- package/dist/otel/metrics.js +66 -48
- package/dist/otel/metrics.js.map +1 -1
- package/dist/otel/tracing.d.ts +6 -2
- package/dist/otel/tracing.js +71 -24
- package/dist/otel/tracing.js.map +1 -1
- package/dist/otel/utils.d.ts +11 -0
- package/dist/otel/utils.js +34 -0
- package/dist/otel/utils.js.map +1 -0
- package/dist/semantic-convention.d.ts +49 -5
- package/dist/semantic-convention.js +56 -8
- package/dist/semantic-convention.js.map +1 -1
- package/dist/types.d.ts +58 -22
- package/package.json +41 -9
|
@@ -1,108 +1,766 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
2
35
|
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
36
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
37
|
};
|
|
5
38
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
39
|
+
exports.runWithFrameworkLlm = exports.OpenLITCallbackHandler = void 0;
|
|
6
40
|
const api_1 = require("@opentelemetry/api");
|
|
41
|
+
const semantic_conventions_1 = require("@opentelemetry/semantic-conventions");
|
|
7
42
|
const config_1 = __importDefault(require("../../config"));
|
|
8
|
-
const helpers_1 =
|
|
43
|
+
const helpers_1 = __importStar(require("../../helpers"));
|
|
44
|
+
Object.defineProperty(exports, "runWithFrameworkLlm", { enumerable: true, get: function () { return helpers_1.runWithFrameworkLlm; } });
|
|
45
|
+
const constant_1 = require("../../constant");
|
|
9
46
|
const semantic_convention_1 = __importDefault(require("../../semantic-convention"));
|
|
10
47
|
const base_wrapper_1 = __importDefault(require("../base-wrapper"));
|
|
11
|
-
//
|
|
48
|
+
// ---------------------------------------------------------------------------
|
|
49
|
+
// Provider detection (mirrors Python PROVIDER_MAP)
|
|
50
|
+
// ---------------------------------------------------------------------------
|
|
51
|
+
const PROVIDER_MAP = {
|
|
52
|
+
anthropic: 'anthropic',
|
|
53
|
+
azure: 'azure',
|
|
54
|
+
bedrock: 'aws.bedrock',
|
|
55
|
+
bedrock_converse: 'aws.bedrock',
|
|
56
|
+
cohere: 'cohere',
|
|
57
|
+
google: 'google',
|
|
58
|
+
google_genai: 'google',
|
|
59
|
+
google_vertexai: 'google',
|
|
60
|
+
groq: 'groq',
|
|
61
|
+
mistralai: 'mistral_ai',
|
|
62
|
+
ollama: 'ollama',
|
|
63
|
+
openai: 'openai',
|
|
64
|
+
together: 'together',
|
|
65
|
+
vertexai: 'google',
|
|
66
|
+
fireworks: 'fireworks',
|
|
67
|
+
perplexity: 'perplexity',
|
|
68
|
+
huggingface: 'huggingface',
|
|
69
|
+
deepinfra: 'deepinfra',
|
|
70
|
+
anyscale: 'anyscale',
|
|
71
|
+
};
|
|
72
|
+
function detectProvider(serialized) {
|
|
73
|
+
if (!serialized)
|
|
74
|
+
return semantic_convention_1.default.GEN_AI_SYSTEM_LANGCHAIN;
|
|
75
|
+
const classId = serialized.id || [];
|
|
76
|
+
if (Array.isArray(classId)) {
|
|
77
|
+
const classPath = classId.join('.').toLowerCase();
|
|
78
|
+
for (const [key, val] of Object.entries(PROVIDER_MAP)) {
|
|
79
|
+
if (classPath.includes(key))
|
|
80
|
+
return val;
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
return semantic_convention_1.default.GEN_AI_SYSTEM_LANGCHAIN;
|
|
84
|
+
}
|
|
85
|
+
// ---------------------------------------------------------------------------
|
|
86
|
+
// Model name extraction (mirrors Python extract_model_name)
|
|
87
|
+
// ---------------------------------------------------------------------------
|
|
88
|
+
const MODEL_PATHS_BY_ID = [
|
|
89
|
+
['ChatGoogleGenerativeAI', ['kwargs', 'model'], 'serialized'],
|
|
90
|
+
['ChatVertexAI', ['kwargs', 'model_name'], 'serialized'],
|
|
91
|
+
['ChatMistralAI', ['kwargs', 'model'], 'serialized'],
|
|
92
|
+
['OpenAI', ['invocation_params', 'model_name'], 'kwargs'],
|
|
93
|
+
['ChatOpenAI', ['invocation_params', 'model_name'], 'kwargs'],
|
|
94
|
+
['AzureChatOpenAI', ['invocation_params', 'model'], 'kwargs'],
|
|
95
|
+
['AzureChatOpenAI', ['invocation_params', 'model_name'], 'kwargs'],
|
|
96
|
+
['AzureChatOpenAI', ['invocation_params', 'azure_deployment'], 'kwargs'],
|
|
97
|
+
['HuggingFacePipeline', ['invocation_params', 'model_id'], 'kwargs'],
|
|
98
|
+
['BedrockChat', ['kwargs', 'model_id'], 'serialized'],
|
|
99
|
+
['Bedrock', ['kwargs', 'model_id'], 'serialized'],
|
|
100
|
+
['BedrockLLM', ['kwargs', 'model_id'], 'serialized'],
|
|
101
|
+
['ChatBedrock', ['kwargs', 'model_id'], 'serialized'],
|
|
102
|
+
['ChatBedrockConverse', ['kwargs', 'model_id'], 'serialized'],
|
|
103
|
+
['LlamaCpp', ['invocation_params', 'model_path'], 'kwargs'],
|
|
104
|
+
['WatsonxLLM', ['invocation_params', 'model_id'], 'kwargs'],
|
|
105
|
+
];
|
|
106
|
+
const MODEL_PATTERNS = [
|
|
107
|
+
['ChatAnthropic', 'model', 'anthropic'],
|
|
108
|
+
['Anthropic', 'model', 'anthropic'],
|
|
109
|
+
['ChatTongyi', 'model_name', null],
|
|
110
|
+
['ChatCohere', 'model', null],
|
|
111
|
+
['Cohere', 'model', null],
|
|
112
|
+
['HuggingFaceHub', 'model', null],
|
|
113
|
+
['ChatAnyscale', 'model_name', null],
|
|
114
|
+
['TextGen', 'model', 'text-gen'],
|
|
115
|
+
['Ollama', 'model', null],
|
|
116
|
+
['OllamaLLM', 'model', null],
|
|
117
|
+
['ChatOllama', 'model', null],
|
|
118
|
+
['ChatFireworks', 'model', null],
|
|
119
|
+
['ChatPerplexity', 'model', null],
|
|
120
|
+
['VLLM', 'model', null],
|
|
121
|
+
['Xinference', 'model_uid', null],
|
|
122
|
+
['ChatOCIGenAI', 'model_id', null],
|
|
123
|
+
['DeepInfra', 'model_id', null],
|
|
124
|
+
];
|
|
125
|
+
const FALLBACK_PATHS = [
|
|
126
|
+
[['kwargs', 'model_name'], 'serialized'],
|
|
127
|
+
[['kwargs', 'model'], 'serialized'],
|
|
128
|
+
[['kwargs', 'model_id'], 'serialized'],
|
|
129
|
+
[['invocation_params', 'model_name'], 'kwargs'],
|
|
130
|
+
[['invocation_params', 'model'], 'kwargs'],
|
|
131
|
+
[['invocation_params', 'model_id'], 'kwargs'],
|
|
132
|
+
];
|
|
133
|
+
function _extractByPath(serialized, kwargs, keys, selectFrom) {
|
|
134
|
+
let obj = selectFrom === 'kwargs' ? kwargs : serialized;
|
|
135
|
+
if (obj == null)
|
|
136
|
+
return null;
|
|
137
|
+
for (const key of keys) {
|
|
138
|
+
if (typeof obj === 'object' && obj !== null) {
|
|
139
|
+
obj = obj[key];
|
|
140
|
+
}
|
|
141
|
+
else {
|
|
142
|
+
return null;
|
|
143
|
+
}
|
|
144
|
+
if (obj == null)
|
|
145
|
+
return null;
|
|
146
|
+
}
|
|
147
|
+
return obj ? String(obj) : null;
|
|
148
|
+
}
|
|
149
|
+
function _getClassName(serialized) {
|
|
150
|
+
if (!serialized)
|
|
151
|
+
return null;
|
|
152
|
+
const id = serialized.id;
|
|
153
|
+
if (Array.isArray(id) && id.length > 0)
|
|
154
|
+
return id[id.length - 1];
|
|
155
|
+
return null;
|
|
156
|
+
}
|
|
157
|
+
function _extractModelFromRepr(serialized, pattern) {
|
|
158
|
+
if (!serialized)
|
|
159
|
+
return null;
|
|
160
|
+
const repr = serialized.repr || '';
|
|
161
|
+
if (repr) {
|
|
162
|
+
const re = new RegExp(`${pattern}='(.*?)'`);
|
|
163
|
+
const match = re.exec(repr);
|
|
164
|
+
if (match)
|
|
165
|
+
return match[1];
|
|
166
|
+
}
|
|
167
|
+
return null;
|
|
168
|
+
}
|
|
169
|
+
function extractModelName(serialized, kwargs) {
|
|
170
|
+
const className = _getClassName(serialized);
|
|
171
|
+
for (const [modelId, keys, selectFrom] of MODEL_PATHS_BY_ID) {
|
|
172
|
+
if (className === modelId) {
|
|
173
|
+
const result = _extractByPath(serialized, kwargs, keys, selectFrom);
|
|
174
|
+
if (result)
|
|
175
|
+
return result;
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
for (const [modelId, pattern, defaultVal] of MODEL_PATTERNS) {
|
|
179
|
+
if (className === modelId) {
|
|
180
|
+
const result = _extractModelFromRepr(serialized, pattern);
|
|
181
|
+
if (result)
|
|
182
|
+
return result;
|
|
183
|
+
if (defaultVal)
|
|
184
|
+
return defaultVal;
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
for (const [keys, selectFrom] of FALLBACK_PATHS) {
|
|
188
|
+
const result = _extractByPath(serialized, kwargs, keys, selectFrom);
|
|
189
|
+
if (result)
|
|
190
|
+
return result;
|
|
191
|
+
}
|
|
192
|
+
return className || 'unknown';
|
|
193
|
+
}
|
|
194
|
+
function extractModelParameters(kwargs) {
|
|
195
|
+
const params = {};
|
|
196
|
+
const ip = kwargs?.invocation_params || {};
|
|
197
|
+
const paramKeys = [
|
|
198
|
+
'temperature', 'max_tokens', 'max_completion_tokens',
|
|
199
|
+
'top_p', 'top_k', 'frequency_penalty', 'presence_penalty',
|
|
200
|
+
'request_timeout', 'stop_sequences', 'seed',
|
|
201
|
+
];
|
|
202
|
+
for (const key of paramKeys) {
|
|
203
|
+
if (ip[key] != null)
|
|
204
|
+
params[key] = ip[key];
|
|
205
|
+
}
|
|
206
|
+
return params;
|
|
207
|
+
}
|
|
208
|
+
// ---------------------------------------------------------------------------
|
|
209
|
+
// Chain type detection (mirrors Python)
|
|
210
|
+
// ---------------------------------------------------------------------------
|
|
211
|
+
const SKIP_CHAIN_CLASS_PREFIXES = new Set([
|
|
212
|
+
'RunnableSequence', 'RunnableParallel', 'RunnableLambda',
|
|
213
|
+
'RunnablePassthrough', 'RunnableAssign', 'RunnablePick',
|
|
214
|
+
'RunnableBranch', 'RunnableEach', 'Prompt', 'PromptTemplate',
|
|
215
|
+
'ChatPromptTemplate', 'MessagesPlaceholder',
|
|
216
|
+
'SystemMessagePromptTemplate', 'HumanMessagePromptTemplate',
|
|
217
|
+
'AIMessagePromptTemplate', 'BasePromptTemplate',
|
|
218
|
+
'StrOutputParser', 'JsonOutputParser', 'PydanticOutputParser',
|
|
219
|
+
]);
|
|
220
|
+
function isInternalChain(serialized, name) {
|
|
221
|
+
if (serialized?.id) {
|
|
222
|
+
const classPath = serialized.id;
|
|
223
|
+
if (Array.isArray(classPath) && classPath.length > 0) {
|
|
224
|
+
const cn = String(classPath[classPath.length - 1]);
|
|
225
|
+
if (SKIP_CHAIN_CLASS_PREFIXES.has(cn))
|
|
226
|
+
return true;
|
|
227
|
+
if (cn.startsWith('Runnable'))
|
|
228
|
+
return true;
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
if (SKIP_CHAIN_CLASS_PREFIXES.has(name))
|
|
232
|
+
return true;
|
|
233
|
+
return false;
|
|
234
|
+
}
|
|
235
|
+
function detectObservationType(serialized, callbackType, name) {
|
|
236
|
+
if (callbackType === 'tool')
|
|
237
|
+
return 'tool';
|
|
238
|
+
if (callbackType === 'retriever')
|
|
239
|
+
return 'retriever';
|
|
240
|
+
if (callbackType === 'llm')
|
|
241
|
+
return 'generation';
|
|
242
|
+
if (callbackType === 'chain') {
|
|
243
|
+
if (serialized?.id) {
|
|
244
|
+
const classPath = serialized.id;
|
|
245
|
+
if (classPath.some((part) => String(part).toLowerCase().includes('agent'))) {
|
|
246
|
+
return 'agent';
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
if (name && name.toLowerCase().includes('agent'))
|
|
250
|
+
return 'agent';
|
|
251
|
+
return 'chain';
|
|
252
|
+
}
|
|
253
|
+
return 'span';
|
|
254
|
+
}
|
|
255
|
+
// ---------------------------------------------------------------------------
|
|
256
|
+
// Message formatting helpers (mirrors Python utils.py)
|
|
257
|
+
// ---------------------------------------------------------------------------
|
|
258
|
+
const ROLE_MAP = {
|
|
259
|
+
system: 'system',
|
|
260
|
+
human: 'user',
|
|
261
|
+
ai: 'assistant',
|
|
262
|
+
tool: 'tool',
|
|
263
|
+
function: 'tool',
|
|
264
|
+
};
|
|
265
|
+
function buildInputMessagesFromLangChain(messages) {
|
|
266
|
+
try {
|
|
267
|
+
const structured = [];
|
|
268
|
+
for (const msgList of messages) {
|
|
269
|
+
for (const msg of msgList) {
|
|
270
|
+
const role = msg._getType?.() || msg.type || 'user';
|
|
271
|
+
const content = msg.content ?? String(msg);
|
|
272
|
+
const otelRole = ROLE_MAP[role] || 'user';
|
|
273
|
+
structured.push({ role: otelRole, parts: buildParts(content) });
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
return structured;
|
|
277
|
+
}
|
|
278
|
+
catch {
|
|
279
|
+
return [];
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
function buildInputMessagesFromPrompts(prompts) {
|
|
283
|
+
try {
|
|
284
|
+
return prompts.map(p => ({
|
|
285
|
+
role: 'user',
|
|
286
|
+
parts: [{ type: 'text', content: typeof p === 'string' ? p : String(p) }],
|
|
287
|
+
}));
|
|
288
|
+
}
|
|
289
|
+
catch {
|
|
290
|
+
return [];
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
function buildInputMessages(messagesOrPrompts) {
|
|
294
|
+
if (!messagesOrPrompts)
|
|
295
|
+
return [];
|
|
296
|
+
if (Array.isArray(messagesOrPrompts) && messagesOrPrompts.length > 0) {
|
|
297
|
+
const first = messagesOrPrompts[0];
|
|
298
|
+
if (typeof first === 'string')
|
|
299
|
+
return buildInputMessagesFromPrompts(messagesOrPrompts);
|
|
300
|
+
if (Array.isArray(first) && first.length > 0 && first[0]?.content !== undefined) {
|
|
301
|
+
return buildInputMessagesFromLangChain(messagesOrPrompts);
|
|
302
|
+
}
|
|
303
|
+
return buildInputMessagesFromPrompts(messagesOrPrompts);
|
|
304
|
+
}
|
|
305
|
+
return [];
|
|
306
|
+
}
|
|
307
|
+
function buildParts(content) {
|
|
308
|
+
if (typeof content === 'string')
|
|
309
|
+
return [{ type: 'text', content }];
|
|
310
|
+
if (Array.isArray(content)) {
|
|
311
|
+
const parts = [];
|
|
312
|
+
for (const part of content) {
|
|
313
|
+
if (typeof part === 'string') {
|
|
314
|
+
parts.push({ type: 'text', content: part });
|
|
315
|
+
}
|
|
316
|
+
else if (typeof part === 'object' && part !== null) {
|
|
317
|
+
const ptype = part.type || 'text';
|
|
318
|
+
if (ptype === 'text') {
|
|
319
|
+
parts.push({ type: 'text', content: part.text || '' });
|
|
320
|
+
}
|
|
321
|
+
else if (ptype === 'image_url') {
|
|
322
|
+
const url = part.image_url;
|
|
323
|
+
if (typeof url === 'string')
|
|
324
|
+
parts.push({ type: 'image', url });
|
|
325
|
+
else if (url?.url)
|
|
326
|
+
parts.push({ type: 'image', url: url.url });
|
|
327
|
+
}
|
|
328
|
+
else {
|
|
329
|
+
parts.push({ type: ptype, content: String(part) });
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
return parts.length > 0 ? parts : [{ type: 'text', content: '' }];
|
|
334
|
+
}
|
|
335
|
+
return [{ type: 'text', content: String(content) }];
|
|
336
|
+
}
|
|
337
|
+
function shouldCaptureMessageContent() {
|
|
338
|
+
return config_1.default.captureMessageContent ?? config_1.default.traceContent ?? true;
|
|
339
|
+
}
|
|
340
|
+
function normalizeToolCalls(rawToolCalls) {
|
|
341
|
+
return rawToolCalls.map((call) => ({
|
|
342
|
+
id: call?.id || call?.tool_call_id || '',
|
|
343
|
+
type: call?.type || 'function',
|
|
344
|
+
name: call?.name || call?.function?.name || '',
|
|
345
|
+
arguments: call?.args ?? call?.arguments ?? call?.function?.arguments ?? {},
|
|
346
|
+
}));
|
|
347
|
+
}
|
|
348
|
+
function stringifyToolCallArgument(value) {
|
|
349
|
+
if (typeof value === 'string')
|
|
350
|
+
return value;
|
|
351
|
+
try {
|
|
352
|
+
return JSON.stringify(value ?? {});
|
|
353
|
+
}
|
|
354
|
+
catch {
|
|
355
|
+
return '[unserializable]';
|
|
356
|
+
}
|
|
357
|
+
}
|
|
358
|
+
// ---------------------------------------------------------------------------
|
|
359
|
+
// Conversation ID extraction (mirrors Python _resolve_conversation_id)
|
|
360
|
+
// ---------------------------------------------------------------------------
|
|
361
|
+
function resolveConversationId(metadata) {
|
|
362
|
+
if (!metadata)
|
|
363
|
+
return null;
|
|
364
|
+
for (const key of ['thread_id', 'conversation_id', 'session_id']) {
|
|
365
|
+
if (metadata[key])
|
|
366
|
+
return String(metadata[key]);
|
|
367
|
+
}
|
|
368
|
+
const configurable = metadata.configurable || {};
|
|
369
|
+
for (const key of ['thread_id', 'conversation_id']) {
|
|
370
|
+
if (configurable[key])
|
|
371
|
+
return String(configurable[key]);
|
|
372
|
+
}
|
|
373
|
+
return null;
|
|
374
|
+
}
|
|
375
|
+
// ---------------------------------------------------------------------------
|
|
376
|
+
// Token calculation (approximate, mirrors Python general_tokens)
|
|
377
|
+
// ---------------------------------------------------------------------------
|
|
378
|
+
function generalTokens(text) {
|
|
379
|
+
return Math.ceil(text.length / 2);
|
|
380
|
+
}
|
|
381
|
+
// ---------------------------------------------------------------------------
|
|
382
|
+
// Callback Handler
|
|
383
|
+
// ---------------------------------------------------------------------------
|
|
12
384
|
let handlerInstance = null;
|
|
13
385
|
class OpenLITCallbackHandler {
|
|
14
386
|
constructor(tracer) {
|
|
15
387
|
this.name = 'openlit_callback_handler';
|
|
16
388
|
this.lc_serializable = false;
|
|
17
|
-
// LangChain checks this to decide if it should be copied to child runs
|
|
18
389
|
this.awaitHandlers = false;
|
|
19
390
|
this.spans = new Map();
|
|
391
|
+
this.skippedRuns = new Map();
|
|
20
392
|
this.tracer = tracer;
|
|
21
393
|
}
|
|
22
|
-
// ----
|
|
23
|
-
|
|
24
|
-
if (
|
|
25
|
-
return
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
if (!llm)
|
|
37
|
-
return semantic_convention_1.default.GEN_AI_SYSTEM_LANGCHAIN;
|
|
38
|
-
const classPath = (llm.id || []).join('.').toLowerCase();
|
|
39
|
-
const providerMap = {
|
|
40
|
-
openai: semantic_convention_1.default.GEN_AI_SYSTEM_OPENAI,
|
|
41
|
-
anthropic: semantic_convention_1.default.GEN_AI_SYSTEM_ANTHROPIC,
|
|
42
|
-
bedrock: semantic_convention_1.default.GEN_AI_SYSTEM_AWS_BEDROCK,
|
|
43
|
-
google: semantic_convention_1.default.GEN_AI_SYSTEM_VERTEXAI,
|
|
44
|
-
cohere: semantic_convention_1.default.GEN_AI_SYSTEM_COHERE,
|
|
45
|
-
mistral: semantic_convention_1.default.GEN_AI_SYSTEM_MISTRAL,
|
|
46
|
-
};
|
|
47
|
-
for (const [key, val] of Object.entries(providerMap)) {
|
|
48
|
-
if (classPath.includes(key))
|
|
49
|
-
return val;
|
|
394
|
+
// ---- Helpers -----------------------------------------------------------
|
|
395
|
+
_getNameFromCallback(serialized, kwargs) {
|
|
396
|
+
if (kwargs?.name)
|
|
397
|
+
return kwargs.name;
|
|
398
|
+
if (serialized) {
|
|
399
|
+
if (serialized.kwargs?.name)
|
|
400
|
+
return serialized.kwargs.name;
|
|
401
|
+
if (serialized.name)
|
|
402
|
+
return serialized.name;
|
|
403
|
+
if (serialized.id) {
|
|
404
|
+
const id = serialized.id;
|
|
405
|
+
if (Array.isArray(id) && id.length > 0)
|
|
406
|
+
return id[id.length - 1];
|
|
407
|
+
}
|
|
50
408
|
}
|
|
51
|
-
return
|
|
409
|
+
return 'unknown';
|
|
410
|
+
}
|
|
411
|
+
_resolveParentRunId(parentRunId) {
|
|
412
|
+
const visited = new Set();
|
|
413
|
+
let current = parentRunId;
|
|
414
|
+
while (current && this.skippedRuns.has(current)) {
|
|
415
|
+
if (visited.has(current))
|
|
416
|
+
break;
|
|
417
|
+
visited.add(current);
|
|
418
|
+
current = this.skippedRuns.get(current);
|
|
419
|
+
}
|
|
420
|
+
return current;
|
|
52
421
|
}
|
|
53
|
-
// ---- Parent context -------------------------------------------------------
|
|
54
422
|
_getParentContext(parentRunId) {
|
|
55
|
-
|
|
423
|
+
const resolved = this._resolveParentRunId(parentRunId);
|
|
424
|
+
if (!resolved)
|
|
56
425
|
return undefined;
|
|
57
|
-
const holder = this.spans.get(
|
|
426
|
+
const holder = this.spans.get(resolved);
|
|
58
427
|
if (!holder)
|
|
59
428
|
return undefined;
|
|
60
429
|
return api_1.trace.setSpan(api_1.context.active(), holder.span);
|
|
61
430
|
}
|
|
62
|
-
|
|
63
|
-
|
|
431
|
+
_createSpan(runId, parentRunId, spanName, kind = api_1.SpanKind.INTERNAL) {
|
|
432
|
+
const resolved = this._resolveParentRunId(parentRunId);
|
|
433
|
+
let parentContext;
|
|
434
|
+
if (resolved && this.spans.has(resolved)) {
|
|
435
|
+
parentContext = api_1.trace.setSpan(api_1.context.active(), this.spans.get(resolved).span);
|
|
436
|
+
}
|
|
437
|
+
return this.tracer.startSpan(spanName, { kind }, parentContext);
|
|
438
|
+
}
|
|
439
|
+
_newHolder(span, parentRunId) {
|
|
440
|
+
return {
|
|
441
|
+
span,
|
|
442
|
+
startTime: Date.now(),
|
|
443
|
+
modelName: 'unknown',
|
|
444
|
+
modelParameters: {},
|
|
445
|
+
provider: '',
|
|
446
|
+
serverAddress: '',
|
|
447
|
+
serverPort: 0,
|
|
448
|
+
parentRunId,
|
|
449
|
+
children: [],
|
|
450
|
+
streamingContent: [],
|
|
451
|
+
tokenTimestamps: [],
|
|
452
|
+
inputTokens: 0,
|
|
453
|
+
outputTokens: 0,
|
|
454
|
+
cacheReadInputTokens: 0,
|
|
455
|
+
cacheCreationInputTokens: 0,
|
|
456
|
+
promptContent: '',
|
|
457
|
+
inputMessagesRaw: null,
|
|
458
|
+
prompts: [],
|
|
459
|
+
inputMessagesStructured: [],
|
|
460
|
+
systemInstructions: null,
|
|
461
|
+
toolDefinitions: null,
|
|
462
|
+
toolCalls: null,
|
|
463
|
+
finishReason: 'stop',
|
|
464
|
+
isAgentChain: false,
|
|
465
|
+
responseId: null,
|
|
466
|
+
suppressionActive: false,
|
|
467
|
+
};
|
|
468
|
+
}
|
|
469
|
+
_setCommonAttributes(span, operationType) {
|
|
470
|
+
span.setAttribute(semantic_convention_1.default.GEN_AI_OPERATION, operationType);
|
|
471
|
+
span.setAttribute(semantic_conventions_1.ATTR_TELEMETRY_SDK_NAME, constant_1.SDK_NAME);
|
|
472
|
+
span.setAttribute(semantic_convention_1.default.ATTR_DEPLOYMENT_ENVIRONMENT, config_1.default.environment || 'default');
|
|
473
|
+
span.setAttribute(semantic_conventions_1.ATTR_SERVICE_NAME, config_1.default.applicationName || 'default');
|
|
474
|
+
span.setAttribute(semantic_convention_1.default.GEN_AI_SDK_VERSION, constant_1.SDK_VERSION);
|
|
475
|
+
}
|
|
476
|
+
_setModelParameters(span, params) {
|
|
477
|
+
if (params.temperature != null)
|
|
478
|
+
span.setAttribute(semantic_convention_1.default.GEN_AI_REQUEST_TEMPERATURE, params.temperature);
|
|
479
|
+
if (params.max_tokens != null)
|
|
480
|
+
span.setAttribute(semantic_convention_1.default.GEN_AI_REQUEST_MAX_TOKENS, params.max_tokens);
|
|
481
|
+
if (params.max_completion_tokens != null)
|
|
482
|
+
span.setAttribute(semantic_convention_1.default.GEN_AI_REQUEST_MAX_TOKENS, params.max_completion_tokens);
|
|
483
|
+
if (params.top_p != null)
|
|
484
|
+
span.setAttribute(semantic_convention_1.default.GEN_AI_REQUEST_TOP_P, params.top_p);
|
|
485
|
+
if (params.top_k != null)
|
|
486
|
+
span.setAttribute(semantic_convention_1.default.GEN_AI_REQUEST_TOP_K, params.top_k);
|
|
487
|
+
if (params.frequency_penalty != null)
|
|
488
|
+
span.setAttribute(semantic_convention_1.default.GEN_AI_REQUEST_FREQUENCY_PENALTY, params.frequency_penalty);
|
|
489
|
+
if (params.presence_penalty != null)
|
|
490
|
+
span.setAttribute(semantic_convention_1.default.GEN_AI_REQUEST_PRESENCE_PENALTY, params.presence_penalty);
|
|
491
|
+
if (params.seed != null)
|
|
492
|
+
span.setAttribute(semantic_convention_1.default.GEN_AI_REQUEST_SEED, Number(params.seed));
|
|
493
|
+
const stop = params.stop || params.stop_sequences;
|
|
494
|
+
if (stop) {
|
|
495
|
+
span.setAttribute(semantic_convention_1.default.GEN_AI_REQUEST_STOP_SEQUENCES, Array.isArray(stop) ? stop : [stop]);
|
|
496
|
+
}
|
|
497
|
+
}
|
|
498
|
+
_endSpan(runId, error) {
|
|
499
|
+
const holder = this.spans.get(runId);
|
|
500
|
+
if (!holder)
|
|
501
|
+
return;
|
|
502
|
+
for (const childId of holder.children || []) {
|
|
503
|
+
const child = this.spans.get(childId);
|
|
504
|
+
if (child) {
|
|
505
|
+
try {
|
|
506
|
+
child.span.end();
|
|
507
|
+
}
|
|
508
|
+
catch { /* already ended */ }
|
|
509
|
+
}
|
|
510
|
+
}
|
|
511
|
+
if (error) {
|
|
512
|
+
holder.span.setStatus({ code: api_1.SpanStatusCode.ERROR, message: error });
|
|
513
|
+
}
|
|
514
|
+
else {
|
|
515
|
+
holder.span.setStatus({ code: api_1.SpanStatusCode.OK });
|
|
516
|
+
}
|
|
517
|
+
holder.span.end();
|
|
518
|
+
this.spans.delete(runId);
|
|
519
|
+
}
|
|
520
|
+
// ---- Chain Callbacks ---------------------------------------------------
|
|
521
|
+
handleChainStart(chain, inputs, runId, parentRunId, _tags, metadata, _runType, name) {
|
|
522
|
+
try {
|
|
523
|
+
const resolvedName = this._getNameFromCallback(chain, { name });
|
|
524
|
+
const obsType = detectObservationType(chain, 'chain', resolvedName);
|
|
525
|
+
if (obsType !== 'agent' && isInternalChain(chain, resolvedName)) {
|
|
526
|
+
this.skippedRuns.set(runId, parentRunId);
|
|
527
|
+
return;
|
|
528
|
+
}
|
|
529
|
+
if (obsType !== 'agent' && (0, helpers_1.isLangGraphActive)() && !parentRunId) {
|
|
530
|
+
this.skippedRuns.set(runId, parentRunId);
|
|
531
|
+
return;
|
|
532
|
+
}
|
|
533
|
+
if (obsType !== 'agent' && parentRunId) {
|
|
534
|
+
this.skippedRuns.set(runId, parentRunId);
|
|
535
|
+
return;
|
|
536
|
+
}
|
|
537
|
+
const operationType = obsType === 'agent'
|
|
538
|
+
? semantic_convention_1.default.GEN_AI_OPERATION_TYPE_AGENT
|
|
539
|
+
: semantic_convention_1.default.GEN_AI_OPERATION_TYPE_FRAMEWORK;
|
|
540
|
+
const spanName = obsType === 'agent'
|
|
541
|
+
? `invoke_agent ${resolvedName}`
|
|
542
|
+
: `invoke_workflow ${resolvedName}`;
|
|
543
|
+
const span = this._createSpan(runId, parentRunId, spanName);
|
|
544
|
+
const holder = this._newHolder(span, parentRunId);
|
|
545
|
+
if (obsType === 'agent')
|
|
546
|
+
holder.isAgentChain = true;
|
|
547
|
+
this.spans.set(runId, holder);
|
|
548
|
+
if (parentRunId) {
|
|
549
|
+
const parentResolved = this._resolveParentRunId(parentRunId);
|
|
550
|
+
if (parentResolved && this.spans.has(parentResolved)) {
|
|
551
|
+
this.spans.get(parentResolved).children.push(runId);
|
|
552
|
+
}
|
|
553
|
+
}
|
|
554
|
+
span.setAttribute(semantic_convention_1.default.GEN_AI_PROVIDER_NAME_OTEL, semantic_convention_1.default.GEN_AI_SYSTEM_LANGCHAIN);
|
|
555
|
+
this._setCommonAttributes(span, operationType);
|
|
556
|
+
if (obsType === 'agent') {
|
|
557
|
+
span.setAttribute(semantic_convention_1.default.GEN_AI_AGENT_NAME, resolvedName);
|
|
558
|
+
span.setAttribute(semantic_convention_1.default.GEN_AI_AGENT_ID, runId);
|
|
559
|
+
}
|
|
560
|
+
else {
|
|
561
|
+
span.setAttribute(semantic_convention_1.default.GEN_AI_WORKFLOW_NAME, resolvedName);
|
|
562
|
+
}
|
|
563
|
+
const convId = resolveConversationId(metadata);
|
|
564
|
+
if (convId)
|
|
565
|
+
span.setAttribute(semantic_convention_1.default.GEN_AI_CONVERSATION_ID, convId);
|
|
566
|
+
if (config_1.default.captureMessageContent && inputs) {
|
|
567
|
+
try {
|
|
568
|
+
const inputStr = typeof inputs === 'string' ? inputs : JSON.stringify(inputs);
|
|
569
|
+
span.setAttribute(semantic_convention_1.default.GEN_AI_WORKFLOW_INPUT, inputStr.slice(0, 2000));
|
|
570
|
+
}
|
|
571
|
+
catch { /* non-blocking */ }
|
|
572
|
+
}
|
|
573
|
+
(0, helpers_1.applyCustomSpanAttributes)(span);
|
|
574
|
+
}
|
|
575
|
+
catch { /* non-blocking */ }
|
|
576
|
+
}
|
|
577
|
+
handleChainEnd(outputs, runId) {
|
|
578
|
+
try {
|
|
579
|
+
this.skippedRuns.delete(runId);
|
|
580
|
+
const holder = this.spans.get(runId);
|
|
581
|
+
if (!holder)
|
|
582
|
+
return;
|
|
583
|
+
const duration = (Date.now() - holder.startTime) / 1000;
|
|
584
|
+
holder.span.setAttribute(semantic_convention_1.default.GEN_AI_CLIENT_OPERATION_DURATION, duration);
|
|
585
|
+
if (config_1.default.captureMessageContent && outputs) {
|
|
586
|
+
try {
|
|
587
|
+
const outputStr = typeof outputs === 'string' ? outputs : JSON.stringify(outputs);
|
|
588
|
+
holder.span.setAttribute(semantic_convention_1.default.GEN_AI_WORKFLOW_OUTPUT, outputStr.slice(0, 2000));
|
|
589
|
+
}
|
|
590
|
+
catch { /* non-blocking */ }
|
|
591
|
+
}
|
|
592
|
+
this._endSpan(runId);
|
|
593
|
+
}
|
|
594
|
+
catch { /* non-blocking */ }
|
|
595
|
+
}
|
|
596
|
+
handleChainError(error, runId) {
|
|
597
|
+
try {
|
|
598
|
+
this.skippedRuns.delete(runId);
|
|
599
|
+
if (this.spans.has(runId)) {
|
|
600
|
+
const span = this.spans.get(runId).span;
|
|
601
|
+
const errorType = error?.constructor?.name || '_OTHER';
|
|
602
|
+
span.setAttribute(semantic_convention_1.default.ERROR_TYPE, errorType);
|
|
603
|
+
}
|
|
604
|
+
this._endSpan(runId, String(error));
|
|
605
|
+
}
|
|
606
|
+
catch { /* non-blocking */ }
|
|
607
|
+
}
|
|
608
|
+
// ---- LLM Callbacks -----------------------------------------------------
|
|
609
|
+
handleChatModelStart(llm, messages, runId, parentRunId, _extraParams, _tags, metadata, kwargs) {
|
|
610
|
+
try {
|
|
611
|
+
const modelName = extractModelName(llm, kwargs || {});
|
|
612
|
+
const modelParams = extractModelParameters(kwargs || {});
|
|
613
|
+
const provider = detectProvider(llm);
|
|
614
|
+
const spanName = `chat ${modelName}`;
|
|
615
|
+
const span = this._createSpan(runId, parentRunId, spanName, api_1.SpanKind.CLIENT);
|
|
616
|
+
const holder = this._newHolder(span, parentRunId);
|
|
617
|
+
holder.modelName = modelName;
|
|
618
|
+
holder.modelParameters = modelParams;
|
|
619
|
+
holder.provider = provider;
|
|
620
|
+
holder.suppressionActive = true;
|
|
621
|
+
(0, helpers_1.setFrameworkLlmActive)();
|
|
622
|
+
(0, helpers_1.setFrameworkParentContext)(api_1.trace.setSpan(api_1.context.active(), span));
|
|
623
|
+
this.spans.set(runId, holder);
|
|
624
|
+
if (parentRunId) {
|
|
625
|
+
const parentResolved = this._resolveParentRunId(parentRunId);
|
|
626
|
+
if (parentResolved && this.spans.has(parentResolved)) {
|
|
627
|
+
this.spans.get(parentResolved).children.push(runId);
|
|
628
|
+
}
|
|
629
|
+
}
|
|
630
|
+
span.setAttribute(semantic_convention_1.default.GEN_AI_PROVIDER_NAME_OTEL, provider);
|
|
631
|
+
this._setCommonAttributes(span, semantic_convention_1.default.GEN_AI_OPERATION_TYPE_CHAT);
|
|
632
|
+
span.setAttribute(semantic_convention_1.default.GEN_AI_REQUEST_MODEL, modelName);
|
|
633
|
+
this._setModelParameters(span, modelParams);
|
|
634
|
+
const convId = resolveConversationId(metadata);
|
|
635
|
+
if (convId)
|
|
636
|
+
span.setAttribute(semantic_convention_1.default.GEN_AI_CONVERSATION_ID, convId);
|
|
637
|
+
// Server address from invocation_params or provider defaults
|
|
638
|
+
const ip = kwargs?.invocation_params || {};
|
|
639
|
+
const apiBase = ip.api_base || ip.base_url;
|
|
640
|
+
if (apiBase) {
|
|
641
|
+
try {
|
|
642
|
+
const url = new URL(apiBase);
|
|
643
|
+
holder.serverAddress = url.hostname || '';
|
|
644
|
+
holder.serverPort = url.port ? Number(url.port) : 443;
|
|
645
|
+
}
|
|
646
|
+
catch { /* ignore */ }
|
|
647
|
+
}
|
|
648
|
+
if (!holder.serverAddress) {
|
|
649
|
+
const [defaultHost, defaultPort] = (0, helpers_1.getServerAddressForProvider)(provider);
|
|
650
|
+
if (defaultHost) {
|
|
651
|
+
holder.serverAddress = defaultHost;
|
|
652
|
+
holder.serverPort = defaultPort;
|
|
653
|
+
}
|
|
654
|
+
}
|
|
655
|
+
if (messages) {
|
|
656
|
+
const formatted = [];
|
|
657
|
+
for (const msgList of messages) {
|
|
658
|
+
for (const msg of msgList) {
|
|
659
|
+
const role = msg._getType?.() || msg.type || 'unknown';
|
|
660
|
+
const content = msg.content ?? String(msg);
|
|
661
|
+
formatted.push(`${role}: ${content}`);
|
|
662
|
+
}
|
|
663
|
+
}
|
|
664
|
+
const promptStr = formatted.join('\n');
|
|
665
|
+
holder.promptContent = promptStr;
|
|
666
|
+
holder.inputTokens = generalTokens(promptStr);
|
|
667
|
+
holder.inputMessagesRaw = messages;
|
|
668
|
+
if (config_1.default.captureMessageContent) {
|
|
669
|
+
// System instructions
|
|
670
|
+
const sysInstructions = [];
|
|
671
|
+
for (const msgList of messages) {
|
|
672
|
+
for (const msg of msgList) {
|
|
673
|
+
const role = msg._getType?.() || msg.type || '';
|
|
674
|
+
if (role === 'system') {
|
|
675
|
+
const content = msg.content ?? '';
|
|
676
|
+
if (content)
|
|
677
|
+
sysInstructions.push({ type: 'text', content: String(content) });
|
|
678
|
+
}
|
|
679
|
+
}
|
|
680
|
+
}
|
|
681
|
+
if (sysInstructions.length > 0) {
|
|
682
|
+
holder.systemInstructions = sysInstructions;
|
|
683
|
+
span.setAttribute(semantic_convention_1.default.GEN_AI_SYSTEM_INSTRUCTIONS, JSON.stringify(sysInstructions));
|
|
684
|
+
}
|
|
685
|
+
// Tool definitions
|
|
686
|
+
const tools = ip.tools || ip.functions;
|
|
687
|
+
if (tools && Array.isArray(tools)) {
|
|
688
|
+
holder.toolDefinitions = tools;
|
|
689
|
+
span.setAttribute(semantic_convention_1.default.GEN_AI_TOOL_DEFINITIONS, JSON.stringify(tools));
|
|
690
|
+
}
|
|
691
|
+
// Structured input messages
|
|
692
|
+
try {
|
|
693
|
+
holder.inputMessagesStructured = buildInputMessages(messages);
|
|
694
|
+
}
|
|
695
|
+
catch { /* non-blocking */ }
|
|
696
|
+
}
|
|
697
|
+
}
|
|
698
|
+
(0, helpers_1.applyCustomSpanAttributes)(span);
|
|
699
|
+
}
|
|
700
|
+
catch { /* non-blocking */ }
|
|
701
|
+
}
|
|
702
|
+
handleLLMStart(serialized, prompts, runId, parentRunId, _extraParams, _tags, metadata, kwargs) {
|
|
64
703
|
try {
|
|
65
|
-
const modelName =
|
|
66
|
-
const
|
|
704
|
+
const modelName = extractModelName(serialized, kwargs || {});
|
|
705
|
+
const modelParams = extractModelParameters(kwargs || {});
|
|
706
|
+
const provider = detectProvider(serialized);
|
|
67
707
|
const spanName = `chat ${modelName}`;
|
|
68
|
-
const
|
|
69
|
-
const
|
|
70
|
-
|
|
708
|
+
const span = this._createSpan(runId, parentRunId, spanName, api_1.SpanKind.CLIENT);
|
|
709
|
+
const holder = this._newHolder(span, parentRunId);
|
|
710
|
+
holder.modelName = modelName;
|
|
711
|
+
holder.modelParameters = modelParams;
|
|
712
|
+
holder.provider = provider;
|
|
713
|
+
holder.prompts = prompts || [];
|
|
714
|
+
holder.suppressionActive = true;
|
|
715
|
+
(0, helpers_1.setFrameworkLlmActive)();
|
|
716
|
+
(0, helpers_1.setFrameworkParentContext)(api_1.trace.setSpan(api_1.context.active(), span));
|
|
717
|
+
this.spans.set(runId, holder);
|
|
718
|
+
if (parentRunId) {
|
|
719
|
+
const parentResolved = this._resolveParentRunId(parentRunId);
|
|
720
|
+
if (parentResolved && this.spans.has(parentResolved)) {
|
|
721
|
+
this.spans.get(parentResolved).children.push(runId);
|
|
722
|
+
}
|
|
723
|
+
}
|
|
71
724
|
span.setAttribute(semantic_convention_1.default.GEN_AI_PROVIDER_NAME_OTEL, provider);
|
|
72
|
-
|
|
73
|
-
span.setAttribute(semantic_convention_1.default.GEN_AI_ENVIRONMENT, config_1.default.environment || '');
|
|
74
|
-
span.setAttribute(semantic_convention_1.default.GEN_AI_APPLICATION_NAME, config_1.default.applicationName || '');
|
|
725
|
+
this._setCommonAttributes(span, semantic_convention_1.default.GEN_AI_OPERATION_TYPE_CHAT);
|
|
75
726
|
span.setAttribute(semantic_convention_1.default.GEN_AI_REQUEST_MODEL, modelName);
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
if (
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
727
|
+
this._setModelParameters(span, modelParams);
|
|
728
|
+
const convId = resolveConversationId(metadata);
|
|
729
|
+
if (convId)
|
|
730
|
+
span.setAttribute(semantic_convention_1.default.GEN_AI_CONVERSATION_ID, convId);
|
|
731
|
+
// Server address
|
|
732
|
+
const ip = kwargs?.invocation_params || {};
|
|
733
|
+
const apiBase = ip.api_base || ip.base_url;
|
|
734
|
+
if (apiBase) {
|
|
735
|
+
try {
|
|
736
|
+
const url = new URL(apiBase);
|
|
737
|
+
holder.serverAddress = url.hostname || '';
|
|
738
|
+
holder.serverPort = url.port ? Number(url.port) : 443;
|
|
739
|
+
}
|
|
740
|
+
catch { /* ignore */ }
|
|
741
|
+
}
|
|
742
|
+
if (!holder.serverAddress) {
|
|
743
|
+
const [defaultHost, defaultPort] = (0, helpers_1.getServerAddressForProvider)(provider);
|
|
744
|
+
if (defaultHost) {
|
|
745
|
+
holder.serverAddress = defaultHost;
|
|
746
|
+
holder.serverPort = defaultPort;
|
|
747
|
+
}
|
|
748
|
+
}
|
|
749
|
+
if (prompts && prompts.length > 0) {
|
|
750
|
+
const promptStr = prompts.join('\n');
|
|
751
|
+
holder.inputTokens = generalTokens(promptStr);
|
|
752
|
+
if (config_1.default.captureMessageContent) {
|
|
753
|
+
try {
|
|
754
|
+
holder.inputMessagesStructured = buildInputMessages(prompts);
|
|
755
|
+
}
|
|
756
|
+
catch { /* non-blocking */ }
|
|
757
|
+
}
|
|
758
|
+
}
|
|
759
|
+
(0, helpers_1.applyCustomSpanAttributes)(span);
|
|
102
760
|
}
|
|
103
761
|
catch { /* non-blocking */ }
|
|
104
762
|
}
|
|
105
|
-
handleLLMNewToken(token, _idx, runId) {
|
|
763
|
+
handleLLMNewToken(token, _idx, runId, _chunk) {
|
|
106
764
|
try {
|
|
107
765
|
const holder = this.spans.get(runId);
|
|
108
766
|
if (!holder)
|
|
@@ -113,194 +771,451 @@ class OpenLITCallbackHandler {
|
|
|
113
771
|
holder.tokenTimestamps.push(now);
|
|
114
772
|
if (token)
|
|
115
773
|
holder.streamingContent.push(token);
|
|
116
|
-
// Mark as streaming
|
|
117
|
-
holder.span.setAttribute(semantic_convention_1.default.GEN_AI_REQUEST_IS_STREAM, true);
|
|
118
774
|
}
|
|
119
775
|
catch { /* non-blocking */ }
|
|
120
776
|
}
|
|
121
777
|
handleLLMEnd(output, runId) {
|
|
122
|
-
this._finalizeLLMSpan(runId, output, undefined);
|
|
123
|
-
}
|
|
124
|
-
handleLLMError(error, runId) {
|
|
125
778
|
try {
|
|
126
779
|
const holder = this.spans.get(runId);
|
|
127
780
|
if (!holder)
|
|
128
781
|
return;
|
|
129
|
-
|
|
130
|
-
holder.span.end();
|
|
131
|
-
this.spans.delete(runId);
|
|
132
|
-
}
|
|
133
|
-
catch { /* non-blocking */ }
|
|
134
|
-
}
|
|
135
|
-
async _finalizeLLMSpan(runId, output, _error) {
|
|
136
|
-
try {
|
|
137
|
-
const holder = this.spans.get(runId);
|
|
138
|
-
if (!holder)
|
|
139
|
-
return;
|
|
140
|
-
const { span, startTime, modelName, streamingContent, tokenTimestamps, firstTokenTime } = holder;
|
|
782
|
+
const { span, startTime, modelName, modelParameters = {}, streamingContent = [], tokenTimestamps = [], firstTokenTime, } = holder;
|
|
141
783
|
const endTime = Date.now();
|
|
784
|
+
const duration = (endTime - startTime) / 1000;
|
|
785
|
+
span.setAttribute(semantic_convention_1.default.GEN_AI_CLIENT_OPERATION_DURATION, duration);
|
|
142
786
|
const isStreaming = streamingContent.length > 0;
|
|
143
|
-
|
|
144
|
-
let
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
787
|
+
let ttft = 0;
|
|
788
|
+
let tbt = 0;
|
|
789
|
+
if (isStreaming) {
|
|
790
|
+
if (firstTokenTime)
|
|
791
|
+
ttft = (firstTokenTime - startTime) / 1000;
|
|
792
|
+
if (tokenTimestamps.length > 1) {
|
|
793
|
+
const diffs = tokenTimestamps.slice(1).map((t, i) => t - tokenTimestamps[i]);
|
|
794
|
+
tbt = diffs.reduce((a, b) => a + b, 0) / diffs.length / 1000;
|
|
795
|
+
}
|
|
796
|
+
}
|
|
797
|
+
else {
|
|
798
|
+
ttft = duration;
|
|
799
|
+
}
|
|
800
|
+
// Extract tokens and content from output
|
|
801
|
+
let inputTokens = holder.inputTokens;
|
|
802
|
+
let outputTokens = 0;
|
|
803
|
+
let completionContent = streamingContent.join('');
|
|
148
804
|
let responseModel = modelName;
|
|
805
|
+
let responseId = null;
|
|
806
|
+
// From llm_output (top-level)
|
|
149
807
|
if (output?.llm_output) {
|
|
150
808
|
const lu = output.llm_output;
|
|
151
809
|
const tu = lu.token_usage || lu.usage || {};
|
|
152
|
-
|
|
153
|
-
|
|
810
|
+
inputTokens = tu.prompt_tokens || tu.input_tokens || inputTokens;
|
|
811
|
+
outputTokens = tu.completion_tokens || tu.output_tokens || outputTokens;
|
|
154
812
|
responseModel = lu.model_name || lu.model || modelName;
|
|
813
|
+
responseId = lu.id || null;
|
|
814
|
+
// Cache token extraction
|
|
815
|
+
const promptDetails = tu.prompt_tokens_details || tu.input_tokens_details || {};
|
|
816
|
+
let cached = promptDetails.cached_tokens || 0;
|
|
817
|
+
const inputDetails = tu.input_tokens_details || {};
|
|
818
|
+
let creation = inputDetails.cache_creation_tokens || 0;
|
|
819
|
+
const langchainInput = tu.input_token_details || {};
|
|
820
|
+
if (!cached)
|
|
821
|
+
cached = langchainInput.cache_read || 0;
|
|
822
|
+
if (!creation)
|
|
823
|
+
creation = langchainInput.cache_creation || 0;
|
|
824
|
+
holder.cacheReadInputTokens = cached;
|
|
825
|
+
holder.cacheCreationInputTokens = creation;
|
|
155
826
|
}
|
|
827
|
+
// From generations
|
|
156
828
|
const generations = output?.generations || [];
|
|
157
829
|
for (const genList of generations) {
|
|
158
830
|
for (const gen of (Array.isArray(genList) ? genList : [genList])) {
|
|
159
831
|
const msg = gen?.message || gen;
|
|
160
|
-
//
|
|
832
|
+
// Usage from usage_metadata
|
|
161
833
|
const um = msg?.usage_metadata;
|
|
162
834
|
if (um) {
|
|
163
|
-
if (!
|
|
164
|
-
|
|
165
|
-
if (!
|
|
166
|
-
|
|
835
|
+
if (!inputTokens)
|
|
836
|
+
inputTokens = um.input_tokens || um.prompt_tokens || 0;
|
|
837
|
+
if (!outputTokens)
|
|
838
|
+
outputTokens = um.output_tokens || um.completion_tokens || 0;
|
|
839
|
+
// Cache tokens from usage_metadata
|
|
840
|
+
const pd = um.prompt_tokens_details || um.input_tokens_details || {};
|
|
841
|
+
if (!holder.cacheReadInputTokens)
|
|
842
|
+
holder.cacheReadInputTokens = pd.cached_tokens || 0;
|
|
843
|
+
const langchainInput = um.input_token_details || {};
|
|
844
|
+
if (!holder.cacheReadInputTokens)
|
|
845
|
+
holder.cacheReadInputTokens = langchainInput.cache_read || 0;
|
|
846
|
+
if (!holder.cacheCreationInputTokens)
|
|
847
|
+
holder.cacheCreationInputTokens = langchainInput.cache_creation || 0;
|
|
848
|
+
}
|
|
849
|
+
// Token usage from response_metadata
|
|
850
|
+
if (msg?.response_metadata) {
|
|
851
|
+
const rm = msg.response_metadata;
|
|
852
|
+
const tokenUsage = rm.token_usage;
|
|
853
|
+
if (tokenUsage) {
|
|
854
|
+
inputTokens = tokenUsage.prompt_tokens || tokenUsage.input_tokens || inputTokens;
|
|
855
|
+
outputTokens = tokenUsage.completion_tokens || tokenUsage.output_tokens || outputTokens;
|
|
856
|
+
}
|
|
857
|
+
if (rm.usage) {
|
|
858
|
+
inputTokens = rm.usage.inputTokens || rm.usage.input_tokens || inputTokens;
|
|
859
|
+
outputTokens = rm.usage.outputTokens || rm.usage.output_tokens || outputTokens;
|
|
860
|
+
}
|
|
861
|
+
if (rm['amazon-bedrock-invocationMetrics']) {
|
|
862
|
+
const bm = rm['amazon-bedrock-invocationMetrics'];
|
|
863
|
+
inputTokens = bm.inputTokenCount || inputTokens;
|
|
864
|
+
outputTokens = bm.outputTokenCount || outputTokens;
|
|
865
|
+
}
|
|
167
866
|
}
|
|
168
867
|
// Content
|
|
169
|
-
const
|
|
170
|
-
if (
|
|
171
|
-
completionContent =
|
|
868
|
+
const genContent = gen?.text || msg?.content;
|
|
869
|
+
if (genContent && typeof genContent === 'string' && genContent.length > completionContent.length) {
|
|
870
|
+
completionContent = genContent;
|
|
172
871
|
}
|
|
173
872
|
// Finish reason
|
|
174
873
|
const fr = gen?.generationInfo?.finish_reason || msg?.response_metadata?.finish_reason;
|
|
175
874
|
if (fr)
|
|
176
|
-
finishReason = fr;
|
|
875
|
+
holder.finishReason = fr;
|
|
876
|
+
// Tool calls. Keep last-writer-wins semantics across generations so
|
|
877
|
+
// choices are not merged into one assistant message.
|
|
878
|
+
const tc = msg?.tool_calls || msg?.additional_kwargs?.tool_calls || gen?.message?.tool_calls;
|
|
879
|
+
if (tc && Array.isArray(tc) && tc.length > 0) {
|
|
880
|
+
holder.toolCalls = normalizeToolCalls(tc);
|
|
881
|
+
}
|
|
177
882
|
}
|
|
178
883
|
}
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
const
|
|
884
|
+
// Fallback token estimation
|
|
885
|
+
if (!outputTokens && completionContent)
|
|
886
|
+
outputTokens = generalTokens(completionContent);
|
|
887
|
+
if (!inputTokens && holder.promptContent)
|
|
888
|
+
inputTokens = generalTokens(holder.promptContent);
|
|
889
|
+
// Cost
|
|
890
|
+
const pricingInfo = config_1.default.pricingInfo || {};
|
|
891
|
+
const cost = helpers_1.default.getChatModelCost(modelName, pricingInfo, inputTokens, outputTokens);
|
|
892
|
+
// Provider for span attributes
|
|
893
|
+
const provider = holder.provider || semantic_convention_1.default.GEN_AI_SYSTEM_LANGCHAIN;
|
|
894
|
+
const serverAddress = holder.serverAddress || '';
|
|
895
|
+
const serverPort = holder.serverPort || 0;
|
|
896
|
+
// Set span attributes
|
|
186
897
|
span.setAttribute(semantic_convention_1.default.GEN_AI_RESPONSE_MODEL, responseModel);
|
|
187
|
-
span.setAttribute(semantic_convention_1.default.GEN_AI_USAGE_INPUT_TOKENS,
|
|
188
|
-
span.setAttribute(semantic_convention_1.default.GEN_AI_USAGE_OUTPUT_TOKENS,
|
|
189
|
-
span.setAttribute(semantic_convention_1.default.
|
|
190
|
-
span.setAttribute(semantic_convention_1.default.GEN_AI_CLIENT_TOKEN_USAGE, totalTokens);
|
|
898
|
+
span.setAttribute(semantic_convention_1.default.GEN_AI_USAGE_INPUT_TOKENS, inputTokens);
|
|
899
|
+
span.setAttribute(semantic_convention_1.default.GEN_AI_USAGE_OUTPUT_TOKENS, outputTokens);
|
|
900
|
+
span.setAttribute(semantic_convention_1.default.GEN_AI_CLIENT_TOKEN_USAGE, inputTokens + outputTokens);
|
|
191
901
|
span.setAttribute(semantic_convention_1.default.GEN_AI_USAGE_COST, cost);
|
|
192
|
-
span.setAttribute(semantic_convention_1.default.GEN_AI_RESPONSE_FINISH_REASON, [finishReason]);
|
|
193
|
-
span.setAttribute(semantic_convention_1.default.GEN_AI_OUTPUT_TYPE,
|
|
194
|
-
span.setAttribute(semantic_convention_1.default.
|
|
195
|
-
if (
|
|
196
|
-
|
|
902
|
+
span.setAttribute(semantic_convention_1.default.GEN_AI_RESPONSE_FINISH_REASON, [holder.finishReason]);
|
|
903
|
+
span.setAttribute(semantic_convention_1.default.GEN_AI_OUTPUT_TYPE, typeof completionContent === 'string' ? 'text' : 'json');
|
|
904
|
+
span.setAttribute(semantic_convention_1.default.GEN_AI_REQUEST_IS_STREAM, isStreaming);
|
|
905
|
+
if (responseId)
|
|
906
|
+
span.setAttribute(semantic_convention_1.default.GEN_AI_RESPONSE_ID, responseId);
|
|
907
|
+
if (serverAddress) {
|
|
908
|
+
span.setAttribute(semantic_convention_1.default.SERVER_ADDRESS, serverAddress);
|
|
909
|
+
span.setAttribute(semantic_convention_1.default.SERVER_PORT, serverPort);
|
|
910
|
+
}
|
|
911
|
+
if (isStreaming && ttft > 0) {
|
|
197
912
|
span.setAttribute(semantic_convention_1.default.GEN_AI_SERVER_TTFT, ttft);
|
|
198
|
-
if (
|
|
199
|
-
const diffs = tokenTimestamps.slice(1).map((t, i) => t - tokenTimestamps[i]);
|
|
200
|
-
const tbt = diffs.reduce((a, b) => a + b, 0) / diffs.length / 1000;
|
|
913
|
+
if (tbt > 0)
|
|
201
914
|
span.setAttribute(semantic_convention_1.default.GEN_AI_SERVER_TBT, tbt);
|
|
915
|
+
}
|
|
916
|
+
// Cache tokens
|
|
917
|
+
if (holder.cacheReadInputTokens) {
|
|
918
|
+
span.setAttribute(semantic_convention_1.default.GEN_AI_USAGE_CACHE_READ_INPUT_TOKENS, holder.cacheReadInputTokens);
|
|
919
|
+
}
|
|
920
|
+
if (holder.cacheCreationInputTokens) {
|
|
921
|
+
span.setAttribute(semantic_convention_1.default.GEN_AI_USAGE_CACHE_CREATION_INPUT_TOKENS, holder.cacheCreationInputTokens);
|
|
922
|
+
}
|
|
923
|
+
// Tool calls on span
|
|
924
|
+
if (holder.toolCalls && holder.toolCalls.length > 0) {
|
|
925
|
+
const names = holder.toolCalls.map((t) => t.name || t.function?.name || '').filter(Boolean);
|
|
926
|
+
const ids = holder.toolCalls.map((t) => t.id || '').filter(Boolean);
|
|
927
|
+
const args = holder.toolCalls.map((t) => stringifyToolCallArgument(t.arguments ?? t.function?.arguments));
|
|
928
|
+
if (names.length)
|
|
929
|
+
span.setAttribute(semantic_convention_1.default.GEN_AI_TOOL_NAME, names.join(', '));
|
|
930
|
+
if (ids.length)
|
|
931
|
+
span.setAttribute(semantic_convention_1.default.GEN_AI_TOOL_CALL_ID, ids.join(', '));
|
|
932
|
+
if (args.length)
|
|
933
|
+
span.setAttribute(semantic_convention_1.default.GEN_AI_TOOL_CALL_ARGUMENTS, args);
|
|
934
|
+
}
|
|
935
|
+
let outputMessagesJson = null;
|
|
936
|
+
// Content attributes (gated by captureMessageContent)
|
|
937
|
+
if (shouldCaptureMessageContent()) {
|
|
938
|
+
const inputRaw = holder.inputMessagesRaw || holder.prompts || [];
|
|
939
|
+
const inputMessagesStructured = holder.inputMessagesStructured || [];
|
|
940
|
+
const inputMsgs = inputMessagesStructured.length > 0
|
|
941
|
+
? inputMessagesStructured
|
|
942
|
+
: buildInputMessages(inputRaw);
|
|
943
|
+
const outputToolCalls = holder.toolCalls && holder.toolCalls.length > 0 ? holder.toolCalls : undefined;
|
|
944
|
+
outputMessagesJson = helpers_1.default.buildOutputMessages(completionContent, holder.finishReason, outputToolCalls);
|
|
945
|
+
if (inputMsgs.length > 0)
|
|
946
|
+
span.setAttribute(semantic_convention_1.default.GEN_AI_INPUT_MESSAGES, JSON.stringify(inputMsgs));
|
|
947
|
+
if (outputMessagesJson && (outputMessagesJson !== '[]' || completionContent || outputToolCalls)) {
|
|
948
|
+
span.setAttribute(semantic_convention_1.default.GEN_AI_OUTPUT_MESSAGES, outputMessagesJson);
|
|
202
949
|
}
|
|
203
950
|
}
|
|
204
|
-
|
|
205
|
-
|
|
951
|
+
// Emit inference event (always emitted; content within is gated)
|
|
952
|
+
const eventAttrs = {
|
|
953
|
+
[semantic_convention_1.default.GEN_AI_OPERATION]: semantic_convention_1.default.GEN_AI_OPERATION_TYPE_CHAT,
|
|
954
|
+
[semantic_convention_1.default.GEN_AI_REQUEST_MODEL]: modelName,
|
|
955
|
+
[semantic_convention_1.default.GEN_AI_RESPONSE_MODEL]: responseModel,
|
|
956
|
+
[semantic_convention_1.default.GEN_AI_USAGE_INPUT_TOKENS]: inputTokens,
|
|
957
|
+
[semantic_convention_1.default.GEN_AI_USAGE_OUTPUT_TOKENS]: outputTokens,
|
|
958
|
+
[semantic_convention_1.default.GEN_AI_RESPONSE_FINISH_REASON]: [holder.finishReason],
|
|
959
|
+
[semantic_convention_1.default.GEN_AI_OUTPUT_TYPE]: typeof completionContent === 'string' ? 'text' : 'json',
|
|
960
|
+
};
|
|
961
|
+
if (serverAddress)
|
|
962
|
+
eventAttrs[semantic_convention_1.default.SERVER_ADDRESS] = serverAddress;
|
|
963
|
+
if (serverPort)
|
|
964
|
+
eventAttrs[semantic_convention_1.default.SERVER_PORT] = serverPort;
|
|
965
|
+
if (responseId)
|
|
966
|
+
eventAttrs[semantic_convention_1.default.GEN_AI_RESPONSE_ID] = responseId;
|
|
967
|
+
if (shouldCaptureMessageContent()) {
|
|
968
|
+
const inputRaw = holder.inputMessagesRaw || holder.prompts || [];
|
|
969
|
+
const inputMessagesStructured = holder.inputMessagesStructured || [];
|
|
970
|
+
const inputMsgs = inputMessagesStructured.length > 0
|
|
971
|
+
? inputMessagesStructured
|
|
972
|
+
: buildInputMessages(inputRaw);
|
|
973
|
+
if (inputMsgs.length > 0)
|
|
974
|
+
eventAttrs[semantic_convention_1.default.GEN_AI_INPUT_MESSAGES] = JSON.stringify(inputMsgs);
|
|
975
|
+
if (outputMessagesJson && outputMessagesJson !== '[]')
|
|
976
|
+
eventAttrs[semantic_convention_1.default.GEN_AI_OUTPUT_MESSAGES] = outputMessagesJson;
|
|
977
|
+
if (holder.systemInstructions) {
|
|
978
|
+
eventAttrs[semantic_convention_1.default.GEN_AI_SYSTEM_INSTRUCTIONS] = JSON.stringify(holder.systemInstructions);
|
|
979
|
+
}
|
|
980
|
+
if (holder.toolDefinitions) {
|
|
981
|
+
eventAttrs[semantic_convention_1.default.GEN_AI_TOOL_DEFINITIONS] = JSON.stringify(holder.toolDefinitions);
|
|
982
|
+
}
|
|
983
|
+
// Request params for event
|
|
984
|
+
for (const [k, attr] of [
|
|
985
|
+
['temperature', semantic_convention_1.default.GEN_AI_REQUEST_TEMPERATURE],
|
|
986
|
+
['top_p', semantic_convention_1.default.GEN_AI_REQUEST_TOP_P],
|
|
987
|
+
['frequency_penalty', semantic_convention_1.default.GEN_AI_REQUEST_FREQUENCY_PENALTY],
|
|
988
|
+
['presence_penalty', semantic_convention_1.default.GEN_AI_REQUEST_PRESENCE_PENALTY],
|
|
989
|
+
]) {
|
|
990
|
+
if (modelParameters[k] != null)
|
|
991
|
+
eventAttrs[attr] = modelParameters[k];
|
|
992
|
+
}
|
|
993
|
+
const maxT = modelParameters.max_tokens || modelParameters.max_completion_tokens;
|
|
994
|
+
if (maxT != null)
|
|
995
|
+
eventAttrs[semantic_convention_1.default.GEN_AI_REQUEST_MAX_TOKENS] = maxT;
|
|
206
996
|
}
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
cost,
|
|
212
|
-
aiSystem: semantic_convention_1.default.GEN_AI_SYSTEM_LANGCHAIN,
|
|
213
|
-
serverAddress: 'localhost',
|
|
214
|
-
serverPort: 80,
|
|
215
|
-
});
|
|
997
|
+
if (typeof helpers_1.default.emitInferenceEvent === 'function') {
|
|
998
|
+
helpers_1.default.emitInferenceEvent(span, eventAttrs);
|
|
999
|
+
}
|
|
1000
|
+
// Record metrics
|
|
216
1001
|
const metricParams = {
|
|
217
1002
|
genAIEndpoint: 'langchain.chat_model',
|
|
218
1003
|
model: responseModel,
|
|
219
1004
|
cost,
|
|
220
|
-
aiSystem:
|
|
221
|
-
serverAddress
|
|
222
|
-
serverPort
|
|
1005
|
+
aiSystem: provider,
|
|
1006
|
+
serverAddress,
|
|
1007
|
+
serverPort,
|
|
223
1008
|
};
|
|
224
|
-
span.setStatus({ code: 1 });
|
|
225
|
-
span.end();
|
|
226
1009
|
base_wrapper_1.default.recordMetrics(span, metricParams);
|
|
227
|
-
|
|
1010
|
+
if (holder.suppressionActive) {
|
|
1011
|
+
(0, helpers_1.resetFrameworkLlmActive)();
|
|
1012
|
+
(0, helpers_1.clearFrameworkParentContext)();
|
|
1013
|
+
}
|
|
1014
|
+
this._endSpan(runId);
|
|
1015
|
+
}
|
|
1016
|
+
catch { /* non-blocking */ }
|
|
1017
|
+
}
|
|
1018
|
+
handleLLMError(error, runId) {
|
|
1019
|
+
try {
|
|
1020
|
+
const holder = this.spans.get(runId);
|
|
1021
|
+
if (!holder)
|
|
1022
|
+
return;
|
|
1023
|
+
if (holder.suppressionActive) {
|
|
1024
|
+
(0, helpers_1.resetFrameworkLlmActive)();
|
|
1025
|
+
(0, helpers_1.clearFrameworkParentContext)();
|
|
1026
|
+
}
|
|
1027
|
+
const errorType = error?.constructor?.name || '_OTHER';
|
|
1028
|
+
holder.span.setAttribute(semantic_convention_1.default.ERROR_TYPE, errorType);
|
|
1029
|
+
helpers_1.default.handleException(holder.span, error instanceof Error ? error : new Error(String(error)));
|
|
1030
|
+
this._endSpan(runId, String(error));
|
|
228
1031
|
}
|
|
229
1032
|
catch { /* non-blocking */ }
|
|
230
1033
|
}
|
|
231
|
-
// ----
|
|
232
|
-
|
|
1034
|
+
// ---- Tool Callbacks ----------------------------------------------------
|
|
1035
|
+
handleToolStart(tool, input, runId, parentRunId, _tags, metadata, kwargs) {
|
|
233
1036
|
try {
|
|
234
|
-
const
|
|
235
|
-
const
|
|
236
|
-
const
|
|
237
|
-
const
|
|
238
|
-
|
|
239
|
-
|
|
1037
|
+
const name = this._getNameFromCallback(tool, kwargs || {});
|
|
1038
|
+
const spanName = `execute_tool ${name}`;
|
|
1039
|
+
const span = this._createSpan(runId, parentRunId, spanName);
|
|
1040
|
+
const holder = this._newHolder(span, parentRunId);
|
|
1041
|
+
this.spans.set(runId, holder);
|
|
1042
|
+
if (parentRunId) {
|
|
1043
|
+
const parentResolved = this._resolveParentRunId(parentRunId);
|
|
1044
|
+
if (parentResolved && this.spans.has(parentResolved)) {
|
|
1045
|
+
this.spans.get(parentResolved).children.push(runId);
|
|
1046
|
+
}
|
|
1047
|
+
}
|
|
240
1048
|
span.setAttribute(semantic_convention_1.default.GEN_AI_PROVIDER_NAME_OTEL, semantic_convention_1.default.GEN_AI_SYSTEM_LANGCHAIN);
|
|
241
|
-
|
|
242
|
-
span.setAttribute(semantic_convention_1.default.
|
|
243
|
-
span.setAttribute(semantic_convention_1.default.
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
completionTokens: 0,
|
|
256
|
-
});
|
|
1049
|
+
this._setCommonAttributes(span, semantic_convention_1.default.GEN_AI_OPERATION_TYPE_TOOLS);
|
|
1050
|
+
span.setAttribute(semantic_convention_1.default.GEN_AI_TOOL_NAME, name);
|
|
1051
|
+
span.setAttribute(semantic_convention_1.default.GEN_AI_TOOL_TYPE_OTEL, 'function');
|
|
1052
|
+
span.setAttribute(semantic_convention_1.default.GEN_AI_TOOL_CALL_ID, runId);
|
|
1053
|
+
const description = tool?.description;
|
|
1054
|
+
if (description)
|
|
1055
|
+
span.setAttribute(semantic_convention_1.default.GEN_AI_TOOL_DESCRIPTION, String(description));
|
|
1056
|
+
const convId = resolveConversationId(metadata);
|
|
1057
|
+
if (convId)
|
|
1058
|
+
span.setAttribute(semantic_convention_1.default.GEN_AI_CONVERSATION_ID, convId);
|
|
1059
|
+
if (config_1.default.captureMessageContent && input) {
|
|
1060
|
+
span.setAttribute(semantic_convention_1.default.GEN_AI_TOOL_CALL_ARGUMENTS, String(input).slice(0, 2000));
|
|
1061
|
+
}
|
|
1062
|
+
(0, helpers_1.applyCustomSpanAttributes)(span);
|
|
257
1063
|
}
|
|
258
1064
|
catch { /* non-blocking */ }
|
|
259
1065
|
}
|
|
260
|
-
|
|
1066
|
+
handleToolEnd(output, runId) {
|
|
261
1067
|
try {
|
|
262
1068
|
const holder = this.spans.get(runId);
|
|
263
1069
|
if (!holder)
|
|
264
1070
|
return;
|
|
265
1071
|
const duration = (Date.now() - holder.startTime) / 1000;
|
|
266
1072
|
holder.span.setAttribute(semantic_convention_1.default.GEN_AI_CLIENT_OPERATION_DURATION, duration);
|
|
267
|
-
|
|
268
|
-
|
|
1073
|
+
// Extract tool_call_id from output if available
|
|
1074
|
+
if (output?.tool_call_id) {
|
|
1075
|
+
holder.span.setAttribute(semantic_convention_1.default.GEN_AI_TOOL_CALL_ID, output.tool_call_id);
|
|
269
1076
|
}
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
1077
|
+
if (config_1.default.captureMessageContent && output) {
|
|
1078
|
+
holder.span.setAttribute(semantic_convention_1.default.GEN_AI_TOOL_CALL_RESULT, String(output).slice(0, 2000));
|
|
1079
|
+
}
|
|
1080
|
+
this._endSpan(runId);
|
|
273
1081
|
}
|
|
274
1082
|
catch { /* non-blocking */ }
|
|
275
1083
|
}
|
|
276
|
-
|
|
1084
|
+
handleToolError(error, runId) {
|
|
1085
|
+
try {
|
|
1086
|
+
if (this.spans.has(runId)) {
|
|
1087
|
+
const span = this.spans.get(runId).span;
|
|
1088
|
+
const errorType = error?.constructor?.name || '_OTHER';
|
|
1089
|
+
span.setAttribute(semantic_convention_1.default.ERROR_TYPE, errorType);
|
|
1090
|
+
}
|
|
1091
|
+
this._endSpan(runId, String(error));
|
|
1092
|
+
}
|
|
1093
|
+
catch { /* non-blocking */ }
|
|
1094
|
+
}
|
|
1095
|
+
// ---- Retriever Callbacks -----------------------------------------------
|
|
1096
|
+
handleRetrieverStart(retriever, query, runId, parentRunId, _tags, metadata, kwargs) {
|
|
1097
|
+
try {
|
|
1098
|
+
const name = this._getNameFromCallback(retriever, kwargs || {});
|
|
1099
|
+
const spanName = `retrieval ${name}`;
|
|
1100
|
+
const span = this._createSpan(runId, parentRunId, spanName, api_1.SpanKind.CLIENT);
|
|
1101
|
+
const holder = this._newHolder(span, parentRunId);
|
|
1102
|
+
this.spans.set(runId, holder);
|
|
1103
|
+
if (parentRunId) {
|
|
1104
|
+
const parentResolved = this._resolveParentRunId(parentRunId);
|
|
1105
|
+
if (parentResolved && this.spans.has(parentResolved)) {
|
|
1106
|
+
this.spans.get(parentResolved).children.push(runId);
|
|
1107
|
+
}
|
|
1108
|
+
}
|
|
1109
|
+
span.setAttribute(semantic_convention_1.default.GEN_AI_PROVIDER_NAME_OTEL, semantic_convention_1.default.GEN_AI_SYSTEM_LANGCHAIN);
|
|
1110
|
+
this._setCommonAttributes(span, semantic_convention_1.default.GEN_AI_OPERATION_TYPE_RETRIEVE);
|
|
1111
|
+
span.setAttribute(semantic_convention_1.default.GEN_AI_DATA_SOURCE_ID, name);
|
|
1112
|
+
const convId = resolveConversationId(metadata);
|
|
1113
|
+
if (convId)
|
|
1114
|
+
span.setAttribute(semantic_convention_1.default.GEN_AI_CONVERSATION_ID, convId);
|
|
1115
|
+
if (config_1.default.captureMessageContent && query) {
|
|
1116
|
+
span.setAttribute(semantic_convention_1.default.GEN_AI_RETRIEVAL_QUERY_TEXT, String(query).slice(0, 2000));
|
|
1117
|
+
}
|
|
1118
|
+
(0, helpers_1.applyCustomSpanAttributes)(span);
|
|
1119
|
+
}
|
|
1120
|
+
catch { /* non-blocking */ }
|
|
1121
|
+
}
|
|
1122
|
+
handleRetrieverEnd(documents, runId) {
|
|
277
1123
|
try {
|
|
278
1124
|
const holder = this.spans.get(runId);
|
|
279
1125
|
if (!holder)
|
|
280
1126
|
return;
|
|
281
|
-
|
|
282
|
-
holder.span.
|
|
283
|
-
|
|
1127
|
+
const duration = (Date.now() - holder.startTime) / 1000;
|
|
1128
|
+
holder.span.setAttribute(semantic_convention_1.default.GEN_AI_CLIENT_OPERATION_DURATION, duration);
|
|
1129
|
+
holder.span.setAttribute(semantic_convention_1.default.GEN_AI_RETRIEVAL_DOCUMENT_COUNT, documents?.length || 0);
|
|
1130
|
+
if (config_1.default.captureMessageContent && documents?.length > 0) {
|
|
1131
|
+
const structured = documents.slice(0, 3).map((doc) => {
|
|
1132
|
+
const content = doc?.pageContent || doc?.page_content || String(doc);
|
|
1133
|
+
const entry = { content: String(content).slice(0, 2000) };
|
|
1134
|
+
const meta = doc?.metadata;
|
|
1135
|
+
if (meta) {
|
|
1136
|
+
const docId = meta.id || meta.source;
|
|
1137
|
+
if (docId)
|
|
1138
|
+
entry.id = String(docId);
|
|
1139
|
+
}
|
|
1140
|
+
return entry;
|
|
1141
|
+
});
|
|
1142
|
+
holder.span.setAttribute(semantic_convention_1.default.GEN_AI_RETRIEVAL_DOCUMENTS, JSON.stringify(structured));
|
|
1143
|
+
}
|
|
1144
|
+
this._endSpan(runId);
|
|
1145
|
+
}
|
|
1146
|
+
catch { /* non-blocking */ }
|
|
1147
|
+
}
|
|
1148
|
+
handleRetrieverError(error, runId) {
|
|
1149
|
+
try {
|
|
1150
|
+
if (this.spans.has(runId)) {
|
|
1151
|
+
const span = this.spans.get(runId).span;
|
|
1152
|
+
const errorType = error?.constructor?.name || '_OTHER';
|
|
1153
|
+
span.setAttribute(semantic_convention_1.default.ERROR_TYPE, errorType);
|
|
1154
|
+
}
|
|
1155
|
+
this._endSpan(runId, String(error));
|
|
1156
|
+
}
|
|
1157
|
+
catch { /* non-blocking */ }
|
|
1158
|
+
}
|
|
1159
|
+
// ---- Agent Callbacks ---------------------------------------------------
|
|
1160
|
+
handleAgentAction(action, runId) {
|
|
1161
|
+
try {
|
|
1162
|
+
const holder = this.spans.get(runId);
|
|
1163
|
+
if (!holder)
|
|
1164
|
+
return;
|
|
1165
|
+
holder.span.setAttribute(semantic_convention_1.default.GEN_AI_OPERATION, semantic_convention_1.default.GEN_AI_OPERATION_TYPE_AGENT);
|
|
1166
|
+
if (config_1.default.captureMessageContent) {
|
|
1167
|
+
const tool = action?.tool ?? String(action);
|
|
1168
|
+
const toolInput = action?.tool_input ?? '';
|
|
1169
|
+
const log = action?.log ?? '';
|
|
1170
|
+
holder.span.setAttribute(semantic_convention_1.default.GEN_AI_AGENT_ACTION_TOOL, String(tool).slice(0, 2000));
|
|
1171
|
+
holder.span.setAttribute(semantic_convention_1.default.GEN_AI_AGENT_ACTION_TOOL_INPUT, String(toolInput).slice(0, 2000));
|
|
1172
|
+
if (log)
|
|
1173
|
+
holder.span.setAttribute(semantic_convention_1.default.GEN_AI_AGENT_ACTION_LOG, String(log).slice(0, 2000));
|
|
1174
|
+
}
|
|
1175
|
+
}
|
|
1176
|
+
catch { /* non-blocking */ }
|
|
1177
|
+
}
|
|
1178
|
+
handleAgentFinish(finish, runId) {
|
|
1179
|
+
try {
|
|
1180
|
+
const holder = this.spans.get(runId);
|
|
1181
|
+
if (!holder)
|
|
1182
|
+
return;
|
|
1183
|
+
if (config_1.default.captureMessageContent) {
|
|
1184
|
+
const output = finish?.return_values ?? String(finish);
|
|
1185
|
+
const log = finish?.log ?? '';
|
|
1186
|
+
holder.span.setAttribute(semantic_convention_1.default.GEN_AI_AGENT_FINISH_OUTPUT, String(output).slice(0, 2000));
|
|
1187
|
+
if (log)
|
|
1188
|
+
holder.span.setAttribute(semantic_convention_1.default.GEN_AI_AGENT_FINISH_LOG, String(log).slice(0, 2000));
|
|
1189
|
+
}
|
|
284
1190
|
}
|
|
285
1191
|
catch { /* non-blocking */ }
|
|
286
1192
|
}
|
|
287
1193
|
}
|
|
288
|
-
|
|
289
|
-
//
|
|
290
|
-
//
|
|
1194
|
+
exports.OpenLITCallbackHandler = OpenLITCallbackHandler;
|
|
1195
|
+
// ---------------------------------------------------------------------------
|
|
1196
|
+
// Wrapper factory that patches CallbackManager._configureSync
|
|
1197
|
+
// ---------------------------------------------------------------------------
|
|
291
1198
|
class LangChainWrapper extends base_wrapper_1.default {
|
|
292
1199
|
static _patchConfigure(tracer) {
|
|
293
|
-
// Ensure singleton handler
|
|
294
1200
|
if (!handlerInstance) {
|
|
295
1201
|
handlerInstance = new OpenLITCallbackHandler(tracer);
|
|
296
1202
|
}
|
|
297
1203
|
const handler = handlerInstance;
|
|
1204
|
+
/**
|
|
1205
|
+
* The LLM call itself must run inside runWithFrameworkLlm() so that
|
|
1206
|
+
* provider wrappers (OpenAI, Anthropic, etc.) skip their own span
|
|
1207
|
+
* creation. We achieve this by wrapping _configureSync: when
|
|
1208
|
+
* LangChain configures callbacks for an LLM call, the callback
|
|
1209
|
+
* handler's handleChatModelStart/handleLLMStart already sets the
|
|
1210
|
+
* suppression flag via the span holder. But to actually suppress
|
|
1211
|
+
* the provider wrapper, we need the flag in the AsyncLocalStorage
|
|
1212
|
+
* context of the LLM call. Since LangChain doesn't give us a
|
|
1213
|
+
* hook around the actual LLM invocation, we rely on the provider
|
|
1214
|
+
* wrappers checking isFrameworkLlmActive() which is set during
|
|
1215
|
+
* the configure phase and active throughout the call chain.
|
|
1216
|
+
*/
|
|
298
1217
|
return (originalConfigure) => {
|
|
299
1218
|
return function (inheritableHandlers, ...rest) {
|
|
300
|
-
// inheritableHandlers can be:
|
|
301
|
-
// - undefined : no handlers passed
|
|
302
|
-
// - Array : list of handler objects (chat model calls)
|
|
303
|
-
// - CallbackManager instance: already-built manager (Runnable/chain calls)
|
|
304
1219
|
if (Array.isArray(inheritableHandlers) || !inheritableHandlers) {
|
|
305
1220
|
const handlers = inheritableHandlers ? [...inheritableHandlers] : [];
|
|
306
1221
|
if (!handlers.some((h) => h?.name === 'openlit_callback_handler')) {
|
|
@@ -309,7 +1224,6 @@ class LangChainWrapper extends base_wrapper_1.default {
|
|
|
309
1224
|
return originalConfigure.call(this, handlers, ...rest);
|
|
310
1225
|
}
|
|
311
1226
|
else {
|
|
312
|
-
// Existing CallbackManager — add our handler directly
|
|
313
1227
|
const cbManager = inheritableHandlers;
|
|
314
1228
|
if (cbManager?.handlers && !cbManager.handlers.some((h) => h?.name === 'openlit_callback_handler')) {
|
|
315
1229
|
cbManager.addHandler(handler, true);
|