From 46bd3e90315feff6d6dc3b3e72fc13e79be9bcb9 Mon Sep 17 00:00:00 2001 From: Teknium <127238744+teknium1@users.noreply.github.com> Date: Wed, 2 Sep 2026 23:02:05 -0700 Subject: [PATCH] =?UTF-8?q?refactor(gateway/platforms):=20api=5Fserver=20?= =?UTF-8?q?=E2=80=94=20shared=20=5Fsession=5Fheaders=20builder,=20tuple=20?= =?UTF-8?q?unpacks=20in=20session=20chat=20handlers?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- gateway/platforms/api_server.py | 51 +++++++++++++++++---------------- 1 file changed, 26 insertions(+), 25 deletions(-) diff --git a/gateway/platforms/api_server.py b/gateway/platforms/api_server.py index 81563377db..d2b17bd812 100644 --- a/gateway/platforms/api_server.py +++ b/gateway/platforms/api_server.py @@ -367,9 +367,7 @@ def _project_client_message(message: Dict[str, Any]) -> Dict[str, Any]: _COMPACTION_INTERNAL_FIELDS, project_compaction_message_for_display) projected = project_compaction_message_for_display(message) if projected is None: - projected = message.copy() - for internal_key in _COMPACTION_INTERNAL_FIELDS: - projected.pop(internal_key, None) + projected = {k: v for k, v in message.items() if k not in _COMPACTION_INTERNAL_FIELDS} projected["content"] = "" projected["display_kind"] = "hidden" return projected @@ -1601,8 +1599,9 @@ class APIServerAdapter(OpenAICompatRoutesMixin, BasePlatformAdapter): logger.warning( "X-Hermes-Session-Key rejected: no API key configured. " "Set API_SERVER_KEY to enable long-term memory scoping.") - return None, _error_response("X-Hermes-Session-Key requires API key authentication. " - "Configure API_SERVER_KEY to enable this feature.", 403) + return None, _error_response( + "X-Hermes-Session-Key requires API key authentication. " + "Configure API_SERVER_KEY to enable this feature.", 403) # Control characters could enable header injection on the echo path. if re.search(r'[\r\n\x00]', raw): return None, _invalid_request("Invalid session key") @@ -2658,9 +2657,9 @@ class APIServerAdapter(OpenAICompatRoutesMixin, BasePlatformAdapter): "actual_cost_usd", "api_call_count", "parent_session_id", "last_active", "preview", "_lineage_root_id", "pinned", "archived", "hidden") payload = {key: session.get(key) for key in safe_keys if key in session} - for flag in ("pinned", "archived", "hidden"): # SQLite stores 0/1 - if flag in payload: - payload[flag] = bool(payload[flag]) + # SQLite stores the flags as 0/1. + payload.update( + {f: bool(payload[f]) for f in ("pinned", "archived", "hidden") if f in payload}) # Full system prompts / model_config never cross the client API; only their presence. payload["has_system_prompt"] = bool(session.get("system_prompt")) payload["has_model_config"] = bool(session.get("model_config")) @@ -2882,8 +2881,7 @@ class APIServerAdapter(OpenAICompatRoutesMixin, BasePlatformAdapter): return err db = await self._ensure_session_db_async() resolved_id = await asyncio.to_thread(db.resolve_resume_session_id, session_id) - raw_limit = request.query.get("limit") - raw_offset = request.query.get("offset", "0") + raw_limit, raw_offset = request.query.get("limit"), request.query.get("offset", "0") order = request.query.get("order") if order not in (None, "oldest", "latest"): return _error_response("order must be one of: oldest, latest", 400, code="invalid_pagination") @@ -3008,6 +3006,14 @@ class APIServerAdapter(OpenAICompatRoutesMixin, BasePlatformAdapter): "user_message": user_message, "runtime_request": runtime_request, "lock_active": lock_active, "run_kwargs": run_kwargs}, None + @staticmethod + def _session_headers(session_id: str, gateway_session_key: Optional[str]) -> Dict[str, str]: + """``X-Hermes-Session-Id`` (+ ``X-Hermes-Session-Key`` when declared) response headers.""" + headers = {"X-Hermes-Session-Id": session_id} + if gateway_session_key: + headers["X-Hermes-Session-Key"] = gateway_session_key + return headers + def _effective_turn_runtime(self, runtime_request: Dict[str, Any], result: Any, usage: Any) -> Dict[str, Any]: """Sanitized runtime metadata for a finished session-chat turn.""" runtime = self._result_runtime(result, usage) @@ -3040,11 +3046,11 @@ class APIServerAdapter(OpenAICompatRoutesMixin, BasePlatformAdapter): session_id = ctx["session_id"] history = await self._conversation_history_for_session(session_id) result, usage = await self._run_agent(conversation_history=history, **ctx["run_kwargs"]) - effective_session_id = result.get("session_id") if isinstance(result, dict) else session_id - final_response = _resolve_media_to_data_urls(result.get("final_response", "") if isinstance(result, dict) else "") - headers = {"X-Hermes-Session-Id": effective_session_id or session_id} - if gateway_session_key: - headers["X-Hermes-Session-Key"] = gateway_session_key + is_dict = isinstance(result, dict) + effective_session_id = result.get("session_id") if is_dict else session_id + final_response = _resolve_media_to_data_urls( + result.get("final_response", "") if is_dict else "") + headers = self._session_headers(effective_session_id or session_id, gateway_session_key) return web.json_response( {"object": "hermes.session.chat.completion", "session_id": effective_session_id or session_id, @@ -3058,10 +3064,8 @@ class APIServerAdapter(OpenAICompatRoutesMixin, BasePlatformAdapter): ctx, err = await self._prepare_session_chat(request) if err is not None: return err - gateway_session_key = ctx["gateway_session_key"] - session_id = ctx["session_id"] - user_message = ctx["user_message"] - runtime_request = ctx["runtime_request"] + gateway_session_key, session_id = ctx["gateway_session_key"], ctx["session_id"] + user_message, runtime_request = ctx["user_message"], ctx["runtime_request"] runtime_meta = self._sanitize_runtime_metadata( requested_runtime=runtime_request.get("requested"), route_source=runtime_request.get("route_source") or "global", @@ -3069,8 +3073,7 @@ class APIServerAdapter(OpenAICompatRoutesMixin, BasePlatformAdapter): message_id = f"msg_{uuid.uuid4().hex}" run_id = f"run_{uuid.uuid4().hex}" events = _SessionEventQueue(session_id, run_id) - queue = events.queue - _event_payload = events.payload + queue, _event_payload = events.queue, events.payload # Claim ownership inside the request's profile scope before any run-keyed state # exists, so /v1/runs/{id}* control is confined to the starting profile. self._run_owners[run_id] = self._run_idempotency_scope(request) @@ -3139,10 +3142,8 @@ class APIServerAdapter(OpenAICompatRoutesMixin, BasePlatformAdapter): task = asyncio.create_task(_run_and_signal()) self._track_background_task(task) headers = { - "Content-Type": "text/event-stream", "Cache-Control": "no-cache", - "X-Accel-Buffering": "no", "X-Hermes-Session-Id": session_id} - if gateway_session_key: - headers["X-Hermes-Session-Key"] = gateway_session_key + "Content-Type": "text/event-stream", "Cache-Control": "no-cache", "X-Accel-Buffering": "no", + **self._session_headers(session_id, gateway_session_key)} response = web.StreamResponse(status=200, headers=headers) await response.prepare(request) try: