Changeset 0.22.3 (#97)

This commit is contained in:
2026-03-02 12:02:16 +00:00
parent a44c768741
commit 14c691b52f
11 changed files with 440 additions and 25 deletions

View File

@@ -1,6 +1,7 @@
package handlers
import (
"context"
"encoding/json"
"log"
"net/http"
@@ -9,6 +10,7 @@ import (
capspkg "git.gobha.me/xcaliber/chat-switchboard/capabilities"
"git.gobha.me/xcaliber/chat-switchboard/database"
"git.gobha.me/xcaliber/chat-switchboard/health"
"git.gobha.me/xcaliber/chat-switchboard/models"
"git.gobha.me/xcaliber/chat-switchboard/providers"
"git.gobha.me/xcaliber/chat-switchboard/store"
@@ -16,13 +18,19 @@ import (
// ModelHandler provides the unified models endpoint.
type ModelHandler struct {
stores store.Stores
stores store.Stores
healthStore HealthStatusQuerier // provider health for status enrichment (v0.22.3, nil = disabled)
}
func NewModelHandler(s store.Stores) *ModelHandler {
return &ModelHandler{stores: s}
}
// SetHealthStore attaches the health store for model status enrichment (v0.22.3).
func (h *ModelHandler) SetHealthStore(hs HealthStatusQuerier) {
h.healthStore = hs
}
// ListEnabledModels returns all models the user can access (catalog + personas),
// with user preferences (hidden, sort order) applied.
func (h *ModelHandler) ListEnabledModels(c *gin.Context) {
@@ -35,12 +43,35 @@ func (h *ModelHandler) ListEnabledModels(c *gin.Context) {
return
}
// Enrich with provider health status (v0.22.3)
if h.healthStore != nil {
healthMap := h.buildHealthMap(c.Request.Context())
for i := range userModels {
if s, ok := healthMap[userModels[i].ProviderConfigID]; ok {
userModels[i].ProviderStatus = s
}
}
}
// Include admin default model so frontend can use it in resolution chain
defaultModel, _ := h.stores.Policies.Get(c.Request.Context(), "default_model")
c.JSON(http.StatusOK, gin.H{"models": userModels, "default_model": defaultModel})
}
// buildHealthMap returns a map of configID → ProviderStatus from current health windows.
func (h *ModelHandler) buildHealthMap(ctx context.Context) map[string]models.ProviderStatus {
m := make(map[string]models.ProviderStatus)
windows, err := h.healthStore.ListAllCurrent(ctx)
if err != nil {
return m
}
for _, w := range windows {
m[w.ProviderConfigID] = health.DeriveStatus(w.ErrorRate(), w.RequestCount)
}
return m
}
// ResolveModelCaps is the canonical capability resolver for any model.
func ResolveModelCaps(c *gin.Context, modelID, configID string) models.ModelCapabilities {
// 1. Exact match: model_id + provider_config_id