2026-07-10 12:54:45 +00:00
|
|
|
package settings
|
|
|
|
|
|
|
|
|
|
import (
|
|
|
|
|
"context"
|
|
|
|
|
|
|
|
|
|
"apps/backend/internal/middleware"
|
2026-07-13 01:15:30 +00:00
|
|
|
"apps/backend/internal/module/ai"
|
2026-07-10 12:54:45 +00:00
|
|
|
"apps/backend/internal/module/member/domain"
|
|
|
|
|
"apps/backend/internal/svc"
|
|
|
|
|
"apps/backend/internal/types"
|
|
|
|
|
|
|
|
|
|
"github.com/zeromicro/go-zero/core/logx"
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
type SaveAiLogic struct {
|
|
|
|
|
logx.Logger
|
|
|
|
|
ctx context.Context
|
|
|
|
|
svcCtx *svc.ServiceContext
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func NewSaveAiLogic(ctx context.Context, svcCtx *svc.ServiceContext) *SaveAiLogic {
|
|
|
|
|
return &SaveAiLogic{Logger: logx.WithContext(ctx), ctx: ctx, svcCtx: svcCtx}
|
|
|
|
|
}
|
|
|
|
|
|
2026-07-13 01:15:30 +00:00
|
|
|
func (l *SaveAiLogic) SaveAi(req *types.SaveAiReq) (*types.AiSettingsData, error) {
|
2026-07-10 12:54:45 +00:00
|
|
|
uid, _ := middleware.UIDFrom(l.ctx)
|
2026-07-15 15:23:59 +00:00
|
|
|
st, err := l.svcCtx.Members.GetSettings(l.ctx, uid)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, err
|
|
|
|
|
}
|
2026-07-10 12:54:45 +00:00
|
|
|
if st == nil {
|
|
|
|
|
st = domain.DefaultSettings(uid)
|
|
|
|
|
}
|
2026-07-13 01:15:30 +00:00
|
|
|
if st.ProviderAPIKeys == nil {
|
|
|
|
|
st.ProviderAPIKeys = map[string]string{}
|
|
|
|
|
}
|
|
|
|
|
// migrate legacy single key into map
|
|
|
|
|
if st.APIKey != "" && st.Provider != "" && st.ProviderAPIKeys[st.Provider] == "" {
|
|
|
|
|
st.ProviderAPIKeys[st.Provider] = st.APIKey
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
provider := st.Provider
|
2026-07-10 12:54:45 +00:00
|
|
|
if req.Provider != "" {
|
2026-07-13 01:15:30 +00:00
|
|
|
provider = req.Provider
|
2026-07-10 12:54:45 +00:00
|
|
|
} else if req.ResearchProvider != "" {
|
2026-07-13 01:15:30 +00:00
|
|
|
provider = req.ResearchProvider
|
2026-07-10 12:54:45 +00:00
|
|
|
}
|
2026-07-13 01:15:30 +00:00
|
|
|
provider = ai.NormalizeProvider(provider)
|
|
|
|
|
st.Provider = provider
|
|
|
|
|
|
2026-07-10 12:54:45 +00:00
|
|
|
if req.Model != "" {
|
|
|
|
|
st.Model = req.Model
|
|
|
|
|
} else if req.ResearchModel != "" {
|
|
|
|
|
st.Model = req.ResearchModel
|
|
|
|
|
}
|
2026-07-13 01:15:30 +00:00
|
|
|
|
|
|
|
|
// BYOK for this provider
|
2026-07-10 12:54:45 +00:00
|
|
|
if req.ApiKey != "" {
|
2026-07-13 01:15:30 +00:00
|
|
|
st.SetKeyForProvider(provider, req.ApiKey)
|
2026-07-10 12:54:45 +00:00
|
|
|
}
|
|
|
|
|
if req.ResearchApiKey != "" {
|
2026-07-13 01:15:30 +00:00
|
|
|
st.SetKeyForProvider(provider, req.ResearchApiKey)
|
|
|
|
|
}
|
|
|
|
|
if req.ApiKeyConfigured != nil && !*req.ApiKeyConfigured {
|
|
|
|
|
st.SetKeyForProvider(provider, "")
|
2026-07-10 12:54:45 +00:00
|
|
|
}
|
2026-07-13 01:15:30 +00:00
|
|
|
|
|
|
|
|
// validate model against live list (or fallback)
|
|
|
|
|
list, _, _ := loadModelsForProvider(l.ctx, l.svcCtx, st, provider)
|
|
|
|
|
st.Model = pickModel(st.Model, list)
|
|
|
|
|
|
|
|
|
|
st.APIKeyConfigured = st.HasKeyForProvider(provider)
|
|
|
|
|
st.ResearchAPIKeyConfigured = st.APIKeyConfigured
|
|
|
|
|
st.UpdatedAt = domain.NowNano()
|
|
|
|
|
|
|
|
|
|
if err := l.svcCtx.Members.SaveSettings(l.ctx, uid, st); err != nil {
|
|
|
|
|
return nil, err
|
2026-07-10 12:54:45 +00:00
|
|
|
}
|
2026-07-13 01:15:30 +00:00
|
|
|
// return combined settings + models
|
|
|
|
|
return NewGetAiLogic(l.ctx, l.svcCtx).GetAi(&types.GetAiReq{Provider: provider})
|
2026-07-10 12:54:45 +00:00
|
|
|
}
|