客户端需要 Manager 下发用户可用模型作唯一来源(里程碑04 收尾)。
- GET /api/heicode/available-models(挂 UserOrV2DeviceAuth,与 /api/heicode/self 同
设备配对鉴权)。
- 服务端解析:user.Group → service.GetUserUsableGroups → model.GetGroupEnabledModels
(与 GetUserModels 同源),去重排序。
- 客户端安全形状 {model_id, display_name, default}:绝不下发 channelId/base_url/
api_key/provider_type/单价(buildAvailableModelItems 纯函数 + 测试断言不泄露)。
- default 标记取 defaultAgentModelID()。
可选字段 capabilities/context_window/cost_tier 暂不下发(HM 无可靠来源,避免臆造;
客户端按可选处理)。etag/updated_at 可作后续。
Fixes #25
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
64 lines
1.9 KiB
Go
64 lines
1.9 KiB
Go
package controller
|
|
|
|
import (
|
|
"sort"
|
|
|
|
"github.com/gin-gonic/gin"
|
|
|
|
"github.com/heicode/manager/common"
|
|
"github.com/heicode/manager/model"
|
|
"github.com/heicode/manager/service"
|
|
)
|
|
|
|
// buildAvailableModelItems maps resolved model names to the client-safe catalog
|
|
// shape for #25: only model_id / display_name / default. It deliberately omits
|
|
// channelId / base_url / api_key_ref / provider_type / per-token price — those
|
|
// stay server-side (Manager). Pure (testable).
|
|
func buildAvailableModelItems(modelNames []string, defaultModel string) []gin.H {
|
|
items := make([]gin.H, 0, len(modelNames))
|
|
for _, name := range modelNames {
|
|
items = append(items, gin.H{
|
|
"model_id": name,
|
|
"display_name": name,
|
|
"default": name == defaultModel,
|
|
})
|
|
}
|
|
return items
|
|
}
|
|
|
|
// HeicodeAvailableModels: GET /api/heicode/available-models (issue #25).
|
|
//
|
|
// The single source of truth for the desktop client's model list: the models
|
|
// the LOGGED-IN user may use, resolved server-side from the user's usable groups
|
|
// → group-enabled models (same resolution as GetUserModels). Bound to the user's
|
|
// own group/subscription; channelId / base_url / api_key / provider type / price
|
|
// are NEVER exposed. Same auth as /api/heicode/self (UserOrV2DeviceAuth / device
|
|
// pairing). Client uses this as the only model source (no local presets).
|
|
func HeicodeAvailableModels(c *gin.Context) {
|
|
userID := c.GetInt("id")
|
|
if userID <= 0 {
|
|
common.ApiErrorMsg(c, "authentication required")
|
|
return
|
|
}
|
|
user, err := model.GetUserCache(userID)
|
|
if err != nil {
|
|
common.ApiError(c, err)
|
|
return
|
|
}
|
|
seen := map[string]bool{}
|
|
var names []string
|
|
for group := range service.GetUserUsableGroups(user.Group) {
|
|
for _, m := range model.GetGroupEnabledModels(group) {
|
|
if !seen[m] {
|
|
seen[m] = true
|
|
names = append(names, m)
|
|
}
|
|
}
|
|
}
|
|
sort.Strings(names)
|
|
|
|
common.ApiSuccess(c, gin.H{
|
|
"available_models": buildAvailableModelItems(names, defaultAgentModelID()),
|
|
})
|
|
}
|