191 lines
5.9 KiB
Go
191 lines
5.9 KiB
Go
package configuration
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
|
|
"gitea.maximumdirect.net/eric/promptkit/internal/backend"
|
|
"gitea.maximumdirect.net/eric/promptkit/internal/defaults"
|
|
"gitea.maximumdirect.net/eric/promptkit/internal/domain"
|
|
"gitea.maximumdirect.net/eric/promptkit/internal/jsonvalue"
|
|
profilepkg "gitea.maximumdirect.net/eric/promptkit/internal/profile"
|
|
)
|
|
|
|
func (state *loaded) buildExecutionProfiles() error {
|
|
definitions := make(map[string]domain.Backend, len(state.result.Config.Backends))
|
|
for _, id := range sortedKeys(state.result.Config.Backends) {
|
|
config := state.result.Config.Backends[id]
|
|
params, err := jsonvalue.CopyMap(config.ExtraParams)
|
|
if err != nil {
|
|
return fmt.Errorf("backend %q extra parameters: %w", id, err)
|
|
}
|
|
definition := domain.Backend{ID: id, ExtraParams: params}
|
|
if config.Endpoint != nil {
|
|
definition.Endpoint = *config.Endpoint
|
|
}
|
|
if config.APIKeyEnv != nil {
|
|
definition.APIKeyEnv = *config.APIKeyEnv
|
|
}
|
|
if config.ConcurrencyLimit != nil {
|
|
definition.ConcurrencyLimit = *config.ConcurrencyLimit
|
|
}
|
|
if config.QueueCapacity != nil {
|
|
definition.QueueCapacity = *config.QueueCapacity
|
|
definition.QueueCapacitySet = definition.ConcurrencyLimit > 0
|
|
}
|
|
definitions[id] = definition
|
|
}
|
|
registry, err := backend.NewEffectiveRegistry(definitions)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
state.registry = registry
|
|
state.indexedProfiles = make(map[string]domain.ExecutionProfile, len(state.result.Config.Profiles))
|
|
for _, id := range sortedKeys(state.result.Config.Profiles) {
|
|
config := state.result.Config.Profiles[id]
|
|
fields := state.profiles[id]
|
|
target := defaults.ExecutionTargetDefault()
|
|
if config.Backend != nil && *config.Backend != "" {
|
|
selected, err := registry.GetBackend(*config.Backend)
|
|
if err != nil {
|
|
return fmt.Errorf("profile %q backend: %w", id, err)
|
|
}
|
|
target.BackendID = selected.ID
|
|
target.Endpoint = selected.Endpoint
|
|
target.APIKeyEnv = selected.APIKeyEnv
|
|
target.ExtraParams = selected.ExtraParams
|
|
}
|
|
presence := domain.ExecutionTargetPresence{}
|
|
if fields["endpoint"] != fieldAbsent {
|
|
target.Endpoint = valueOrZero(config.Endpoint)
|
|
}
|
|
if fields["model"] != fieldAbsent {
|
|
target.Model = valueOrZero(config.Model)
|
|
}
|
|
if fields["temperature"] != fieldAbsent {
|
|
target.Temperature = valueOrZero(config.Temperature)
|
|
presence.Temperature = fields["temperature"] == fieldValue
|
|
}
|
|
if fields["top_p"] != fieldAbsent {
|
|
target.TopP = valueOrZero(config.TopP)
|
|
presence.TopP = fields["top_p"] == fieldValue
|
|
}
|
|
if fields["max_tokens"] != fieldAbsent {
|
|
target.MaxTokens = valueOrZero(config.MaxTokens)
|
|
presence.MaxTokens = fields["max_tokens"] == fieldValue
|
|
}
|
|
if fields["timeout_seconds"] == fieldValue {
|
|
target.TimeoutSeconds = valueOrZero(config.TimeoutSeconds)
|
|
presence.TimeoutSeconds = true
|
|
}
|
|
if fields["service_tier"] != fieldAbsent {
|
|
target.ServiceTier = valueOrZero(config.ServiceTier)
|
|
}
|
|
if fields["reasoning_effort"] != fieldAbsent {
|
|
target.ReasoningEffort = valueOrZero(config.ReasoningEffort)
|
|
}
|
|
if fields["api_key_env"] != fieldAbsent {
|
|
target.APIKeyEnv = valueOrZero(config.APIKeyEnv)
|
|
}
|
|
if fields["api_key_required"] != fieldAbsent {
|
|
target.APIKeyRequired = valueOrZero(config.APIKeyRequired)
|
|
}
|
|
if target.APIKeyRequired && fields["api_key_env"] == fieldAbsent {
|
|
target.APIKeyEnv = ""
|
|
}
|
|
switch fields["extra_params"] {
|
|
case fieldNull:
|
|
target.ExtraParams = nil
|
|
case fieldValue:
|
|
merged, err := overlayProviderMap(target.ExtraParams, config.ExtraParams)
|
|
if err != nil {
|
|
return fmt.Errorf("profile %q extra parameters: %w", id, err)
|
|
}
|
|
target.ExtraParams = merged
|
|
}
|
|
profile := domain.ExecutionProfile{ID: id, ResolvedTarget: &target, TargetPresence: presence}
|
|
state.indexedProfiles[id] = profile
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func valueOrZero[T any](value *T) T {
|
|
if value == nil {
|
|
var zero T
|
|
return zero
|
|
}
|
|
return *value
|
|
}
|
|
|
|
// overlayProviderMap applies only the backend-to-profile JSON-object default
|
|
// merge. ConfigKit has already resolved source layering and inheritance.
|
|
func overlayProviderMap(lower, upper map[string]any) (map[string]any, error) {
|
|
merged, err := jsonvalue.CopyMap(lower)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if merged == nil {
|
|
merged = make(map[string]any)
|
|
}
|
|
for key, value := range upper {
|
|
if child, ok := value.(map[string]any); ok {
|
|
if existing, ok := merged[key].(map[string]any); ok {
|
|
combined, err := overlayProviderMap(existing, child)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
merged[key] = combined
|
|
continue
|
|
}
|
|
}
|
|
copied, err := jsonvalue.Copy(value)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
merged[key] = copied
|
|
}
|
|
return merged, nil
|
|
}
|
|
|
|
// GetProfile returns an independently owned indexed profile for the internal
|
|
// runner's lookup boundary.
|
|
func (state *loaded) GetProfile(ctx context.Context, id string) (*domain.ExecutionProfile, error) {
|
|
if err := ctx.Err(); err != nil {
|
|
return nil, err
|
|
}
|
|
if state == nil {
|
|
return nil, fmt.Errorf("%w: %q", profilepkg.ErrProfileNotFound, id)
|
|
}
|
|
profile, found := state.indexedProfiles[id]
|
|
if !found {
|
|
return nil, fmt.Errorf("%w: %q", profilepkg.ErrProfileNotFound, id)
|
|
}
|
|
target := *profile.ResolvedTarget
|
|
params, err := jsonvalue.CopyMap(target.ExtraParams)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
target.ExtraParams = params
|
|
profile.ResolvedTarget = &target
|
|
return &profile, nil
|
|
}
|
|
|
|
// GetBackend returns an independently owned effective backend definition.
|
|
func (state *loaded) GetBackend(id string) (domain.Backend, error) {
|
|
if state == nil {
|
|
return domain.Backend{}, fmt.Errorf("backend registry is not configured")
|
|
}
|
|
return state.registry.GetBackend(id)
|
|
}
|
|
|
|
// CapacityPolicies returns the selected backend admission policies.
|
|
func (state *loaded) CapacityPolicies() map[string]domain.BackendCapacityPolicy {
|
|
if state == nil {
|
|
return nil
|
|
}
|
|
return state.registry.CapacityPolicies()
|
|
}
|
|
|
|
// ExactIDs indicates that ConfigKit definition keys are literal identities.
|
|
func (state *loaded) ExactIDs() bool { return true }
|