AdithyaSK HF Staff commited on
Commit
e44a877
·
verified ·
1 Parent(s): c6d9f46

Serve Crello corpus aafdb0e86816 from the mounted bucket

Browse files
Files changed (1) hide show
  1. server/router.py +41 -14
server/router.py CHANGED
@@ -90,7 +90,7 @@ class Router:
90
  async def http_proxy(self, request: Request) -> Response:
91
  body = await request.body()
92
  worker = self.for_path(request.url.path)
93
- creating = False
94
  if worker is None and request.url.path.rstrip("/") == "/mcp" and request.method == "POST":
95
  try:
96
  rpc = json.loads(body or b"{}")
@@ -99,9 +99,10 @@ class Router:
99
  sid = (rpc.get("params") or {}).get("session_id") if isinstance(rpc, dict) else None
100
  if sid and sid in self.sessions:
101
  worker = self.sessions[sid]
 
 
102
  elif isinstance(rpc, dict) and rpc.get("method") == "openenv/session/create":
103
- worker, creating = self.least_busy(), True
104
- self.load[worker] += 1
105
  if worker is None:
106
  worker = next(self.rr)
107
  url = self.backends[worker] + request.url.path + (f"?{request.url.query}" if request.url.query else "")
@@ -110,21 +111,13 @@ class Router:
110
  req = self.http.build_request(request.method, url, headers=headers, content=body)
111
  resp = await self.http.send(req, stream=True)
112
  except httpx.HTTPError as e:
113
- if creating:
114
- self.load[worker] -= 1
115
  return JSONResponse({"error": f"worker {worker} unreachable: {e}"}, status_code=502)
116
  out_headers = {k: v for k, v in resp.headers.items() if k.lower() not in HOP}
117
- if creating or request.url.path.startswith("/api/episodes"):
 
 
118
  data = await resp.aread()
119
  await resp.aclose()
120
- if creating:
121
- self.load[worker] -= 1
122
- try:
123
- sid = json.loads(data).get("result", {}).get("session_id")
124
- except ValueError:
125
- sid = None
126
- if sid:
127
- self.sessions[sid] = worker
128
  self.learn(worker, data)
129
  return Response(data, status_code=resp.status_code, headers=out_headers)
130
 
@@ -137,6 +130,40 @@ class Router:
137
 
138
  return StreamingResponse(stream(), status_code=resp.status_code, headers=out_headers)
139
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
140
  # ------------------------------------------------------------------ WebSocket
141
 
142
  async def ws_proxy(self, ws: WebSocket) -> None:
 
90
  async def http_proxy(self, request: Request) -> Response:
91
  body = await request.body()
92
  worker = self.for_path(request.url.path)
93
+ closing = None
94
  if worker is None and request.url.path.rstrip("/") == "/mcp" and request.method == "POST":
95
  try:
96
  rpc = json.loads(body or b"{}")
 
99
  sid = (rpc.get("params") or {}).get("session_id") if isinstance(rpc, dict) else None
100
  if sid and sid in self.sessions:
101
  worker = self.sessions[sid]
102
+ if rpc.get("method") == "openenv/session/close":
103
+ closing = sid
104
  elif isinstance(rpc, dict) and rpc.get("method") == "openenv/session/create":
105
+ return await self.create_session(request, body)
 
106
  if worker is None:
107
  worker = next(self.rr)
108
  url = self.backends[worker] + request.url.path + (f"?{request.url.query}" if request.url.query else "")
 
111
  req = self.http.build_request(request.method, url, headers=headers, content=body)
112
  resp = await self.http.send(req, stream=True)
113
  except httpx.HTTPError as e:
 
 
114
  return JSONResponse({"error": f"worker {worker} unreachable: {e}"}, status_code=502)
115
  out_headers = {k: v for k, v in resp.headers.items() if k.lower() not in HOP}
116
+ if closing:
117
+ self.sessions.pop(closing, None)
118
+ if request.url.path.startswith("/api/episodes"):
119
  data = await resp.aread()
120
  await resp.aclose()
 
 
 
 
 
 
 
 
121
  self.learn(worker, data)
122
  return Response(data, status_code=resp.status_code, headers=out_headers)
123
 
 
130
 
131
  return StreamingResponse(stream(), status_code=resp.status_code, headers=out_headers)
132
 
133
+ async def create_session(self, request: Request, body: bytes) -> Response:
134
+ """A new OpenEnv session: the least busy worker first, the next one if it is full (each worker caps its
135
+ sessions at MAX_CONCURRENT_ENVS), and the session's worker remembered for /mcp and /ws."""
136
+ headers = {k: v for k, v in request.headers.items() if k.lower() not in HOP}
137
+ order = sorted(range(len(self.backends)), key=lambda i: (self.load[i], i))
138
+ first = self.least_busy()
139
+ order.remove(first)
140
+ data, status, out_headers = b"", 502, {}
141
+ for worker in [first, *order]:
142
+ self.load[worker] += 1
143
+ try:
144
+ resp = await self.http.post(self.backends[worker] + "/mcp", headers=headers, content=body)
145
+ except httpx.HTTPError as e:
146
+ data, status = json.dumps({"error": f"worker {worker} unreachable: {e}"}).encode(), 502
147
+ continue
148
+ finally:
149
+ self.load[worker] -= 1
150
+ data, status = resp.content, resp.status_code
151
+ out_headers = {k: v for k, v in resp.headers.items() if k.lower() not in HOP}
152
+ try:
153
+ reply = json.loads(data)
154
+ except ValueError:
155
+ reply = {}
156
+ sid = (reply.get("result") or {}).get("session_id") if isinstance(reply, dict) else None
157
+ if sid:
158
+ self.sessions[sid] = worker
159
+ if len(self.sessions) > 200_000: # sessions closed without telling us, oldest first
160
+ for k in list(self.sessions)[:50_000]:
161
+ del self.sessions[k]
162
+ break
163
+ if "capacity" not in json.dumps(reply.get("error", "")).lower():
164
+ break # a real error, not a full worker: pass it on
165
+ return Response(data, status_code=status, headers=out_headers)
166
+
167
  # ------------------------------------------------------------------ WebSocket
168
 
169
  async def ws_proxy(self, ws: WebSocket) -> None: