Changeset 0.10.2 (#58)

This commit is contained in:
2026-02-24 20:29:08 +00:00
parent 13772ebd6c
commit 4061e4a145
20 changed files with 1223 additions and 41 deletions

View File

@@ -14,13 +14,14 @@ import (
// ── Known Roles ────────────────────────────
const (
RoleUtility = "utility" // Internal tasks: summarization, title generation
RoleEmbedding = "embedding" // Vector embedding for knowledge bases
RoleGeneration = "generation" // Primary chat generation (future routing)
RoleUtility = "utility" // Internal tasks: summarization, title generation
RoleEmbedding = "embedding" // Vector embedding for knowledge bases
)
// ValidRoles lists all recognized role names.
var ValidRoles = []string{RoleUtility, RoleEmbedding, RoleGeneration}
// Note: "generation" (image/media) was removed in v0.10.2 — image gen
// will be extension-managed with its own provider config, not a role slot.
var ValidRoles = []string{RoleUtility, RoleEmbedding}
// IsValidRole returns true if the given role name is recognized.
func IsValidRole(role string) bool {
@@ -96,9 +97,9 @@ func NewResolver(s store.Stores, vault *crypto.KeyResolver) *Resolver {
}
// Complete sends a chat completion using the named role.
// Resolution: team override → global config → try primary → fallback on error.
func (r *Resolver) Complete(ctx context.Context, role string, teamID *string, messages []providers.Message) (*CompletionResult, error) {
cfg, err := r.GetConfig(ctx, role, teamID)
// Resolution: personal override → team override → global config → try primary → fallback on error.
func (r *Resolver) Complete(ctx context.Context, role string, userID string, teamID *string, messages []providers.Message) (*CompletionResult, error) {
cfg, err := r.GetConfig(ctx, role, userID, teamID)
if err != nil {
return nil, err
}
@@ -129,8 +130,8 @@ func (r *Resolver) Complete(ctx context.Context, role string, teamID *string, me
}
// Embed generates embeddings using the named role.
func (r *Resolver) Embed(ctx context.Context, role string, teamID *string, input []string) (*EmbeddingResult, error) {
cfg, err := r.GetConfig(ctx, role, teamID)
func (r *Resolver) Embed(ctx context.Context, role string, userID string, teamID *string, input []string) (*EmbeddingResult, error) {
cfg, err := r.GetConfig(ctx, role, userID, teamID)
if err != nil {
return nil, err
}
@@ -161,13 +162,21 @@ func (r *Resolver) Embed(ctx context.Context, role string, teamID *string, input
}
// GetConfig returns the resolved role config for the given role name.
// Team overrides take precedence over global config.
func (r *Resolver) GetConfig(ctx context.Context, role string, teamID *string) (*RoleConfig, error) {
// Resolution order: personal override → team override → global config.
func (r *Resolver) GetConfig(ctx context.Context, role string, userID string, teamID *string) (*RoleConfig, error) {
if !IsValidRole(role) {
return nil, fmt.Errorf("unknown role: %q", role)
}
// Check team override first
// Check personal override first (BYOK users)
if userID != "" {
personalCfg, err := r.getPersonalRoleConfig(ctx, userID, role)
if err == nil && personalCfg != nil && (personalCfg.Primary != nil || personalCfg.Fallback != nil) {
return personalCfg, nil
}
}
// Check team override
if teamID != nil && *teamID != "" {
teamCfg, err := r.getTeamRoleConfig(ctx, *teamID, role)
if err == nil && teamCfg != nil && (teamCfg.Primary != nil || teamCfg.Fallback != nil) {
@@ -181,7 +190,13 @@ func (r *Resolver) GetConfig(ctx context.Context, role string, teamID *string) (
// IsConfigured returns true if the named role has at least a primary binding.
func (r *Resolver) IsConfigured(ctx context.Context, role string) bool {
cfg, err := r.GetConfig(ctx, role, nil)
cfg, err := r.GetConfig(ctx, role, "", nil)
return err == nil && cfg != nil && cfg.Primary != nil
}
// IsPersonalOverride returns true if the user has a personal binding for the role.
func (r *Resolver) IsPersonalOverride(ctx context.Context, userID, role string) bool {
cfg, err := r.getPersonalRoleConfig(ctx, userID, role)
return err == nil && cfg != nil && cfg.Primary != nil
}
@@ -285,6 +300,35 @@ func (r *Resolver) resolveBinding(ctx context.Context, binding *RoleBinding) (pr
// ── Internal: Config Loading ───────────────
func (r *Resolver) getPersonalRoleConfig(ctx context.Context, userID, role string) (*RoleConfig, error) {
user, err := r.stores.Users.GetByID(ctx, userID)
if err != nil {
return nil, err
}
settings := user.Settings
if settings == nil {
return nil, nil
}
rolesRaw, ok := settings["model_roles"]
if !ok {
return nil, nil
}
rolesMap, ok := rolesRaw.(map[string]interface{})
if !ok {
return nil, nil
}
roleData, ok := rolesMap[role]
if !ok {
return nil, nil
}
return parseRoleConfig(roleData)
}
func (r *Resolver) getGlobalRoleConfig(ctx context.Context, role string) (*RoleConfig, error) {
allRoles, err := r.stores.GlobalConfig.Get(ctx, "model_roles")
if err != nil {