Skip to content

Conversation

@Steffen911
Copy link
Member

@Steffen911 Steffen911 commented Sep 29, 2025

Replaces #1349


Important

Introduces context propagation for session, user, and metadata attributes using OpenTelemetry, deprecating older methods, with comprehensive tests added.

  • Behavior:
    • Adds context managers with_attributes() in client.py for session, user, and metadata propagation.
    • Deprecates update_current_trace() and update_trace() in client.py and span.py.
    • Implements attribute propagation in LangfuseSpanProcessor.on_start() in span_processor.py.
  • Constants:
    • Adds LANGFUSE_CTX_USER_ID, LANGFUSE_CTX_SESSION_ID, LANGFUSE_CTX_METADATA in constants.py.
  • Tests:
    • Adds tests for context manager propagation in test_core_sdk.py.
    • Validates propagation for nested contexts, baggage, and metadata.

This description was created by Ellipsis for d210b49. You can customize this summary. It will automatically update as commits are pushed.


Disclaimer: Experimental PR review

Greptile Overview

Updated On: 2025-09-30 09:33:53 UTC

Summary

This review covers only the changes made since the last review, not the entire PR.

This PR introduces a comprehensive context propagation system that automatically propagates trace attributes (session_id, user_id, metadata) from parent contexts to child spans using OpenTelemetry's context and baggage mechanisms. The implementation adds three new context managers (session(), user(), metadata()) to both the main Langfuse client and individual span objects, enabling automatic attribute inheritance without requiring manual specification on each child span.

The core architecture leverages OpenTelemetry context variables for local propagation within a single service and optional baggage for cross-service distributed tracing. When a context manager is entered, it sets the appropriate context variables and optionally baggage entries. The LangfuseSpanProcessor.on_start() method then automatically applies these propagated attributes to new spans during creation.

The implementation also deprecates the older update_current_trace() and update_trace() methods with proper deprecation warnings, indicating a shift toward this more systematic context-based approach. The feature integrates well with the existing OpenTelemetry-based architecture and follows OpenTelemetry best practices for context propagation.

Comprehensive test coverage validates the propagation behavior across various scenarios including nested contexts, baggage propagation for cross-service use cases, and both client-level and span-level context managers. The tests demonstrate proper attribute inheritance and context cleanup behavior.

Important Files Changed

Changed Files
Filename Score Overview
langfuse/_client/client.py 4/5 Adds three new context managers (session, user, metadata) for automatic trace attribute propagation and deprecates update_current_trace method
langfuse/_client/span.py 4/5 Adds identical context managers to span objects and deprecates update_trace method - well-structured implementation
langfuse/_client/span_processor.py 5/5 Implements attribute propagation logic in on_start method with proper dictionary key checking (bugs from previous review have been fixed)
tests/test_core_sdk.py 5/5 Comprehensive test coverage for new context manager functionality with proper validation of attribute propagation across multiple scenarios

Confidence score: 5/5

  • This PR is safe to merge with minimal risk as the critical bugs identified in previous reviews have been resolved
  • Score reflects the comprehensive implementation, thorough testing, and proper integration with OpenTelemetry standards
  • No files require special attention as the attribute checking logic has been corrected

@Steffen911 Steffen911 marked this pull request as draft September 29, 2025 17:57
Copy link
Contributor

@greptile-apps greptile-apps bot left a comment

Choose a reason for hiding this comment

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

4 files reviewed, 2 comments

Edit Code Review Agent Settings | Greptile

@Steffen911 Steffen911 marked this pull request as ready for review September 30, 2025 09:32
Copy link
Contributor

@greptile-apps greptile-apps bot left a comment

Choose a reason for hiding this comment

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

4 files reviewed, no comments

Edit Code Review Agent Settings | Greptile


@_agnosticcontextmanager
def session(
self, id: str, *, as_baggage: bool = False
Copy link
Contributor

@hassiebp hassiebp Oct 1, 2025

Choose a reason for hiding this comment

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

I think we should rename as_baggage to something more immediately descriptive on what the impact is. Something like propagate_via_http makes it both clear that this is propagation related and comes with security considerations (users might be unfamiliar with OTEL in general and baggage in particular). We could also consider dropping this entirely and only allow enabling it via env var only as users probably won't dynamically decide in runtime whether to use baggage or not

Copy link
Contributor

Choose a reason for hiding this comment

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

For propagation, we also need to consider / test the multiprocessing case in Python, where users use Celery workers that are separate Python processes to handle background tasks. Trace attributes must be propagated there, too

Copy link
Member Author

Choose a reason for hiding this comment

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

@hassiebp Test langchain

@philipbjorge
Copy link

Thank you for putting this PR together @Steffen911 -- This is exactly what we need to reliably attribute users/organization ids to all our spans 👍

f"Metadata values must be strings, got {type(v)} for key '{k}'"
)
del metadata[k]
if len(v) > 200:

