Skip to content

Add a gRPC wire beside the websocket for inference sessions - #728

Open
v-positronic wants to merge 46 commits into
mainfrom
grpc-wire
Open

Add a gRPC wire beside the websocket for inference sessions#728
v-positronic wants to merge 46 commits into
mainfrom
grpc-wire

Conversation

@v-positronic

@v-positronic v-positronic commented Sep 9, 2026

Copy link
Copy Markdown
Collaborator

A session runs over a websocket today. This adds a gRPC wire beside it — an open port on a
LAN, and an authenticated endpoint behind a TLS-terminating front — and puts both wires
behind one interface.

What it ships

The server takes the wires it serves on. wire.Wire covers what a transport does
outside an open session: bind, accept, refuse an unauthorized peer, stop. WebsocketWire
and GrpcWire implement it, each in its own module, and PolicyServer.serve takes the
list. A third wire costs a class and a flag. Each wire reads its own route for the model a
session names — a path parameter on one, session metadata on the other — and checks its
own headers. server.api holds the model catalogue, which a wire that speaks HTTP serves
on the port it carries sessions on.

ServerConnection already covered an open session, so the inference loop was
transport-free while opening one was not. The handshake meta now names the port that
carried the session, where a gRPC session took the websocket's before.
serve(wires, on_ready=...) reports when every wire has bound; a wire asked for port 0
can be dialled.

The client names no wire either. Each wire dials its own session (websocket_wire.dial,
grpc_wire.dial) and raises one wire.ConnectRefused that says what the refusal means:
cold, forbidden, or final. The connect loop retries those and a lost peer, and client.py
imports neither library. new_session raises ConnectRefused where it raised a library's own
exception; a bad certificate is final on both wires.

grpcs:// dials the gRPC port through a TLS edge. The client verifies the edge against
its own roots and the server keeps its plaintext port — every authenticated shape puts a
front in the way, and a front hands the server an HTTP/2 stream. One enum member carries a
scheme's wire and its TLS half together; a scheme added later cannot leave the two
disagreeing.

Pings through a silent wait. A front drops a connection it has read nothing from — the
Nebius one after about 90 s — and one inference sends nothing until it answers. The gRPC
defaults space two client pings five minutes apart and answer a 20 s ping with GOAWAY too_many_pings; the options here set both.

An edge no client can use is permanent. An uncovered certificate and a front selecting
no HTTP/2 over ALPN both answer UNAVAILABLE, as a cold backend does. The connect loop
reads the status details and stops.

A wire-only stub server (positronic.offboard.stub) answers one constant chunk; a
round trip measures the transport alone. workflows/nebius/serve.sh serves both wires and
prints the grpcs:// URL.

The front has to negotiate HTTP/2 over ALPN

Declare the gRPC port as an ordinary HTTP port and dial its https:// host as
grpcs://<host>:443. A port declared /tcp is fronted by a tls:// URL that negotiates
no ALPN, and gRPC refuses it with Cannot check peer: missing selected ALPN property.

What the smoke measured

A CPU endpoint running the stub on a live Nebius Serverless Endpoint, over its managed
https:// front. A grpcs:// session infers an 846 KiB observation in 8 ms with the
bearer in its metadata, and answers PERMISSION_DENIED without it. A session idle 150 s
still infers, and ?delay_sec=120 answers after two minutes — both past the front's idle
close.

Round trip against the websocket, same front: 1.8 ms against 6.2 at 63 KiB, 3.0 against
19.2 at 255 KiB, 6.0 against 61.3 at 846 KiB. A back-to-back loop of 846 KiB settles at
about 83 ms after some 11 MB and recovers after a minute of quiet: the front shapes a
session that sends faster than it forwards. The websocket holds 60 ms throughout, below
that rate.

