Yash030 Claude Opus 4.7 commited on
Commit
948c8f9
·
1 Parent(s): d64f2a2

Track sessions via X-Session-ID header for accurate admin dashboard

Browse files

Claude Code --session-id <uuid> sends X-Session-ID on every request.
Use this header as the session identifier instead of inferring from
client IP (gateway_<ip>), which is unreliable when multiple sessions
share an IP. Fall back to gateway IP for non-session-ID requests.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

Files changed (1) hide show
  1. api/services.py +14 -12
api/services.py CHANGED
@@ -106,6 +106,18 @@ def _get_client_ip(request: Request) -> str | None:
106
  return None # Direct connection
107
 
108
 
 
 
 
 
 
 
 
 
 
 
 
 
109
  class ClaudeProxyService:
110
  """Coordinate request optimization, model routing, and providers."""
111
 
@@ -122,16 +134,6 @@ class ClaudeProxyService:
122
  self._token_counter = token_counter
123
  self._session_tracker = SessionTracker.get_instance()
124
 
125
- def _get_session_id(self, request: Request, request_data: MessagesRequest) -> str:
126
- """Extract or generate a session ID for gateway clients only."""
127
- # Check if request came through a gateway/proxy
128
- ip = _get_client_ip(request)
129
- if ip is None:
130
- return "direct" # Don't track direct connections
131
-
132
- # Use gateway client IP as session identifier
133
- return f"gateway_{ip}"
134
-
135
  def create_message(self, request: Request, request_data: MessagesRequest) -> object:
136
  """Create a message response or streaming response with optional failover."""
137
  try:
@@ -210,7 +212,7 @@ class ClaudeProxyService:
210
  thinking_enabled=resolved.thinking_enabled,
211
  )
212
 
213
- session_id = self._get_session_id(request, request_data)
214
  self._session_tracker.track_request_sync(session_id, resolved.provider_id)
215
 
216
  request_id = f"req_{uuid.uuid4().hex[:12]}"
@@ -274,7 +276,7 @@ class ClaudeProxyService:
274
  thinking_enabled=resolved.thinking_enabled,
275
  )
276
 
277
- session_id = self._get_session_id(request, request_data)
278
  self._session_tracker.track_request_sync(
279
  session_id, resolved.provider_id
280
  )
 
106
  return None # Direct connection
107
 
108
 
109
+ def _get_session_id(request: Request) -> str:
110
+ """Get session ID from X-Session-ID header or fall back to gateway IP.
111
+
112
+ Claude Code sends X-Session-ID when started with --session-id <uuid>.
113
+ """
114
+ session = request.headers.get("X-Session-ID")
115
+ if session:
116
+ return session
117
+ ip = _get_client_ip(request)
118
+ return f"gateway_{ip}" if ip else "direct"
119
+
120
+
121
  class ClaudeProxyService:
122
  """Coordinate request optimization, model routing, and providers."""
123
 
 
134
  self._token_counter = token_counter
135
  self._session_tracker = SessionTracker.get_instance()
136
 
 
 
 
 
 
 
 
 
 
 
137
  def create_message(self, request: Request, request_data: MessagesRequest) -> object:
138
  """Create a message response or streaming response with optional failover."""
139
  try:
 
212
  thinking_enabled=resolved.thinking_enabled,
213
  )
214
 
215
+ session_id = _get_session_id(request)
216
  self._session_tracker.track_request_sync(session_id, resolved.provider_id)
217
 
218
  request_id = f"req_{uuid.uuid4().hex[:12]}"
 
276
  thinking_enabled=resolved.thinking_enabled,
277
  )
278
 
279
+ session_id = _get_session_id(request)
280
  self._session_tracker.track_request_sync(
281
  session_id, resolved.provider_id
282
  )