Skip to content

Update message format to align with OTel spec#256

Open
fpfp100 wants to merge 3 commits into
mainfrom
users/pefan/otel-message-format
Open

Update message format to align with OTel spec#256
fpfp100 wants to merge 3 commits into
mainfrom
users/pefan/otel-message-format

Conversation

@fpfp100
Copy link
Copy Markdown
Contributor

@fpfp100 fpfp100 commented May 21, 2026

Summary

  • Remove the versioned { version: "0.1.0", messages: [...] } envelope from message serialization — messages now serialize as a plain JSON array [...] per OTel gen-ai semantic conventions
  • Default finish_reason to "stop" on output messages created from plain strings via toOutputMessages/normalizeOutputMessages
  • Tighten isWrappedMessages type guard with Array.isArray check to prevent false positives

Port of microsoft/Agent365-dotnet#253.

Test plan

  • All 1270 unit tests pass
  • Build succeeds (CJS + ESM)
  • Lint clean
  • Verify integration tests pass in CI

🤖 Generated with Claude Code

Remove the versioned wrapper `{ version, messages }` envelope from message
serialization. Messages now serialize as a plain JSON array per OTel gen-ai
semantic conventions. Output messages default finish_reason to "stop" when
created from plain strings via toOutputMessages/normalizeOutputMessages.

Port of microsoft/Agent365-dotnet#253.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings May 21, 2026 21:10
@fpfp100 fpfp100 requested a review from a team as a code owner May 21, 2026 21:10
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR updates the observability message serialization format to match OpenTelemetry gen-ai semantic conventions by removing the versioned { version, messages } envelope and emitting a plain JSON array of messages, while also defaulting finish_reason to "stop" for output messages created from plain strings and tightening wrapper detection.

Changes:

  • Serialize gen_ai.{input,output}.messages as a plain JSON array instead of a versioned wrapper object.
  • Default finish_reason to "stop" for output messages produced from string / string[] inputs.
  • Update wrapper type guards, contracts, exporter truncation logic, and tests to reflect the new format.

Reviewed changes

Copilot reviewed 22 out of 22 changed files in this pull request and generated 4 comments.

Show a summary per file
File Description
tests/observability/tracing/message-utils.test.ts Updates unit tests for wrapper detection, normalization, and array-based serialization; adds finish_reason expectations.
tests/observability/integration/openai-agent-instrument.test.ts Adjusts integration assertions to expect JSON arrays for input/output message attributes.
tests/observability/integration/langchain-agent-instrument.test.ts Adjusts integration assertions to expect JSON arrays for input/output message attributes.
tests/observability/integration/helpers/span-validators.ts Updates span validators to treat message attributes as arrays (not {version,messages}).
tests/observability/extension/openai/OpenAIMessageContract.test.ts Updates OpenAI extension contract tests to index into the serialized array.
tests/observability/extension/openai/OpenAIAgentsTraceProcessor.test.ts Updates processor tests to validate array serialization and indexing.
tests/observability/extension/langchain/LangChainObservabilityAttributes.test.ts Updates LangChain extension attribute assertions to expect array JSON.
tests/observability/extension/langchain/LangChainMessageContract.test.ts Updates LangChain message contract tests for array JSON format.
tests/observability/extension/hosting/scope-utils.test.ts Updates hosting scope tests to expect array serialization for input messages.
tests/observability/extension/hosting/output-logging-middleware.test.ts Updates middleware tests to expect array output messages plus default finish_reason.
tests/observability/extension/helpers/message-schema-validator.ts Updates shared validator to validate “plain array of messages” payloads.
tests/observability/core/scopes.test.ts Updates core scope tests to validate array-based attributes and finish_reason defaults.
tests/observability/core/scope-messages.test.ts Updates scope message recording tests for array format and finish_reason defaults.
tests/observability/core/output-scope.test.ts Updates OutputScope tests to validate array output messages and finish_reason defaults.
tests/observability/core/agent365-exporter.test.ts Updates exporter truncation tests to validate array-based message attributes.
packages/agents-a365-observability/src/tracing/scopes/OutputScope.ts Removes versioned wrapping and refines raw-dict detection for output message recording.
packages/agents-a365-observability/src/tracing/message-utils.ts Implements array serialization, default finish_reason, and tighter wrapper guard.
packages/agents-a365-observability/src/tracing/exporter/utils.ts Updates truncation logic to parse/flush message attributes as arrays and adjust overflow sentinel generation.
packages/agents-a365-observability/src/tracing/contracts.ts Removes version from message wrapper contracts and introduces DEFAULT_FINISH_REASON.
packages/agents-a365-observability/src/index.ts Updates public exports to expose DEFAULT_FINISH_REASON instead of the schema version constant.
packages/agents-a365-observability-extensions-openai/src/Utils.ts Updates OpenAI extension helpers to build wrappers without version and default finish_reason for assistant.
packages/agents-a365-observability-extensions-langchain/src/Utils.ts Updates LangChain extension to wrap messages without version before serializing.