CI carries the production shape: a TLS terminator in the test process, the bearer refused
without it, an edge with no ALPN and one with an unknown root, and the keepalives. Both
wires answer one observation alike apart from the endpoint, each naming its own port.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 27633ace1c

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment thread positronic/offboard/grpc_wire.py Outdated

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 27e0f042e2

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment thread positronic/offboard/grpc_wire.py Outdated
Comment thread positronic/offboard/tests/test_grpc_wire.py Outdated
Comment thread positronic/offboard/client.py Outdated

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 60e3960b05

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment thread workflows/nebius/serve.sh Outdated
Comment thread positronic/offboard/grpc_wire.py Outdated
Comment thread positronic/offboard/tests/test_grpc_wire.py Outdated

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 740b67efbb

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment thread positronic/offboard/grpc_wire.py
Comment thread positronic/offboard/grpc_wire.py Outdated

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: aa7c87bc16

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment thread positronic/offboard/client.py Outdated
Comment thread positronic/offboard/client.py Outdated
Comment thread positronic/offboard/client.py Outdated
Comment thread workflows/nebius/serve.sh

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: f1b11063a7

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment thread positronic/offboard/grpc_wire.py

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 1fa3b12f87

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment thread positronic/offboard/grpc_wire.py Outdated

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 66c25367cb

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment thread positronic/offboard/grpc_wire.py
Comment thread workflows/nebius/serve.sh Outdated

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 617be708d2

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment thread positronic/offboard/grpc_wire.py Outdated
Comment thread positronic/offboard/tests/test_grpc_wire.py Outdated
`positronic/offboard` serves a session over two wires now. `wire.py` holds the seam and the
websocket wire; `grpc_wire.py` holds the gRPC one. Every frame stays the same msgpack message, and
a wire carries it as opaque bytes, so `protocol.py` does not change.

A gRPC session is one bidirectional stream. A generic handler with no serialiser carries the
frames, so there is no protobuf schema and no generated code. The session path, the query and the
`Authorization` header cross as gRPC metadata, so both wires accept the same model ids and the
same session params.

`grpc://host:port/api/v1/session/<model_id>` selects the wire, and `PolicyServer(grpc_port=...)`
serves it beside the websocket one. Both wires run on one event loop, so they share the model
slot, the inference lock and the idle watchdog.

Python's websocket stack costs about 30 ms per 846 KiB observation, which gRPC does in about 1 ms.
The websocket wire stays the default, because a managed HTTPS front drops the HTTP/2 frame detail
gRPC needs.

Ticket: none - a transport measurement with no ticket; the numbers are in the pull request
The writing rules refuse a sentence that opens on its reason.

Ticket: none - a one-line prose fix the style gate named
Cut the cold-backend comment to the fact a reader acts on, and drop the HTTP/2
rationale the offboard README already carries.

Ticket: none - a comment pass on the change in flight
gRPC's target syntax needs an IPv6 literal in brackets, so a server hosted on `::` bound
`:::<port>` — which gRPC refuses by returning port 0, and the server then started, accepted
nothing and said nothing. The bind reports a refusal now instead of starting deaf.

Ticket: none - a review finding on an unmerged branch
…shake refuses

Three findings from the review round.

The 16 MiB ceiling was gRPC's alone and matched uvicorn's `ws_max_size` default by coincidence,
so a move in that default would have parted the two wires silently. It lives in `wire` now and
both the gRPC options and `uvicorn.Config` are passed it.

A session the server refuses in a protocol frame — an unknown model, a session param it rejects —
raises past every transport handler, and a gRPC connection holds a reader thread until it is
closed. `_open_session` owns one attempt and closes the connection whenever the handshake does not
finish.

The query test reads `LOCAL_STACK` and `SEQ` rather than spelling them.

Ticket: none - review findings on an unmerged branch
An authenticated endpoint is served by a TLS-terminating front, so `grpcs://`
dials the gRPC port through one: the client verifies the edge against its own
roots and the server keeps its plaintext port, which is what every front hands
an HTTP/2 stream to. A `grpcs://` URL defaults to port 443 like the other TLS
schemes.

