PandaProbe supports attaching session_id and user_id to traces. Use this to group related traces into conversations and to associate activity with specific users or accounts.
Session and user identifiers are strings you control. They are echoed on exported traces and in the UI so you can join to your own identity store or conversation store.
Scoped (recommended)
Global
Prefer pandaprobe.session() and pandaprobe.user() so IDs are tied to lexical scope and unwind cleanly when the block exits.
set_session / set_user are useful in request middleware where you set once per request, but you must avoid leaking values across async tasks or threads.
Context managers (scoped)
import pandaprobe
with pandaprobe.session("conversation-123"):
with pandaprobe.user("user-abc"):
# All traces created in this scope inherit session_id and user_id
with pandaprobe.start_trace("my-agent", input={"messages": [...]}) as t:
...
Imperative API (global)
pandaprobe.set_session("conversation-123")
pandaprobe.set_user("user-abc")
# All subsequent traces inherit these values
The imperative API sets session and user globally for the current context. It does not automatically reset. Prefer the context manager API when possible.
Explicit parameters (highest precedence)
When using decorators or context managers, explicit parameters always take precedence over scoped or global values:
@pandaprobe.trace(session_id="explicit-session", user_id="explicit-user")
def my_agent(query: str):
...
with pandaprobe.start_trace("agent", session_id="explicit") as t:
...
Precedence rules
- Explicit parameters passed to
@trace, start_trace(), or integration constructors
- Context manager scope (
pandaprobe.session() / pandaprobe.user())
- Imperative setting (
pandaprobe.set_session() / pandaprobe.set_user())
Works across all layers
Session and user IDs propagate to wrappers, integrations, and manual instrumentation. Integration constructors also accept session_id and user_id:
from pandaprobe.integrations.langgraph import LangGraphCallbackHandler
handler = LangGraphCallbackHandler(session_id="conv-1", user_id="user-1")
Multi-turn conversation example
import pandaprobe
from pandaprobe.wrappers import wrap_openai
from openai import OpenAI
client = wrap_openai(OpenAI())
history = []
with pandaprobe.session("conversation-123"), pandaprobe.user("user-abc"):
while True:
user_input = input("You: ")
if user_input.lower() == "quit":
break
history.append({"role": "user", "content": user_input})
response = client.chat.completions.create(
model="gpt-4o",
messages=history,
)
assistant_msg = response.choices[0].message.content
history.append({"role": "assistant", "content": assistant_msg})
print(f"Assistant: {assistant_msg}")