Choose a reason for hiding this comment

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

We ran into an error here in production where v is None and this length check failed.

  File "/app/documents_parser/telemetry/middleware.py", line 41, in dispatch
    with client.with_attributes(
         ^^^^^^^^^^^^^^^^^^^^^^^
  File "/app/.venv/lib/python3.12/site-packages/opentelemetry/util/_decorator.py", line 61, in __enter__
    return next(self.gen)  # type: ignore
           ^^^^^^^^^^^^^^
  File "/app/.venv/lib/python3.12/site-packages/langfuse/_client/client.py", line 440, in with_attributes
    if len(v) > 200:
       ^^^^^^
TypeError: object of type 'NoneType' has no len()

Choose a reason for hiding this comment

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

It's a bug on our side, but I think the package should probably warn and fail gracefully.

host
if host is not None
else os.environ.get(LANGFUSE_HOST, "https://cloud.langfuse.com")
)
Copy link
Member

Choose a reason for hiding this comment

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

The original version is more readableand to my understanding they do the same. If this is true, should we revert this?

Copy link
Contributor

Choose a reason for hiding this comment

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

yes, they are both equivalent

metadata (dict): Additional metadata to associate with all spans in the context. Values must be strings and are truncated to 200 characters.
as_baggage (bool, optional): If True, stores the values in OpenTelemetry baggage
for cross-service propagation. If False, stores only in local context for
current-service propagation. Defaults to False.
Copy link
Member

@maxdeichmann maxdeichmann Oct 7, 2025

Choose a reason for hiding this comment

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

We should add a sentence here that this will be added to the headers of all outgoing HTTP requests. I would add the warning from below up here as not everyone is reading everything.

# Process metadata
if metadata is not None:
# Truncate values with size > 200 to 200 characters and emit warning including the ky
for k, v in metadata.items():
Copy link
Member

Choose a reason for hiding this comment

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

Should we also have an overall limit fo e.g. 4k characters?

Copy link
Member Author

Choose a reason for hiding this comment

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

@maxdeichmann IMO that would be complicated to enforce and eventually, this is an issue that will surface quickly on the client side. I would highlight it in the documentation, but don't complicate the happy path further.

Copy link
Member

Choose a reason for hiding this comment

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

Well the risk is here that our users run into failing APIs as the header is too large to be processed. I thought we could add a simple logic such as: keeping a count of added characters to the header. If threshhold exceeded, stop adding key/values to the metadata.

self,
session_id: Optional[str] = None,
user_id: Optional[str] = None,
metadata: Optional[dict[str, str]] = None,
Copy link
Member Author

Choose a reason for hiding this comment

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

Should we highlight somehow that this metadata is only to be used if it should be set on all children? I'm wondering whether people understand and make use of the difference for setting metadata on current span vs all spans?

Copy link
Member

Choose a reason for hiding this comment

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

Yes, I agree with the sentiment! Options:

  • with_trace_attributes
  • with_context_attributes
  • with_propagated_attributes

...
Lets quickly talk sync about this. Nailing this is key.

Copy link
Contributor

Choose a reason for hiding this comment

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

We should think about this from the perspective on how this is consumed, namely via context manager. so the 'with' is already there.

from langfuse import get_client

langfuse = get_client()

...

with langfuse.correlation_context(session_id='my-session'):
    # do stuff
    pass

I prefer correlation_context or set_correlation_context as it makes sure that

  • this is not necessarily related to a 'trace'
  • this has some propagation included ('context')
  • is used for aggregating / correlating multiple spans ('correlation')

Copy link
Member

Choose a reason for hiding this comment

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

  • propagation_context
  • propagated_context
  • distributed_context
  • trace_scope
  • trace_context
  • shared_attributes

Copy link
Member

maxdeichmann commented Oct 7, 2025

@Steffen911 lets talk about function naming of the new functions and then we are good to go here. Marc should also see them.

@hassiebp
Copy link
Contributor

hassiebp commented Oct 9, 2025

Docs required

max length for trace metadata

ascii-us characters required https://opentelemetry.io/docs/specs/otel/context/api-propagators/?utm_source=chatgpt.com#textmap-propagator

@hassiebp hassiebp self-assigned this Oct 9, 2025
@hassiebp hassiebp changed the base branch from main to add-attribute-propagation October 21, 2025 08:11
@hassiebp hassiebp merged commit d3521ef into add-attribute-propagation Oct 21, 2025
7 of 11 checks passed
@hassiebp hassiebp deleted the steffen/nested-context-propagation branch October 21, 2025 08:11
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.

5 participants