A front also drops a connection it has read nothing from, and one inference
sends nothing until it answers, so the client pings through the silence. Three
of gRPC's own defaults stop that working and each is set here: the client sends
two pings and stops, it spaces them five minutes apart, and the server answers
a 20s ping with `GOAWAY too_many_pings`.

A certificate the client's roots do not cover now raises the exception the
websocket wire raises, so the connect loop reads it as permanent instead of
retrying its whole deadline out. gRPC's readiness future says only that the
channel is down, so one call on it fetches the reason.

Ticket: Positronic-Robotics/internal#1191 #refs
It answers one constant chunk, so a round trip measures the wire and nothing
else. `?delay_sec=120` holds an inference open for two minutes, which is what a
cold model does to a connection with nothing crossing it meanwhile.

Ticket: Positronic-Robotics/internal#1191 #refs
The endpoint now exposes port 9000 beside 8000 and prints the `grpcs://` URL
for it. The gRPC port is declared as an ordinary HTTP port: that front
negotiates HTTP/2 over ALPN and carries a gRPC session end to end, where a port
declared `/tcp` gets a `tls://` URL whose front negotiates no ALPN at all and
gRPC refuses it.

The README records what a round trip costs through such a front, and where the
front stops being the faster wire's problem.

Ticket: Positronic-Robotics/internal#1191 #refs
…sked for

Three review findings.

A front that selects no HTTP/2 over ALPN refuses every gRPC client, exactly as an
unverifiable certificate does, and both answer `UNAVAILABLE` like a cold backend —
so the connect loop reads the status details rather than the code alone, and stops.
The connection raises what gRPC blamed instead of a bare timeout, which is what
carries those details. A test edge that selects no protocol pins the new case; it
binds one address, because gRPC reports the last address it failed on and a second
family refusing the connection hides what the first blamed.

`serve.sh` exposed port 9000 whatever the caller asked the server to listen on. It
now reads the port back out of `--grpc_port` when one is given.

The test proxy caught every `OSError`, which hid a broken edge behind the reset an
ending session is expected to raise. It now catches the two that end a session.

Ticket: Positronic-Robotics/internal#1191 #refs
Two review findings.

A timed-out inference closes the connection, and the server can still answer inside
the five seconds `close` waits for the stream to end. That reply sat in the inbox,
where the next `infer` would have read it — one observation's actions against the
next observation's state. `send` and `recv` now refuse a closed session.

`channel_credentials` wrapped one call and had one caller, so it was a production
name held open for the tests. It is inlined, and the tests substitute roots on gRPC's
own function.

Ticket: Positronic-Robotics/internal#1191 #refs
Three review findings.

gRPC stops reading the request iterator once a stream ends, so a send after the
caller has been told why it ended sat in the outbox while the next `recv` waited out
a whole inference timeout on an inbox nothing refills. The connection records the
end when it hands out the reason, and refuses a send past it.

The websocket client kept the library's 1 MiB receive default while the server, the
gRPC client and the gRPC server all carry the shared 16 MiB ceiling, so a result in
between closed the session rather than answering it.

`serve.sh` printed `grpcs://:443` when the endpoint served no URL for the gRPC port.
It fails there instead.

Ticket: Positronic-Robotics/internal#1191 #refs
`channel_ready_future` and the probe that asks why it failed each took the whole
`open_timeout`, so a target that black-holes connection attempts blocked for
nearly twice the documented limit before the connect loop saw a status. Both now
run against one deadline, with a second reserved for the probe.

Ticket: Positronic-Robotics/internal#1191 #refs
The scheme was dispatched as a closed set of strings, and `grpcs` was spelled a
second time to recover its TLS half. A scheme added to one tuple and not the
other would have left the wire and the security disagreeing. One enum member now
carries both, and the parse fails on anything it does not name.

Ticket: Positronic-Robotics/internal#1191 #refs
The create declared `--container-port 8000` and the poll selected
`https://port8000-`, so a port change could leave the reader looking for an
endpoint creation never made.

