User-level Attribution
Overview
User-level attribution allows you to associate AI costs and usage with specific users of your application. By adding user identifiers to your GenAI calls, you can:
- Track costs by user: Understand which users or customer segments drive your AI costs
- Monitor usage patterns: Analyze how different users interact with your AI features
- Apply user-specific limits: Implement budget constraints for individual users
- Troubleshoot issues: Quickly identify problems related to specific users
- Meet compliance requirements: Support audit trails and user-level accountability
Setting User IDs in Pay-i
Pay-i provides two primary methods for associating user IDs with your GenAI requests:
1. Using the @track
Decorator and track_context()
Context Manager
@track
Decorator and track_context()
Context ManagerPay-i provides two complementary approaches for adding user attribution to your GenAI calls, each designed for specific scenarios:
When to Use track_context()
(For Runtime/Dynamic Data)
track_context()
(For Runtime/Dynamic Data)The track_context()
context manager should be used when your user ID is only available at runtime:
from payi.lib.instrument import track_context
# RECOMMENDED: For user IDs from function parameters or request context
def get_ai_response(prompt, user_id):
# Use track_context to capture runtime values
with track_context(user_id=user_id):
response = client.chat.completions.create(
model="gpt-3.5-turbo",
messages=[{"role": "user", "content": prompt}]
)
return response.choices[0].message.content
When to Use @track
Decorator (For Static Metadata)
@track
Decorator (For Static Metadata)The @track
decorator should only be used with truly static values, not with functions that should run per-request:
from payi.lib.instrument import track
# IMPORTANT: Do NOT use functions like get_current_user_id() with @track
# as they'll only run once at module load time, not per request
# CORRECT: Only use @track with truly static values
@track(user_id="system_bot") # Static literal value
def get_system_response(prompt):
response = client.chat.completions.create(
model="gpt-3.5-turbo",
messages=[{"role": "user", "content": prompt}]
)
return response.choices[0].message.content
2. Using HTTP Headers
For direct API calls or non-Python implementations, you can include the user ID in the request header:
xProxy-User-ID: user_123
This works with all Pay-i's proxy endpoints for supported GenAI providers.
User ID Format Requirements
User IDs in Pay-i:
- Must be strings (automatically converted if not)
- Can contain alphanumeric characters, periods (
.
), hyphens (-
), and underscores (_
) - Cannot contain spaces
- Have no fixed length limit, but keep them reasonably short for efficiency
Implementation Strategies
Web Applications
For web applications, you typically have user authentication systems that provide user identifiers:
# Flask example using session data
from flask import Flask, session, request
from payi.lib.instrument import track_context
app = Flask(__name__)
@app.route('/ai-chat')
def ai_chat():
# Get user ID from session and pass it to the function
user_id = session.get('user_id')
return get_ai_response(request.args.get('prompt'), user_id)
def get_ai_response(prompt, user_id):
# CORRECT: Use track_context for runtime values like session data
with track_context(user_id=user_id):
response = client.chat.completions.create(
model="gpt-3.5-turbo",
messages=[{"role": "user", "content": prompt}]
)
return response.choices[0].message.content
B2B Applications
For B2B applications, you might want to track by organization and user:
# Composite ID approach
def get_enterprise_ai_response(prompt, org_id, user_id):
# Create composite ID inside the function
composite_id = f"{org_id}:{user_id}"
with track_context(user_id=composite_id):
response = client.chat.completions.create(
model="gpt-3.5-turbo",
messages=[{"role": "user", "content": prompt}]
)
return response.choices[0].message.content
Anonymous Users
For applications with unauthenticated users, consider device or session-based identification:
# Using browser fingerprint or session ID
def get_anonymous_response(prompt, session_id):
with track_context(user_id=session_id):
response = client.chat.completions.create(
model="gpt-3.5-turbo",
messages=[{"role": "user", "content": prompt}]
)
return response.choices[0].message.content
Best Practices
- Use
track_context()
for user IDs: Always usetrack_context()
for user attribution since user IDs are typically request-specific values that change at runtime - Capture user IDs at runtime: Get the ID when processing the request, not when defining functions
- Never use runtime functions with
@track
: Functions likeget_current_user_id()
should never be used with the@track
decorator as they'll only execute once at module load time - Reserve
@track
for static values: The@track
decorator is designed for static metadata like use case names, not for dynamic values like user IDs - Consistency: Use the same user ID format across your entire application
- Privacy considerations: Avoid using personally identifiable information (PII)
- Granularity: Choose an identification level that matches your analytics needs
- Fallbacks: Have a strategy for cases where user ID might not be available
Viewing User-level Data in Pay-i
Once you've implemented user-level attribution:
- Log in to developer.pay-i.com
- Navigate to your application dashboard
- Click on the Users tab
- Use the
user_id
column to filter, sort, and analyze requests by user - Generate reports by user to understand cost distribution
Related Resources
payi_instrument()
- Set a defaultuser_id
globally and initialize the SDK.- Track Context - Use the
track_context()
function for dynamic runtime values like user IDs. - Track Decorator - Use the
@track
decorator for static metadata like use case names. - Custom Headers - Use the
xProxy-User-ID
header for per-request attribution with proxied requests or the REST ingest API. - Manual Event Submission - Pass the
user_id
when submitting events via the SDK or REST API.
Updated 5 days ago