Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions internal/handlers/errors.go
Original file line number Diff line number Diff line change
Expand Up @@ -178,3 +178,14 @@ func respondServiceUnavailable(w http.ResponseWriter, r *http.Request, message s
err := restapi.NewAPIError(http.StatusServiceUnavailable, restapi.ErrCodeServiceUnavailable, message)
restapi.RespondError(w, r, err)
}

// respondUpgradeRequired writes a 426 Upgrade Required JSON response. Used by
// the llm-proxy broker when a coding-agent client presents a missing or
// mismatched protocol version, so an out-of-step agent fails loudly with a
// diagnostic signal instead of misparsing the response (WI-921). The caller
// advertises the supported version in the X-Protocol-Version response header.
func respondUpgradeRequired(w http.ResponseWriter, r *http.Request) {
err := restapi.NewAPIError(http.StatusUpgradeRequired, "PROTOCOL_VERSION_MISMATCH",
"coding-agent protocol version is out of date; the agent image must match the server version")
restapi.RespondError(w, r, err)
}
15 changes: 15 additions & 0 deletions internal/handlers/runner_broker.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,16 @@ const (
// egressResponseHeaderTimeout bounds time-to-first-header for arbitrary
// HTTP/git egress, where a slow upstream is treated as a fault.
egressResponseHeaderTimeout = 30 * time.Second

// brokerProtocolVersion identifies the non-streaming inference contract the
// broker speaks. The coding-agent client must send this exact value in the
// X-Protocol-Version header on every /complete request; the broker rejects
// a missing or newer/mismatched version with 426 Upgrade Required so an
// out-of-step agent fails loudly and diagnostically instead of misparsing
// the response (WI-921). Bump it when CompletionRequest/CompletionResponse
// changes incompatibly.
brokerProtocolVersion = "1"
protocolVersionHeader = "X-Protocol-Version"
// llmResponseHeaderTimeout is intentionally generous: an OpenAI-compatible
// chat completion can spend minutes on prompt prefill (long context,
// reasoning) before committing the SSE response headers. A 30s bound aborts
Expand Down Expand Up @@ -171,6 +181,11 @@ func (h *RunnerBrokerHandler) ProxyLLM(w http.ResponseWriter, r *http.Request) {
respondForbidden(w, r)
return
}
if v := r.Header.Get(protocolVersionHeader); v != brokerProtocolVersion {
w.Header().Set(protocolVersionHeader, brokerProtocolVersion)
respondUpgradeRequired(w, r)
return
}
cfg, err := h.llmConns.ConnectionRuntime(r.Context(), grants.LLM.ConnectionID)
if err != nil {
respondServiceUnavailable(w, r, "llm connection unavailable")
Expand Down
Loading