feat: AUTO chain rewrite — silent failover+busy skip+pref round-robin+503 tier summary; chain edits reset slot cooldowns (P0/P1); stats by_status + audit jsonl rotation; UI priority-page health badges & status-code card; ctx-menu capture-phase close (outside-press guard); main.go ops warnings; local bundled-Lua verified tests (3 latent bugs fixed); plan.md

This commit is contained in:
JianFeeeee
2026-08-10 23:58:31 +08:00
parent 397af36fbb
commit 88802f9ef6
17 changed files with 2060 additions and 433 deletions

View File

@ -15,7 +15,6 @@ import (
"net/url"
"strings"
"sync"
"sync/atomic"
"time"
"llmsproxy/internal/config"
@ -27,12 +26,11 @@ var uiFS embed.FS
// Gateway is the HTTP handler for the OpenAI-compatible endpoint + web UI.
type Gateway struct {
core *core.Core
ui http.Handler
stats *Stats
probeMu sync.Mutex
lastProbe time.Time
autoRR atomic.Uint64
core *core.Core
ui http.Handler
stats *Stats
probeMu sync.Mutex
lastProbe time.Time
}
func New(c *core.Core, gatewayKeys []string) (*Gateway, error) {
@ -131,6 +129,8 @@ func (g *Gateway) routes(w http.ResponseWriter, r *http.Request) {
g.handleChat(w, r)
case r.URL.Path == "/api/status":
g.handleStatusAPI(w, r)
case r.URL.Path == "/api/status/reset":
g.handleResetHealth(w, r)
case r.URL.Path == "/api/stats" || strings.HasPrefix(r.URL.Path, "/api/stats/"):
g.handleStatsAPI(w, r)
case r.URL.Path == "/api/keys" || strings.HasPrefix(r.URL.Path, "/api/keys/"):
@ -389,6 +389,22 @@ func (g *Gateway) ensureProbe(ctx context.Context) {
}
}
// handleResetHealth (admin) clears the per-source backoff state so a fixed
// upstream or an edited AUTO priority chain becomes schedulable immediately.
func (g *Gateway) handleResetHealth(w http.ResponseWriter, r *http.Request) {
if r.Method != http.MethodPost {
writeError(w, http.StatusMethodNotAllowed, "method_not_allowed", "use POST")
return
}
if reqRole(r.Context()) != "admin" {
writeError(w, http.StatusForbidden, "forbidden", "admin role required")
return
}
g.core.ResetHealth()
g.stats.AppendAudit("config", map[string]interface{}{"action": "reset_health", "key": keyID(reqKey(r.Context()))})
writeJSON(w, http.StatusOK, map[string]interface{}{"ok": true})
}
func (g *Gateway) handleStatusAPI(w http.ResponseWriter, r *http.Request) {
g.ensureProbe(r.Context())
host := r.Host