Ticket: Positronic-Robotics/internal#1191 #refs
The `InferenceClient` section named `https` and `wss`, so adding `grpcs` left it
wrong: the default is 443 whenever the scheme is TLS. The rule cannot go stale as
schemes arrive; the wire table above it names which they are.

Ticket: Positronic-Robotics/internal#1191 #refs

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 569d4dc63b

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment thread workflows/nebius/serve.sh Outdated
The header claimed gRPC on 9000, while `--grpc_port` decides it and 9000 is only
the default. "Container port" was the same claim one line down; the websocket port
is the one this script fixes.

Ticket: Positronic-Robotics/internal#1191 #refs
The `--policy.url` section named `http`, `ws` and `wss` and stopped there, so the
wire this branch adds was absent from the one doc a user reads to reach a server.
The offboard README and `InferenceClient` already list `grpc(s)`; this was the third
copy and the only stale one.

Ticket: Positronic-Robotics/internal#1191 #refs

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 0b9ab74e0b

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment thread docs/inference.md
The guide opened by calling remote inference WebSocket-only and labelled the offboard
README "WebSocket protocol", so its overview contradicted the URL schemes further
down. One protocol, two wires, said once in each place.

Ticket: Positronic-Robotics/internal#1191 #refs

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: c84de5cd91

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment thread positronic/offboard/grpc_wire.py Outdated
`PolicyServer` held each wire's bind, its accept-time auth and its model-id
extraction: a FastAPI app and two routes for the websocket, `grpc_session` and
`_start_grpc` for gRPC. `wire.ServerConnection` already covered an open session, so
the inference loop was transport-free while opening a session was not, and a third
wire cost five edits to the server.

`wire.Wire` covers the other half: bind, accept, refuse and stop. Each wire reads its
own route for the model a session names and checks its own session headers. `serve`
takes the list of them, and the CLI entry point is where the two wires are named.

The handshake meta names the port that carried the session. A gRPC session reported
the websocket's port before.

The test fixture serves through `PolicyServer.serve` rather than rebuilding it, which
removed a second copy of the wire lifecycle and gave the idle watchdog its first test.

Ticket: Positronic-Robotics/internal#1191 #refs
`serve` gathered the wire tasks with `return_exceptions=True` and read none of the
results, so a wire that failed as it stopped was discarded without a line in the log.
One failure now raises out of `serve` and every other one is logged at ERROR.

`STOP_GRACE_SEC` moves into `WebsocketWire`, which is its only user, and the idle
test's own constant moves above the test that reads it.

Ticket: Positronic-Robotics/internal#1191 #refs
The class docstring restated what a wire reads and checks, which `wire.Wire` and the
offboard README both already say. It names what this server does with a wire instead.

Ticket: Positronic-Robotics/internal#1191 #refs

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 14a2c3861e

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment thread positronic/offboard/server.py
Comment thread positronic/offboard/README.md Outdated

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 71609909a1

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment thread positronic/offboard/server.py Outdated
`PolicyServer.default_session` and `model_session` are gone, and the two lerobot vendor
suites still called them. Both skip without their vendor package, so a local run reported
them as skipped and CI failed on them.

Each now builds the connection the websocket wire builds and calls `_serve_session`, which
is the same handler the wire reaches. They are the only two callers the removal left.

Two comments in `_listening_socket` said `is what` where the verb does the work.

Ticket: Positronic-Robotics/internal#1191 #refs
`serve` started every wire before it entered the cleanup `try`, so a `GrpcWire` that bound
stayed bound when the `WebsocketWire` after it found its port taken: a live listener in
front of a server nothing serves. Startup runs inside the `try` now, and the cleanup stops
the wires that actually started.

The status-update section said the frames prevent a WebSocket keepalive timeout. They hold
the client's 30s-per-message handshake open, which is the same on either wire.

Ticket: Positronic-Robotics/internal#1191 #refs

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 43536c7aa0

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment thread positronic/offboard/wire.py Outdated
A wire binds its listening socket in `start`, and uvicorn takes it over only when `serve` runs. A startup that rolls back stops a wire that bound but never served, so `stop` set uvicorn's `should_exit` on a server that never ran and the socket kept its port. Close the socket in `stop` where the wire never served.