Comment on lines 8 to 11
InputMessages,
OutputMessages,
A365_MESSAGE_SCHEMA_VERSION,
DEFAULT_FINISH_REASON,
} from '@microsoft/agents-a365-observability';
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in 3854c7d — removed the unused import.

Comment on lines +91 to +92
],
finish_reason: 'error'
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good catch. Fixed in 3854c7d — removed \ from the serialization fallback since it's used for both input and output attributes.

Comment on lines +251 to +252
],
finish_reason: 'error'
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in 3854c7d — removed finish_reason from the overflow sentinel for the same reason.

Comment on lines 435 to +451
export interface InputMessages {
version: typeof A365_MESSAGE_SCHEMA_VERSION;
messages: ChatMessage[];
}
/**
* An output message produced by a model (OTEL gen-ai semantic conventions).
* `finish_reason` defaults to `"stop"` per OTel spec when not provided.
*/
export interface OutputMessage extends ChatMessage {
finish_reason?: FinishReason | string;
}

export interface OutputMessages {
version: typeof A365_MESSAGE_SCHEMA_VERSION;
messages: OutputMessage[];
}

export const A365_MESSAGE_SCHEMA_VERSION = '0.1.0' as const;
/** Default finish reason applied when none is provided (per OTel spec). */
export const DEFAULT_FINISH_REASON = 'stop' as const;
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Intentional breaking change — matches the dotnet approach in microsoft/Agent365-dotnet#253, which also deleted MessageConstants.SchemaVersion and the Version property outright with no deprecation shim.

jsl517 and others added 2 commits May 21, 2026 15:35
- Remove unused DEFAULT_FINISH_REASON import from test file
- Remove finish_reason from serialization fallback and overflow sentinel
  (finish_reason is output-only per OTel spec, sentinels are used for
  both input and output attributes)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The "validate error.type on failing tool" test was flaky because it used
waitForSpans with a generic count of 2, which could resolve before the
tool span was emitted. Now polls specifically for the execute_tool span
by name with a 15s timeout.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings May 21, 2026 23:07
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 22 out of 22 changed files in this pull request and generated 3 comments.

Comment on lines 19 to 21
export function isWrappedMessages(input: InputMessagesParam | OutputMessagesParam): input is InputMessages | OutputMessages {
return !Array.isArray(input) && typeof input === 'object' && input !== null && 'version' in input && 'messages' in input;
return !Array.isArray(input) && typeof input === 'object' && input !== null && 'messages' in input && Array.isArray((input as InputMessages).messages);
}
Comment on lines 106 to 110
private _isRawDict(messages: ResponseMessagesParam): messages is Record<string, unknown> {
return typeof messages === 'object' && messages !== null
&& !Array.isArray(messages)
&& !('version' in messages && 'messages' in messages);
&& !('messages' in messages && Array.isArray((messages as OutputMessages).messages));
}
return {
version: A365_MESSAGE_SCHEMA_VERSION,
messages: [{ role, parts: [{ type: 'text', content }] }],
messages: [{ role, parts: [{ type: 'text', content }], finish_reason: role === MessageRole.ASSISTANT ? DEFAULT_FINISH_REASON : undefined }],
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants