Skip to content

Commit 58b0dd1

Browse files
committed
Use OpenTelemetry enduser.id attribute for Langfuse user tracking
- Remove broken Langfuse SDK trace() call (method doesn't exist) - Add user_id via OpenTelemetry semantic convention 'enduser.id' - Langfuse will automatically extract user info from OTLP spans - Cleaner, standards-based approach using OTLP exclusively - Fixes AttributeError: 'Langfuse' object has no attribute 'trace' Signed-off-by: sallyom <somalley@redhat.com>
1 parent e238e6e commit 58b0dd1

File tree

1 file changed

+26
-27
lines changed

1 file changed

+26
-27
lines changed

components/runners/claude-code-runner/wrapper.py

Lines changed: 26 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -191,9 +191,14 @@ async def run(self):
191191
async def _run_claude_agent_sdk(self, prompt: str):
192192
"""Execute the Claude Code SDK with the given prompt."""
193193
try:
194+
# Extract user context for observability (will be added to OTLP spans)
195+
user_id = os.getenv('USER_ID', '').strip()
196+
user_name = os.getenv('USER_NAME', '').strip()
197+
194198
# Initialize Langfuse for observability if configured
195199
langfuse_client = None
196200
langfuse_session_span = None
201+
langfuse_enabled = False
197202
if LANGFUSE_AVAILABLE:
198203
langfuse_enabled = os.getenv('LANGFUSE_ENABLED', '').strip().lower() in ('1', 'true', 'yes')
199204
if langfuse_enabled:
@@ -204,33 +209,15 @@ async def _run_claude_agent_sdk(self, prompt: str):
204209
host=os.getenv('LANGFUSE_HOST')
205210
)
206211

207-
# Extract user context for observability
208-
user_id = os.getenv('USER_ID', '').strip()
209-
user_name = os.getenv('USER_NAME', '').strip()
210-
211-
# Create a trace for this session with userId (Langfuse SDK 3.x)
212-
# Pass user_id directly to trace() for proper user tracking
213-
langfuse_session_span = langfuse_client.trace(
214-
name="claude_agent_session",
215-
input={"prompt": prompt},
216-
user_id=user_id if user_id else None,
217-
metadata={
218-
"session_id": self.context.session_id,
219-
"namespace": self.context.get_env('AGENTIC_SESSION_NAMESPACE', 'unknown'),
220-
"user_name": user_name if user_name else None,
221-
},
222-
)
223-
224212
if user_id:
225-
logging.info(f"Langfuse: Tracking session for user {user_name} ({user_id})")
213+
logging.info(f"Langfuse: User context available for user {user_name} ({user_id})")
226214

227-
logging.info(f"Langfuse tracing enabled for session")
215+
logging.info(f"Langfuse client initialized (user tracking via OTLP)")
228216
except Exception as e:
229-
logging.warning(f"Failed to initialize Langfuse: {e}")
217+
logging.warning(f"Failed to initialize Langfuse client: {e}")
230218
import traceback
231219
logging.warning(traceback.format_exc())
232220
langfuse_client = None
233-
langfuse_session_span = None
234221

235222
# Initialize OpenTelemetry for distributed tracing
236223
# Can send to Langfuse OTLP endpoint OR separate OTEL Collector
@@ -299,14 +286,26 @@ async def _run_claude_agent_sdk(self, prompt: str):
299286
# Get tracer (works whether we just set it or it was already set)
300287
otel_tracer = trace.get_tracer(__name__)
301288

302-
# Start session span
289+
# Start session span with user tracking
290+
# Use OpenTelemetry semantic convention 'enduser.id' for user tracking
291+
# Langfuse will automatically extract this from OTLP spans
292+
span_attributes = {
293+
"session.id": self.context.session_id,
294+
"namespace": self.context.get_env('AGENTIC_SESSION_NAMESPACE', 'unknown'),
295+
"prompt.length": len(prompt),
296+
}
297+
298+
# Add user attributes if available (OpenTelemetry semantic conventions)
299+
if user_id:
300+
span_attributes["enduser.id"] = user_id
301+
logging.info(f"OTLP: Adding user tracking for {user_name} ({user_id})")
302+
if user_name:
303+
# Not a standard OTEL attribute, but Langfuse may recognize it
304+
span_attributes["user.name"] = user_name
305+
303306
otel_span = otel_tracer.start_span(
304307
"claude_agent_session",
305-
attributes={
306-
"session.id": self.context.session_id,
307-
"namespace": self.context.get_env('AGENTIC_SESSION_NAMESPACE', 'unknown'),
308-
"prompt.length": len(prompt),
309-
}
308+
attributes=span_attributes
310309
)
311310

312311
logging.info(f"OpenTelemetry tracing enabled (endpoint: {otel_endpoint})")

0 commit comments

Comments
 (0)