Skip to content

Commit c493d82

Browse files
fpfp100jsl517claude
authored
Add OTEL gen-ai message format for auto-instrumented message tracing (#231)
* Add OTEL gen-ai message format support for auto-instrumented input/output message tracing - OpenAI extension: Auto-record structured InputMessages/OutputMessages on response, generation, and function spans via OpenAIAgentsTraceProcessor with isContentRecordingEnabled option - LangChain extension: Auto-record structured InputMessages/OutputMessages on chat, invoke_agent, and execute_tool spans via LangChainTracer with isContentRecordingEnabled option - LangChain v1 support: Fix setToolAttributes to handle plain string tool results (v1 format), fix tool_call_id extraction from inputs, fix setSystemInstructionsAttribute to use getMessageType() for all message formats, fix setTokenAttributes to check last message for usage_metadata, fix getModel to check v1 direct response_metadata path - Add message-schema-validator test helper for validating OTEL gen-ai message contract - Add LangChainMessageContract and OpenAIMessageContract test suites Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * Address code review findings (CRM-001 through CRM-013) - CRM-001: Move GEN_AI_TOOL_TYPE_KEY setAttribute outside forEach loop - CRM-002: Wrap string output/input fallbacks in versioned OTEL message envelope - CRM-003: Support tokenUsage shape (promptTokens/completionTokens) in setTokenAttributes - CRM-009: Remove unused getAttributesFromInput export - CRM-010: Replace dead extractMessageContent with extractStringContent delegating to extractRawContent - CRM-011: Fix self-referential comment - CRM-006: Add direct v1 constructor format tests for input/output messages - CRM-007: Add response span content-gating suppression test - CRM-008: Add empty array and null entry edge case tests - CRM-013: Add function_call, input_file, malformed JSON, and unknown block type tests - Fix getMessageType to skip v1 'constructor' type and check id array for message class Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * Remove isContentRecordingEnabled gating, remove truncateValue from extensions, align tool serialization - Remove isContentRecordingEnabled toggle: content recording is now ON by default (aligned with Python/.NET SDKs). suppressInvokeAgentInput remains. - Remove truncateValue from both extensions: truncation now happens at the span/exporter level in core, not pre-truncated in extensions. - Use safeSerializeToJson for tool arguments/results in both extensions, matching the core ExecuteToolScope pattern from PR #228. - Export safeSerializeToJson from core index.ts. - Add tool argument validation tests for both OpenAI and LangChain extensions. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * Address PR review: fix output fallback, input_image part type, stale JSDoc - processResponseSpanData: add else fallback for non-array output via wrapRawContentAsOutputMessages (handles object output) - mapInputContentBlock: map input_image to type 'blob' (matches BlobPart contract) instead of non-standard 'image' - Remove stale @param options JSDoc from LangChainTraceInstrumentor.instrument Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * Use MAX_SPAN_SIZE_BYTES constant for truncation, export from observability, add missing expect import - Replace hardcoded 1024 with MAX_SPAN_SIZE_BYTES in OpenAI extensions Utils - Export MAX_SPAN_SIZE_BYTES from observability package - Add missing `import { expect } from '@jest/globals'` to test helper Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Peng Fan <pefan@microsoft.com> Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent c3803eb commit c493d82

File tree

13 files changed

+1262
-275
lines changed

13 files changed

+1262
-275
lines changed

packages/agents-a365-observability-extensions-langchain/src/LangChainTraceInstrumentor.ts

Lines changed: 9 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -20,9 +20,8 @@ class LangChainTraceInstrumentorImpl extends InstrumentationBase<Instrumentation
2020
private _hasBeenEnabled = false;
2121
private _isPatched = false;
2222
protected otelTracer: Tracer;
23-
private isContentRecordingEnabled: boolean;
2423

25-
private constructor(options?: { isContentRecordingEnabled?: boolean }) {
24+
private constructor() {
2625
if (LangChainTraceInstrumentorImpl._instance !== null) {
2726
throw new Error("LangChainTraceInstrumentor can only be instantiated once.");
2827
}
@@ -42,15 +41,14 @@ class LangChainTraceInstrumentorImpl extends InstrumentationBase<Instrumentation
4241
"agent365-langchain",
4342
"1.0.0"
4443
);
45-
this.isContentRecordingEnabled = options?.isContentRecordingEnabled ?? false;
4644

4745
LangChainTraceInstrumentorImpl._instance = this;
4846
logger.info("[LangChainTraceInstrumentor] Initialized and automatically enabled");
4947
}
5048

51-
static getInstance(options?: { isContentRecordingEnabled?: boolean }): LangChainTraceInstrumentorImpl {
49+
static getInstance(): LangChainTraceInstrumentorImpl {
5250
if (!LangChainTraceInstrumentorImpl._instance) {
53-
LangChainTraceInstrumentorImpl._instance = new LangChainTraceInstrumentorImpl(options);
51+
LangChainTraceInstrumentorImpl._instance = new LangChainTraceInstrumentorImpl();
5452
}
5553
return LangChainTraceInstrumentorImpl._instance;
5654
}
@@ -104,7 +102,7 @@ class LangChainTraceInstrumentorImpl extends InstrumentationBase<Instrumentation
104102
this: CallbackManagerModuleType,
105103
...args: Parameters<typeof CallbackManager["_configureSync"]>
106104
) {
107-
args[0] = addTracerToHandlers(instrumentor.otelTracer, args[0], { isContentRecordingEnabled: instrumentor.isContentRecordingEnabled });
105+
args[0] = addTracerToHandlers(instrumentor.otelTracer, args[0]);
108106
logger.info("[LangChainTraceInstrumentor] _configureSync wrapped to add LangChainTracer");
109107
return original.apply(this, args);
110108
};
@@ -154,10 +152,9 @@ export class LangChainTraceInstrumentor {
154152
/**
155153
* Initialize and auto-instrument for LangChain
156154
* @param module The CallbackManager module to instrument
157-
* @param options Optional configuration options
158155
*/
159-
static instrument(module: CallbackManagerModuleType, options?: { isContentRecordingEnabled?: boolean }): void {
160-
LangChainTraceInstrumentorImpl.getInstance(options).manuallyInstrumentImpl(module);
156+
static instrument(module: CallbackManagerModuleType): void {
157+
LangChainTraceInstrumentorImpl.getInstance().manuallyInstrumentImpl(module);
161158
}
162159

163160
/**
@@ -191,21 +188,20 @@ export class LangChainTraceInstrumentor {
191188
export function addTracerToHandlers(
192189
tracer: Tracer,
193190
handlers: CallbackManagerModule.Callbacks | undefined,
194-
options?: { isContentRecordingEnabled?: boolean }
195191
): CallbackManagerModule.Callbacks {
196192
if (handlers == null) {
197-
return [new LangChainTracer(tracer, options)];
193+
return [new LangChainTracer(tracer)];
198194
}
199195

200196
if (Array.isArray(handlers)) {
201197
if (!handlers.some((h) => h instanceof LangChainTracer)) {
202-
handlers.push(new LangChainTracer(tracer, options));
198+
handlers.push(new LangChainTracer(tracer));
203199
}
204200
return handlers;
205201
}
206202

207203
if (!handlers.inheritableHandlers.some((h) => h instanceof LangChainTracer)) {
208-
handlers.addHandler(new LangChainTracer(tracer, options), true);
204+
handlers.addHandler(new LangChainTracer(tracer), true);
209205
}
210206
return handlers;
211207
}

0 commit comments

Comments
 (0)