feat: first-byte timeout with same-endpoint retry
Some checks failed
Build Docker Image / build (push) Has been cancelled
Some checks failed
Build Docker Image / build (push) Has been cancelled
Upstream sometimes accepts a request (HTTP 200 headers) but stalls without sending any event-stream packet. Add a configurable timeout that counts from request dispatch until the first AWS event-stream prelude is read, and retry on the same endpoint before falling back. - Config: FirstByteTimeoutSec (default 10s, 0=disabled, range 0-300), FirstByteRetries (default 1, range 0-10), with Get/Update helpers. - kiro.go: parseEventStream signature gains onFirstByte callback, fired once when the first 12-byte prelude reads successfully. CallKiroAPI wraps each attempt in a context.WithCancel + time.AfterFunc timer that cancels the HTTP request if no event arrives before the deadline. Separate retry budgets for INVALID_MODEL_ID and first-byte timeout, tracked on the same attempt loop; maxAttempts = max(both)+1. - handler.go: /admin/api/general extended to read/write the two new fields with validation (timeout 0-300, retries 0-10). - web/index.html: General Settings card gains two numeric inputs plus CN/EN i18n and the corresponding load/save JS.
This commit is contained in:
@@ -2935,6 +2935,8 @@ func (h *Handler) apiUpdateProxy(w http.ResponseWriter, r *http.Request) {
|
||||
func (h *Handler) apiGetGeneralConfig(w http.ResponseWriter, r *http.Request) {
|
||||
json.NewEncoder(w).Encode(map[string]interface{}{
|
||||
"invalidModelRetries": config.GetInvalidModelRetries(),
|
||||
"firstByteTimeoutSec": config.GetFirstByteTimeoutSec(),
|
||||
"firstByteRetries": config.GetFirstByteRetries(),
|
||||
})
|
||||
}
|
||||
|
||||
@@ -2942,6 +2944,8 @@ func (h *Handler) apiGetGeneralConfig(w http.ResponseWriter, r *http.Request) {
|
||||
func (h *Handler) apiUpdateGeneralConfig(w http.ResponseWriter, r *http.Request) {
|
||||
var req struct {
|
||||
InvalidModelRetries *int `json:"invalidModelRetries"`
|
||||
FirstByteTimeoutSec *int `json:"firstByteTimeoutSec"`
|
||||
FirstByteRetries *int `json:"firstByteRetries"`
|
||||
}
|
||||
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
|
||||
w.WriteHeader(400)
|
||||
@@ -2963,6 +2967,34 @@ func (h *Handler) apiUpdateGeneralConfig(w http.ResponseWriter, r *http.Request)
|
||||
}
|
||||
}
|
||||
|
||||
if req.FirstByteTimeoutSec != nil {
|
||||
n := *req.FirstByteTimeoutSec
|
||||
if n < 0 || n > 300 {
|
||||
w.WriteHeader(400)
|
||||
json.NewEncoder(w).Encode(map[string]string{"error": "firstByteTimeoutSec must be 0-300"})
|
||||
return
|
||||
}
|
||||
if err := config.UpdateFirstByteTimeoutSec(n); err != nil {
|
||||
w.WriteHeader(500)
|
||||
json.NewEncoder(w).Encode(map[string]string{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if req.FirstByteRetries != nil {
|
||||
n := *req.FirstByteRetries
|
||||
if n < 0 || n > 10 {
|
||||
w.WriteHeader(400)
|
||||
json.NewEncoder(w).Encode(map[string]string{"error": "firstByteRetries must be 0-10"})
|
||||
return
|
||||
}
|
||||
if err := config.UpdateFirstByteRetries(n); err != nil {
|
||||
w.WriteHeader(500)
|
||||
json.NewEncoder(w).Encode(map[string]string{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
json.NewEncoder(w).Encode(map[string]bool{"success": true})
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user