fix: improve ops alias routing

Accept ops-prefixed alias names in the local proxy so OpenCode requests do not fail on model lookup. Add clearer proxy diagnostics and fix alias list target state output to simplify ops troubleshooting.
This commit is contained in:
apale7 2026-04-17 09:22:09 +08:00
parent c06afaecea
commit 4264ada4f8
3 changed files with 75 additions and 2 deletions

View File

@ -0,0 +1,53 @@
{
"id": "ops-mvp-4xx-review",
"name": "ops-mvp-4xx-review",
"title": "Review and fix OPS MVP 4xx failures",
"description": "",
"status": "planning",
"dev_type": null,
"scope": null,
"package": null,
"priority": "P2",
"creator": "Apale",
"assignee": "Apale",
"createdAt": "2026-04-17",
"completedAt": null,
"branch": null,
"base_branch": "master",
"worktree_path": null,
"current_phase": 0,
"next_action": [
{
"phase": 1,
"action": "brainstorm"
},
{
"phase": 2,
"action": "research"
},
{
"phase": 3,
"action": "implement"
},
{
"phase": 4,
"action": "check"
},
{
"phase": 5,
"action": "update-spec"
},
{
"phase": 6,
"action": "record-session"
}
],
"commit": null,
"pr_url": null,
"subtasks": [],
"children": [],
"parent": null,
"relatedFiles": [],
"notes": "",
"meta": {}
}

View File

@ -74,9 +74,9 @@ func newAliasListCmd() *cobra.Command {
}
fmt.Fprintf(cmd.OutOrStdout(), "%s [%s]\n", a.Alias, state)
for i, t := range a.Targets {
mark := " "
mark := "x"
if !t.Enabled {
mark = "x"
mark = " "
}
fmt.Fprintf(cmd.OutOrStdout(), " [%s] %d. %s/%s\n", mark, i+1, t.Provider, t.Model)
}

View File

@ -150,12 +150,17 @@ func (s *Server) handleResponses(w http.ResponseWriter, r *http.Request) {
http.Error(w, "missing model field", http.StatusBadRequest)
return
}
rawModel := aliasName
aliasName = normalizeAliasName(aliasName)
s.logger.Printf("req=%d incoming model=%q alias=%q stream=%v", reqID, rawModel, aliasName, payload["stream"])
alias := s.cfg.FindAlias(aliasName)
if alias == nil {
s.logger.Printf("req=%d alias lookup failed for model=%q alias=%q", reqID, rawModel, aliasName)
http.Error(w, fmt.Sprintf("alias %q not found", aliasName), http.StatusNotFound)
return
}
if !alias.Enabled {
s.logger.Printf("req=%d alias=%q disabled", reqID, aliasName)
http.Error(w, fmt.Sprintf("alias %q is disabled", aliasName), http.StatusNotFound)
return
}
@ -166,6 +171,7 @@ func (s *Server) handleResponses(w http.ResponseWriter, r *http.Request) {
}
}
if len(targets) == 0 {
s.logger.Printf("req=%d alias=%q has no enabled targets", reqID, aliasName)
http.Error(w, fmt.Sprintf("alias %q has no enabled targets", aliasName), http.StatusFailedDependency)
return
}
@ -178,6 +184,7 @@ func (s *Server) handleResponses(w http.ResponseWriter, r *http.Request) {
failoverCount++
continue
}
s.logger.Printf("req=%d alias=%s attempt=%d provider=%s remote_model=%s failovers=%d", reqID, aliasName, attempt+1, p.ID, t.Model, failoverCount)
// rewrite payload.model for this upstream
cloned := cloneMap(payload)
cloned["model"] = t.Model
@ -248,6 +255,7 @@ func (s *Server) tryOnce(
}
if resp.StatusCode >= 400 {
// non-retryable: forward status + body to client
s.logger.Printf("alias=%s attempt=%d provider=%s remote_model=%s upstream_status=%d", aliasName, attempt, provider.ID, target.Model, resp.StatusCode)
s.writeDebugHeaders(w, aliasName, provider.ID, target.Model, attempt, failoverCount)
copyResponseHeaders(w.Header(), resp.Header)
w.WriteHeader(resp.StatusCode)
@ -256,6 +264,7 @@ func (s *Server) tryOnce(
}
// 2xx: start streaming pass-through. From this point no failover is allowed.
s.logger.Printf("alias=%s attempt=%d provider=%s remote_model=%s upstream_status=%d", aliasName, attempt, provider.ID, target.Model, resp.StatusCode)
s.writeDebugHeaders(w, aliasName, provider.ID, target.Model, attempt, failoverCount)
copyResponseHeaders(w.Header(), resp.Header)
w.WriteHeader(resp.StatusCode)
@ -281,6 +290,17 @@ func (s *Server) tryOnce(
}
}
func normalizeAliasName(model string) string {
const prefix = "ops/"
if strings.HasPrefix(model, prefix) {
trimmed := strings.TrimPrefix(model, prefix)
if trimmed != "" {
return trimmed
}
}
return model
}
// writeDebugHeaders sets the X-OPS-* debug headers before WriteHeader.
func (s *Server) writeDebugHeaders(w http.ResponseWriter, alias, provider, remoteModel string, attempt, failoverCount int) {
h := w.Header()