# Sessions
Sessions group related traces together, which is essential for tracking multi-turn conversations, user interactions, or any sequence of related operations.
## Basic Sessions
```python
from mirascope import ops
@ops.trace
def process_request(data: str) -> str:
return f"Processed: {data}"
# Group related traces in a session
with ops.session(id="user-123"):
# All traces within this block share the session ID
trace1 = process_request.wrapped("first request")
trace2 = process_request.wrapped("second request")
print(trace1.span_id)
print(trace2.span_id)
```
All traces created within a session block share the same session ID, making it easy to analyze related operations together.
## Custom Session IDs
You can provide your own session ID or let the system generate one:
```python
from mirascope import ops
# Auto-generated session ID
with ops.session() as ctx:
print(ctx.id) # e.g., "a1b2c3d4-e5f6-7890-abcd-ef1234567890"
# Custom session ID
with ops.session(id="user-123-conversation-1"):
do_work()
```
## Session Attributes
Add attributes to sessions for filtering and organization:
```python
from mirascope import ops
@ops.trace
def handle_request(query: str) -> str:
return f"Response to: {query}"
# Session with custom attributes
with ops.session(
id="conversation-456",
attributes={"user_tier": "premium", "channel": "web"},
) as session_ctx:
print(f"Session ID: {session_ctx.id}")
print(f"Attributes: {session_ctx.attributes}")
trace = handle_request.wrapped("What's the weather?")
print(trace.result)
```
| Parameter | Description |
| --- | --- |
| `id` | Custom session ID (auto-generated if not provided) |
| `attributes` | Key-value pairs for session metadata |
## Accessing the Current Session
Use `ops.current_session()` to access the active session from anywhere:
```python
from mirascope import ops
@ops.trace
def process_request():
session = ops.current_session()
if session:
print(f"Processing in session: {session.id}")
print(f"User tier: {session.attributes.get('user_tier')}")
return "done"
with ops.session(attributes={"user_tier": "premium"}):
process_request()
```
Returns `None` if no session is active.
## Nested Sessions
Sessions can be nested. The innermost session takes precedence:
```python
from mirascope import ops
with ops.session(id="outer"):
with ops.session(id="inner"):
session = ops.current_session()
print(session.id) # "inner"
session = ops.current_session()
print(session.id) # "outer"
```
## Session Propagation
Sessions are automatically propagated across service boundaries when using context propagation:
```python
from mirascope import ops
# Client side
with ops.session(id="user-session-123"):
headers: dict[str, str] = {}
ops.inject_context(headers)
# headers includes "X-Mirascope-Session-Id: user-session-123"
```
See [Context Propagation](/docs/ops/context-propagation) for more details.
## Use Cases
### Multi-turn Conversations
```python
from mirascope import llm, ops
messages = []
@ops.trace
@llm.call("openai/gpt-4o-mini")
def chat(user_message: str) -> list:
messages.append({"role": "user", "content": user_message})
return messages
with ops.session(id=f"conversation-{user_id}"):
response1 = chat("Hello!")
response2 = chat("Tell me more")
response3 = chat("Thanks!")
```
### Request Tracing
```python
from mirascope import ops
@app.middleware("http")
async def session_middleware(request, call_next):
session_id = request.headers.get("X-Session-Id", str(uuid.uuid4()))
with ops.session(id=session_id, attributes={"path": request.url.path}):
return await call_next(request)
```
## Next Steps
- [Spans](/docs/ops/spans) — Create explicit spans for fine-grained control
- [Context Propagation](/docs/ops/context-propagation) — Pass context across services