yao/llmprovider/process.go
Max 6f78f6066b feat(llm): refactor audio capabilities and enhance role management
- Replaced 'Voice' with 'Audio' in the system configuration and related tests to better reflect functionality.
- Introduced new methods for role management in the llmprovider, allowing for dynamic retrieval of roles based on user and team context.
- Updated the OpenAPI settings to support new role management endpoints and capabilities.
- Enhanced the handling of API keys in provider management, allowing for optional plain-text retrieval.
2026-04-30 12:33:05 +08:00

594 lines
17 KiB
Go

package llmprovider
import (
"encoding/json"
"github.com/yaoapp/gou/connector"
"github.com/yaoapp/gou/process"
"github.com/yaoapp/kun/exception"
)
func init() {
process.RegisterGroup("llmprovider", map[string]process.Handler{
// --- existing ---
"get": ProcessGet,
"getmasked": ProcessGetMasked,
"create": ProcessCreate,
"update": ProcessUpdate,
"delete": ProcessDelete,
"list": ProcessList,
"getsetting": ProcessGetSetting,
"getpresets": ProcessGetPresets,
"getpreset": ProcessGetPreset,
// --- roles ---
"getrole": ProcessGetRole,
"getrolebyuser": ProcessGetRoleByUser,
"getrolebyteam": ProcessGetRoleByTeam,
"listroles": ProcessListRoles,
"listrolesbyuser": ProcessListRolesByUser,
"listrolesbyteam": ProcessListRolesByTeam,
// --- models ---
"getmodel": ProcessGetModel,
"getrolemodel": ProcessGetRoleModel,
"getrolemodelbyuser": ProcessGetRoleModelByUser,
"getrolemodelbyteam": ProcessGetRoleModelByTeam,
"getdefaultmodel": ProcessGetDefaultModel,
"getdefaultmodelbyuser": ProcessGetDefaultModelByUser,
"getdefaultmodelbyteam": ProcessGetDefaultModelByTeam,
"getvisionmodel": ProcessGetVisionModel,
"getvisionmodelbyuser": ProcessGetVisionModelByUser,
"getvisionmodelbyteam": ProcessGetVisionModelByTeam,
"getaudiomodel": ProcessGetAudioModel,
"getaudiomodelbyuser": ProcessGetAudioModelByUser,
"getaudiomodelbyteam": ProcessGetAudioModelByTeam,
"getembeddingmodel": ProcessGetEmbeddingModel,
"getembeddingmodelbyuser": ProcessGetEmbeddingModelByUser,
"getembeddingmodelbyteam": ProcessGetEmbeddingModelByTeam,
// --- capabilities ---
"getcapabilities": ProcessGetCapabilities,
"getrolecapabilities": ProcessGetRoleCapabilities,
"getrolecapabilitiesbyuser": ProcessGetRoleCapabilitiesByUser,
"getrolecapabilitiesbyteam": ProcessGetRoleCapabilitiesByTeam,
// --- list models ---
"listmodels": ProcessListModels,
"listmodelsbyuser": ProcessListModelsByUser,
"listmodelsbyteam": ProcessListModelsByTeam,
})
}
func requireGlobal() {
if Global == nil {
exception.New("LLM Provider Registry not initialized", 500).Throw()
}
}
// ProcessGet retrieves a provider by key.
// Args[0] string: provider key
// Args[1] bool: withKey (optional, default false) — true returns plain-text APIKey
func ProcessGet(p *process.Process) interface{} {
requireGlobal()
p.ValidateArgNums(1)
key := p.ArgsString(0)
withKey := len(p.Args) > 1 && toBool(p.Args[1])
provider, err := Global.Get(key, withKey)
if err != nil {
exception.New(err.Error(), 404).Throw()
}
return provider
}
// ProcessGetMasked retrieves a provider with API key masked.
// Args[0] string: provider key
func ProcessGetMasked(p *process.Process) interface{} {
requireGlobal()
p.ValidateArgNums(1)
key := p.ArgsString(0)
provider, err := Global.GetMasked(key)
if err != nil {
exception.New(err.Error(), 404).Throw()
}
return provider
}
// ProcessCreate adds a new provider.
// Args[0] map: Provider data
func ProcessCreate(p *process.Process) interface{} {
requireGlobal()
p.ValidateArgNums(1)
var provider Provider
raw, err := json.Marshal(p.Args[0])
if err != nil {
exception.New("invalid provider data: "+err.Error(), 400).Throw()
}
if err := json.Unmarshal(raw, &provider); err != nil {
exception.New("invalid provider data: "+err.Error(), 400).Throw()
}
result, err := Global.Create(&provider)
if err != nil {
exception.New(err.Error(), 400).Throw()
}
return result
}
// ProcessUpdate modifies an existing provider.
// Args[0] string: provider key
// Args[1] map: Provider data
func ProcessUpdate(p *process.Process) interface{} {
requireGlobal()
p.ValidateArgNums(2)
key := p.ArgsString(0)
var provider Provider
raw, err := json.Marshal(p.Args[1])
if err != nil {
exception.New("invalid provider data: "+err.Error(), 400).Throw()
}
if err := json.Unmarshal(raw, &provider); err != nil {
exception.New("invalid provider data: "+err.Error(), 400).Throw()
}
result, err := Global.Update(key, &provider)
if err != nil {
exception.New(err.Error(), 400).Throw()
}
return result
}
// ProcessDelete removes a provider by key.
// Args[0] string: provider key
func ProcessDelete(p *process.Process) interface{} {
requireGlobal()
p.ValidateArgNums(1)
key := p.ArgsString(0)
if err := Global.Delete(key); err != nil {
exception.New(err.Error(), 404).Throw()
}
return nil
}
// ProcessList returns providers matching a filter.
// Args[0] map: ProviderFilter (optional)
// Args[1] bool: withKey (optional, default false) — true returns plain-text APIKeys
func ProcessList(p *process.Process) interface{} {
requireGlobal()
var filter *ProviderFilter
if len(p.Args) > 0 && p.Args[0] != nil {
raw, err := json.Marshal(p.Args[0])
if err == nil {
var f ProviderFilter
if json.Unmarshal(raw, &f) == nil {
filter = &f
}
}
}
withKey := len(p.Args) > 1 && toBool(p.Args[1])
result, err := Global.List(filter, withKey)
if err != nil {
exception.New(err.Error(), 500).Throw()
}
return result
}
// ProcessGetSetting returns the runtime connector setting map.
// Args[0] string: provider key
func ProcessGetSetting(p *process.Process) interface{} {
requireGlobal()
p.ValidateArgNums(1)
key := p.ArgsString(0)
setting, err := Global.GetSetting(key)
if err != nil {
exception.New(err.Error(), 404).Throw()
}
return setting
}
// ProcessGetPresets returns all provider presets.
func ProcessGetPresets(p *process.Process) interface{} {
return GetPresets()
}
// ProcessGetPreset returns a single preset by key.
// Args[0] string: preset key
func ProcessGetPreset(p *process.Process) interface{} {
p.ValidateArgNums(1)
key := p.ArgsString(0)
preset := GetPreset(key)
if preset == nil {
exception.New("preset "+key+" not found", 404).Throw()
}
return preset
}
// ---------------------------------------------------------------------------
// Roles
// ---------------------------------------------------------------------------
// ProcessGetRole returns the connectorID for a role (system scope).
// Args[0] string: role name
func ProcessGetRole(p *process.Process) interface{} {
requireGlobal()
p.ValidateArgNums(1)
cid, err := Global.GetRole(p.ArgsString(0))
if err != nil {
exception.New(err.Error(), 404).Throw()
}
return cid
}
// ProcessGetRoleByUser returns the connectorID for a role (user > system merge).
// Args[0] string: role, Args[1] string: userID
func ProcessGetRoleByUser(p *process.Process) interface{} {
requireGlobal()
p.ValidateArgNums(2)
cid, err := Global.GetRoleByUser(p.ArgsString(0), p.ArgsString(1))
if err != nil {
exception.New(err.Error(), 404).Throw()
}
return cid
}
// ProcessGetRoleByTeam returns the connectorID for a role (team > system merge).
// Args[0] string: role, Args[1] string: teamID
func ProcessGetRoleByTeam(p *process.Process) interface{} {
requireGlobal()
p.ValidateArgNums(2)
cid, err := Global.GetRoleByTeam(p.ArgsString(0), p.ArgsString(1))
if err != nil {
exception.New(err.Error(), 404).Throw()
}
return cid
}
// ProcessListRoles returns all role assignments (system scope).
func ProcessListRoles(p *process.Process) interface{} {
requireGlobal()
roles, err := Global.ListRoles()
if err != nil {
exception.New(err.Error(), 500).Throw()
}
return rolesToMap(roles)
}
// ProcessListRolesByUser returns all role assignments (user > system merge).
// Args[0] string: userID
func ProcessListRolesByUser(p *process.Process) interface{} {
requireGlobal()
p.ValidateArgNums(1)
roles, err := Global.ListRolesByUser(p.ArgsString(0))
if err != nil {
exception.New(err.Error(), 500).Throw()
}
return rolesToMap(roles)
}
// ProcessListRolesByTeam returns all role assignments (team > system merge).
// Args[0] string: teamID
func ProcessListRolesByTeam(p *process.Process) interface{} {
requireGlobal()
p.ValidateArgNums(1)
roles, err := Global.ListRolesByTeam(p.ArgsString(0))
if err != nil {
exception.New(err.Error(), 500).Throw()
}
return rolesToMap(roles)
}
// ---------------------------------------------------------------------------
// Models
// ---------------------------------------------------------------------------
// ProcessGetModel returns the connector setting map by connectorID.
// Args[0] string: connectorID
func ProcessGetModel(p *process.Process) interface{} {
requireGlobal()
p.ValidateArgNums(1)
conn, err := Global.GetModel(p.ArgsString(0))
if err != nil {
exception.New(err.Error(), 404).Throw()
}
return conn.Setting()
}
// ProcessGetRoleModel returns the connector setting map for a role (system scope).
// Args[0] string: role
func ProcessGetRoleModel(p *process.Process) interface{} {
requireGlobal()
p.ValidateArgNums(1)
conn, err := Global.GetRoleModel(p.ArgsString(0))
if err != nil {
exception.New(err.Error(), 404).Throw()
}
return conn.Setting()
}
// ProcessGetRoleModelByUser returns the connector setting map for a role (user scope).
// Args[0] string: role, Args[1] string: userID
func ProcessGetRoleModelByUser(p *process.Process) interface{} {
requireGlobal()
p.ValidateArgNums(2)
conn, err := Global.GetRoleModelByUser(p.ArgsString(0), p.ArgsString(1))
if err != nil {
exception.New(err.Error(), 404).Throw()
}
return conn.Setting()
}
// ProcessGetRoleModelByTeam returns the connector setting map for a role (team scope).
// Args[0] string: role, Args[1] string: teamID
func ProcessGetRoleModelByTeam(p *process.Process) interface{} {
requireGlobal()
p.ValidateArgNums(2)
conn, err := Global.GetRoleModelByTeam(p.ArgsString(0), p.ArgsString(1))
if err != nil {
exception.New(err.Error(), 404).Throw()
}
return conn.Setting()
}
// ProcessGetDefaultModel returns the default model connector setting map.
func ProcessGetDefaultModel(p *process.Process) interface{} {
requireGlobal()
conn, err := Global.GetDefaultModel()
if err != nil {
exception.New(err.Error(), 404).Throw()
}
return conn.Setting()
}
// ProcessGetDefaultModelByUser returns the default model for a user.
// Args[0] string: userID
func ProcessGetDefaultModelByUser(p *process.Process) interface{} {
requireGlobal()
p.ValidateArgNums(1)
conn, err := Global.GetDefaultModelByUser(p.ArgsString(0))
if err != nil {
exception.New(err.Error(), 404).Throw()
}
return conn.Setting()
}
// ProcessGetDefaultModelByTeam returns the default model for a team.
// Args[0] string: teamID
func ProcessGetDefaultModelByTeam(p *process.Process) interface{} {
requireGlobal()
p.ValidateArgNums(1)
conn, err := Global.GetDefaultModelByTeam(p.ArgsString(0))
if err != nil {
exception.New(err.Error(), 404).Throw()
}
return conn.Setting()
}
// ProcessGetVisionModel returns the vision model connector setting map.
func ProcessGetVisionModel(p *process.Process) interface{} {
requireGlobal()
conn, err := Global.GetVisionModel()
if err != nil {
exception.New(err.Error(), 404).Throw()
}
return conn.Setting()
}
// ProcessGetVisionModelByUser returns the vision model for a user.
// Args[0] string: userID
func ProcessGetVisionModelByUser(p *process.Process) interface{} {
requireGlobal()
p.ValidateArgNums(1)
conn, err := Global.GetVisionModelByUser(p.ArgsString(0))
if err != nil {
exception.New(err.Error(), 404).Throw()
}
return conn.Setting()
}
// ProcessGetVisionModelByTeam returns the vision model for a team.
// Args[0] string: teamID
func ProcessGetVisionModelByTeam(p *process.Process) interface{} {
requireGlobal()
p.ValidateArgNums(1)
conn, err := Global.GetVisionModelByTeam(p.ArgsString(0))
if err != nil {
exception.New(err.Error(), 404).Throw()
}
return conn.Setting()
}
// ProcessGetAudioModel returns the audio model connector setting map.
func ProcessGetAudioModel(p *process.Process) interface{} {
requireGlobal()
conn, err := Global.GetAudioModel()
if err != nil {
exception.New(err.Error(), 404).Throw()
}
return conn.Setting()
}
// ProcessGetAudioModelByUser returns the audio model for a user.
// Args[0] string: userID
func ProcessGetAudioModelByUser(p *process.Process) interface{} {
requireGlobal()
p.ValidateArgNums(1)
conn, err := Global.GetAudioModelByUser(p.ArgsString(0))
if err != nil {
exception.New(err.Error(), 404).Throw()
}
return conn.Setting()
}
// ProcessGetAudioModelByTeam returns the audio model for a team.
// Args[0] string: teamID
func ProcessGetAudioModelByTeam(p *process.Process) interface{} {
requireGlobal()
p.ValidateArgNums(1)
conn, err := Global.GetAudioModelByTeam(p.ArgsString(0))
if err != nil {
exception.New(err.Error(), 404).Throw()
}
return conn.Setting()
}
// ProcessGetEmbeddingModel returns the embedding model connector setting map.
func ProcessGetEmbeddingModel(p *process.Process) interface{} {
requireGlobal()
conn, err := Global.GetEmbeddingModel()
if err != nil {
exception.New(err.Error(), 404).Throw()
}
return conn.Setting()
}
// ProcessGetEmbeddingModelByUser returns the embedding model for a user.
// Args[0] string: userID
func ProcessGetEmbeddingModelByUser(p *process.Process) interface{} {
requireGlobal()
p.ValidateArgNums(1)
conn, err := Global.GetEmbeddingModelByUser(p.ArgsString(0))
if err != nil {
exception.New(err.Error(), 404).Throw()
}
return conn.Setting()
}
// ProcessGetEmbeddingModelByTeam returns the embedding model for a team.
// Args[0] string: teamID
func ProcessGetEmbeddingModelByTeam(p *process.Process) interface{} {
requireGlobal()
p.ValidateArgNums(1)
conn, err := Global.GetEmbeddingModelByTeam(p.ArgsString(0))
if err != nil {
exception.New(err.Error(), 404).Throw()
}
return conn.Setting()
}
// ---------------------------------------------------------------------------
// Capabilities
// ---------------------------------------------------------------------------
// ProcessGetCapabilities returns capabilities for a connectorID.
// Args[0] string: connectorID
func ProcessGetCapabilities(p *process.Process) interface{} {
requireGlobal()
p.ValidateArgNums(1)
caps, err := Global.GetCapabilities(p.ArgsString(0))
if err != nil {
exception.New(err.Error(), 404).Throw()
}
return capsToMap(caps)
}
// ProcessGetRoleCapabilities returns capabilities for a role (system scope).
// Args[0] string: role
func ProcessGetRoleCapabilities(p *process.Process) interface{} {
requireGlobal()
p.ValidateArgNums(1)
caps, err := Global.GetRoleCapabilities(p.ArgsString(0))
if err != nil {
exception.New(err.Error(), 404).Throw()
}
return capsToMap(caps)
}
// ProcessGetRoleCapabilitiesByUser returns capabilities for a role (user scope).
// Args[0] string: role, Args[1] string: userID
func ProcessGetRoleCapabilitiesByUser(p *process.Process) interface{} {
requireGlobal()
p.ValidateArgNums(2)
caps, err := Global.GetRoleCapabilitiesByUser(p.ArgsString(0), p.ArgsString(1))
if err != nil {
exception.New(err.Error(), 404).Throw()
}
return capsToMap(caps)
}
// ProcessGetRoleCapabilitiesByTeam returns capabilities for a role (team scope).
// Args[0] string: role, Args[1] string: teamID
func ProcessGetRoleCapabilitiesByTeam(p *process.Process) interface{} {
requireGlobal()
p.ValidateArgNums(2)
caps, err := Global.GetRoleCapabilitiesByTeam(p.ArgsString(0), p.ArgsString(1))
if err != nil {
exception.New(err.Error(), 404).Throw()
}
return capsToMap(caps)
}
// ---------------------------------------------------------------------------
// List Models
// ---------------------------------------------------------------------------
// ProcessListModels returns all enabled models as []Option (system scope).
func ProcessListModels(p *process.Process) interface{} {
requireGlobal()
return optionsToSlice(Global.ListModels())
}
// ProcessListModelsByUser returns models visible to a user.
// Args[0] string: userID
func ProcessListModelsByUser(p *process.Process) interface{} {
requireGlobal()
p.ValidateArgNums(1)
return optionsToSlice(Global.ListModelsByUser(p.ArgsString(0)))
}
// ProcessListModelsByTeam returns models visible to a team.
// Args[0] string: teamID
func ProcessListModelsByTeam(p *process.Process) interface{} {
requireGlobal()
p.ValidateArgNums(1)
return optionsToSlice(Global.ListModelsByTeam(p.ArgsString(0)))
}
// ---------------------------------------------------------------------------
// helpers
// ---------------------------------------------------------------------------
func rolesToMap(roles map[string]RoleTarget) map[string]interface{} {
result := make(map[string]interface{}, len(roles))
for k, v := range roles {
result[k] = map[string]interface{}{
"provider": v.Provider,
"model": v.Model,
}
}
return result
}
func optionsToSlice(opts []connector.Option) []interface{} {
result := make([]interface{}, len(opts))
for i, o := range opts {
result[i] = map[string]interface{}{
"label": o.Label,
"value": o.Value,
}
}
return result
}
func toBool(v interface{}) bool {
switch b := v.(type) {
case bool:
return b
case float64:
return b != 0
case int:
return b != 0
case string:
return b == "true" || b == "1"
default:
return false
}
}