Ticket: Positronic-Robotics/internal#1191 #refs

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 55bb7f4a5b

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment thread positronic/offboard/server.py
`wire.py` keeps the protocol every wire implements. `websocket_wire.py` holds the websocket wire and the two ends of a websocket session, beside `grpc_wire.py`.

Ticket: Positronic-Robotics/internal#1191 #refs
Each comment and docstring the gRPC wire adds states one fact, in one or
two lines. A sentence that gave a reason, weighed an alternative, or told
the reader how to read a fact is gone. A docstring that repeated its test
name is gone.

Ticket: Positronic-Robotics/internal#1191 #refs
The inference guide states the GPU split as a fact.

Ticket: Positronic-Robotics/internal#1191 #refs

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: c01b1d3cfa

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment thread positronic/offboard/server.py
Comment thread positronic/offboard/server.py Outdated
…at holds

The name says what the tuple holds, and the comment above it goes.

Five passages predate the pull request and sit in files it touches. Each one
carried design rationale or ran far past the docstring budget. The rationale
is in the offboard README and in `workflows/nebius/README.md`, next to the
subject it explains. The passages keep each constraint and each footgun:

- `PolicyServer`: what it serves, the two halves of the marker, the fixed
  source, session params, and the pinned default checkpoint.
- `serve`: the token comes from the environment, a flag exposes a secret.
- `InferenceClient`: the URL forms, the gRPC port, the headers, the timeouts.
- `serve.sh`: the managed URL changes on re-create, and the `--auth token`
  ingress strips the WebSocket upgrade headers.

Ticket: Positronic-Robotics/internal#1191 #refs

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: c9e66e2862

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment thread positronic/offboard/README.md
Comment thread positronic/offboard/tests/test_grpc_wire.py Outdated
`wire.Refusal` names what a refused connect says about the server, and
`wire.ConnectRefused` carries it with the library error as the cause.
`websocket_wire.dial` and `grpc_wire.dial` open a client's connection and
classify what their library refuses. The gRPC wire also classifies a status
that ends the stream before its first message, and the websocket client end
raises `PeerDisconnected` for a closed connection.

`InferenceClient.new_session` catches `ConnectRefused`, `TimeoutError` and
`PeerDisconnected`, spends the refusal against the retry budget, and keeps
the connect deadline. It raises `ConnectRefused` where it raised the
library exception. `client.py` imports neither `grpc` nor `websockets`.

Ticket: Positronic-Robotics/internal#1191 #refs

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 9738f0c6a9

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment thread positronic/offboard/grpc_wire.py
A status that ends the stream before its first message is the server
refusing the call. A status after one is a lost peer, and the wire raised
the `grpc.RpcError` itself.

`InferenceClient.new_session` retries `ConnectRefused`, `TimeoutError` and
`PeerDisconnected`, so that error escaped a cold start the websocket wire
retries: the websocket client end raises `PeerDisconnected` for a closed
connection whether or not a frame crossed. The gRPC wire now raises it too,
with the status as the cause.

Ticket: Positronic-Robotics/internal#1191 #refs
A failed session tells the client before it cleans up. That send reaches a
peer which may already be gone, and one blanket catch recorded every
outcome at DEBUG.

A peer that has gone raises `PeerDisconnected` and stays at DEBUG. Any
other failure names the peer at ERROR, so a delivery path that breaks for
another reason is not silent.

Ticket: Positronic-Robotics/internal#1191 #refs
`PolicyServer` takes no `host` or `port`, and `serve` takes the wires it
serves on, so the custom-model example in `docs/connect-your-model.md`
raised `TypeError`. It now builds the server, then serves it a
`WebsocketWire` over `server.api`.

The offboard README says every wire carries the same frames, while its
session endpoints were named and shown for the websocket alone. The
headings name the path, and the examples show both schemes.

Ticket: Positronic-Robotics/internal#1191 #refs
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant