Skip to content

feat(mapper): added framework detection for traces from CloudWatch#164

Merged
poshinchen merged 1 commit intostrands-agents:mainfrom
poshinchen:feat/mappers-cloudwatch-change
Mar 20, 2026
Merged

feat(mapper): added framework detection for traces from CloudWatch#164
poshinchen merged 1 commit intostrands-agents:mainfrom
poshinchen:feat/mappers-cloudwatch-change

Conversation

@poshinchen
Copy link
Contributor

@poshinchen poshinchen commented Mar 16, 2026

Description

Users could have non-Strands traces in CloudWatch (via the collector). Our current CloudWatchProvider is using CloudWatchSessionMapper which only works for Strands.

Since we're going to have the mapper which is "framework / convention specific", we will deprecate CloudWatchSessionMapper and route the traces / spans to the correct mapper via detect_otel_mapper method.

What has been changed

  • CloudWatchProvider now queries aws/spans log group for span hierarchy (spanId → parentSpanId), enriching event records that previously had parent_span_id: None for all spans.
    This is a best-effort enrichment — failure falls back gracefully.
  • LangChainOtelSessionMapper — ADOT span type detection fixed:
    • Inference spans: role == "unknown" with raw string content
    • Tool execution spans: input_str in parsed body (skips tool_call_with_context duplicates)
    • Agent invocation spans: kwargs.name == "LangGraph"
  • Multi-agent AgentInvocationSpan dedup:
    • In multi-agent LangGraph systems, each nested sub-graph produces its own LangGraph record. Only the last (root graph) is kept, matching
      live/Traceloop behavior.
  • Fixed leaking mutable state:
    • _trace_tools_map replaced with local trace_tools dict. Added per-call caches (_span_messages_cache, _adot_body_cache) reset each map_to_session()
      call.
  • available_tools back-fill: When llm.request.functions.* attributes are missing (ADOT path), builds ToolConfig from converted ToolExecutionSpans.
  • Fixed AgentInvocationSpan creation: if not user_query → if user_query is None (empty string is valid). _get_last_message_text() now skips tool messages and searches backward
    for non-empty text.
  • Fixed ADOT double-encoded empty strings: Raw "" (2-char literal) decoded properly, empty assistant messages now return None instead of AssistantMessage(content=[]).
  • Extracted hard-coded strings into module-level constants (ATTR, KIND, ADOT*).
  • Cleanup: Removed unused log_group parameter from _run_logs_insights_query, removed dead _ATTR_GEN_AI_PREFIX constant, fixed tool_status default from "" to None.

Tested

  • TestMapperAutoDetection — verifies auto-detection for Strands, LangChain (OpenLLMetry), OpenInference scopes, explicit mapper override, and default None mapper
  • TestScopePreservation — verifies CloudWatchLogsParser preserves scope.name for LangChain (OpenLLMetry), OpenInference, Strands, spans with events, and synthetic spans
  • Existing TestSpanConversion and TestSessionBuilding updated to use pytest fixtures (deprecation warning compat)
  • Tested and compared the mapped sessions using langchain/traceloop, and in_memory_exporter langchain/traceloop.

Related Issues

#91

Documentation PR

N/A

Type of Change

Bug fix
Breaking change

Testing

How have you tested the change? Verify that the changes do not break functionality or introduce warnings in consuming repositories: agents-docs, agents-tools, agents-cli

  • I ran hatch run prepare

Checklist

  • I have read the CONTRIBUTING document
  • I have added any necessary tests that prove my fix is effective or my feature works
  • I have updated the documentation accordingly
  • I have added an appropriate example to the documentation to outline the feature, or no new docs are needed
  • My changes generate no new warnings
  • Any dependent changes have been merged and published

By submitting this pull request, I confirm that you can use, modify, copy, and redistribute this contribution, under the terms of your choice.

@afarntrog
Copy link
Contributor

I would like to see a real world successful run - can just be pasted in the comments here

@poshinchen poshinchen force-pushed the feat/mappers-cloudwatch-change branch from e62d5c9 to 14047a7 Compare March 17, 2026 20:23
@poshinchen poshinchen force-pushed the feat/mappers-cloudwatch-change branch from 14047a7 to 125907d Compare March 19, 2026 19:20
@poshinchen poshinchen force-pushed the feat/mappers-cloudwatch-change branch from 125907d to c3ad181 Compare March 19, 2026 19:33
@poshinchen poshinchen force-pushed the feat/mappers-cloudwatch-change branch from c3ad181 to 2f34e9c Compare March 19, 2026 19:53
Copy link
Contributor

@afarntrog afarntrog left a comment

Choose a reason for hiding this comment

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

Since this is lots of parsing logic I would like to see some robust test that pass in JSON files of what we would expect to get from a log insights query and pass that through this parsing logic etc. We should have a bunch of these for different cases.

In addition, more for later, I would like to see integration tests that run through a real scenario so that we can detect early on if something in our parsing logic is broken/needs updating.

@poshinchen poshinchen merged commit 7d7ed26 into strands-agents:main Mar 20, 2026
13 checks passed
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.

2 participants