Cut over public engine construction to ConfigKit sources
This commit is contained in:
138
backends.go
138
backends.go
@@ -1,107 +1,69 @@
|
||||
package promptkit
|
||||
|
||||
import (
|
||||
"gitea.maximumdirect.net/eric/ConfigKit"
|
||||
"gitea.maximumdirect.net/eric/promptkit/internal/backend"
|
||||
"gitea.maximumdirect.net/eric/promptkit/internal/domain"
|
||||
)
|
||||
|
||||
// BackendOpenRouter is the reserved ID of Promptkit's built-in OpenRouter
|
||||
// backend.
|
||||
// BackendOpenRouter identifies the maintained OpenRouter backend.
|
||||
const BackendOpenRouter = backend.OpenRouterID
|
||||
|
||||
// BackendRakestrawHome is the reserved ID of Promptkit's built-in
|
||||
// Rakestrawhome backend.
|
||||
// BackendRakestrawHome identifies the maintained Rakestrawhome backend.
|
||||
const BackendRakestrawHome = backend.RakestrawHomeID
|
||||
|
||||
// BackendLocal is the case-sensitive conventional ID used by [LocalBackend].
|
||||
// It is not a built-in or reserved backend and must be registered with
|
||||
// [WithBackend].
|
||||
// BackendLocal is a conventional ID for a caller-configured local backend.
|
||||
const BackendLocal = "local"
|
||||
|
||||
// Backend configures one engine-scoped OpenAI-compatible backend.
|
||||
//
|
||||
// Backend has no stable JSON representation. Use keyed literals so additions
|
||||
// to this configuration value do not break source compatibility.
|
||||
type Backend struct {
|
||||
// ID is the stable, case-sensitive registry key. NewEngine trims it and
|
||||
// requires a non-blank value. Built-in backend IDs are reserved.
|
||||
ID string
|
||||
// Endpoint is the OpenAI-compatible base endpoint. NewEngine trims it and
|
||||
// requires an absolute HTTP or HTTPS URL with a host and without user
|
||||
// information, a query string, or a fragment. Paths are allowed.
|
||||
Endpoint string
|
||||
// APIKeyEnv optionally names an environment lookup source for an API key.
|
||||
// NewEngine trims it and requires the portable form [A-Za-z_][A-Za-z0-9_]*.
|
||||
// A direct RunRequest.APIKey takes precedence. When no usable credential is
|
||||
// available, the built-in client omits Authorization; injected clients own
|
||||
// their own credential-resolution behavior. Store only the name, never a
|
||||
// credential value.
|
||||
APIKeyEnv string
|
||||
// ExtraParams contains backend-wide request defaults. Values must be
|
||||
// JSON-compatible, finite, acyclic, and keyed by non-empty strings. Keys
|
||||
// must not be model, session_id, messages, temperature, max_tokens, top_p,
|
||||
// service_tier, reasoning_effort, or response_format. An empty map supplies
|
||||
// no defaults. NewEngine deeply copies the map and rejects excessively deep
|
||||
// or large values for safety.
|
||||
ExtraParams map[string]any
|
||||
// ConcurrencyLimit is the maximum number of simultaneous model-generation
|
||||
// calls allowed for this backend within one Engine. Zero leaves the backend
|
||||
// unlimited. A negative value makes NewEngine fail with ErrInvalidConfig.
|
||||
ConcurrencyLimit int
|
||||
// QueueCapacity controls how many additional Run or RunPrepared calls may
|
||||
// be admitted beyond ConcurrencyLimit. Nil uses 1024 when ConcurrencyLimit
|
||||
// is positive; a pointer uses its exact value, including zero. The pointed-to
|
||||
// value must be non-negative, and QueueCapacity must be nil when
|
||||
// ConcurrencyLimit is zero. Their sum must fit in an int. WithBackend copies
|
||||
// the value and does not retain the pointer.
|
||||
// BackendConfig is a presence-aware convenience value for a keyed backend
|
||||
// definition. Nil pointer fields are omitted, while non-nil pointers preserve
|
||||
// explicit zero or blank values. A nil ExtraParams map is omitted; a non-nil
|
||||
// empty map supplies an empty mapping. Use raw ConfigKit Mapping inputs for
|
||||
// explicit null. BackendConfig has no stable JSON representation.
|
||||
type BackendConfig struct {
|
||||
// Endpoint is the required absolute HTTP(S) base URL after all layers merge.
|
||||
Endpoint *string
|
||||
// APIKeyEnv optionally names an environment variable, not a credential.
|
||||
APIKeyEnv *string
|
||||
// ConcurrencyLimit is zero for unlimited generation; negative is invalid.
|
||||
ConcurrencyLimit *int
|
||||
// QueueCapacity defaults to 1024 for positive concurrency when omitted.
|
||||
// An explicit value, including zero, requires positive concurrency.
|
||||
QueueCapacity *int
|
||||
// ExtraParams supplies JSON-compatible provider request defaults.
|
||||
ExtraParams map[string]any
|
||||
}
|
||||
|
||||
// LocalBackend returns a caller-owned Backend for a conventional local
|
||||
// OpenAI-compatible endpoint. It sets ID to BackendLocal and copies endpoint
|
||||
// and concurrencyLimit into Endpoint and ConcurrencyLimit without
|
||||
// normalization or validation. APIKeyEnv, ExtraParams, and QueueCapacity keep
|
||||
// their zero values.
|
||||
//
|
||||
// LocalBackend does not read environment variables, register the value, or
|
||||
// mutate engine or package state. Supply the returned value through
|
||||
// [WithBackend]; [NewEngine] then applies the ordinary backend validation and
|
||||
// concurrency semantics, including default queue capacity for a positive
|
||||
// limit, unlimited behavior for zero, and ErrInvalidConfig for a negative
|
||||
// limit.
|
||||
func LocalBackend(endpoint string, concurrencyLimit int) Backend {
|
||||
return Backend{
|
||||
ID: BackendLocal,
|
||||
Endpoint: endpoint,
|
||||
ConcurrencyLimit: concurrencyLimit,
|
||||
// BackendSource creates a labeled ConfigKit mapping from keyed backend values.
|
||||
// It encodes fields without validating them; NewEngine validates the final
|
||||
// effective configuration after all selected source layers are combined.
|
||||
// Mapping captures a copy of supported input values at creation; unsupported
|
||||
// values fail when the source is loaded.
|
||||
func BackendSource(label string, backends map[string]BackendConfig) configkit.Source {
|
||||
definitions := make(map[string]any, len(backends))
|
||||
for id, config := range backends {
|
||||
fields := make(map[string]any)
|
||||
if config.Endpoint != nil {
|
||||
fields["endpoint"] = *config.Endpoint
|
||||
}
|
||||
if config.APIKeyEnv != nil {
|
||||
fields["api_key_env"] = *config.APIKeyEnv
|
||||
}
|
||||
if config.ConcurrencyLimit != nil {
|
||||
fields["concurrency_limit"] = *config.ConcurrencyLimit
|
||||
}
|
||||
if config.QueueCapacity != nil {
|
||||
fields["queue_capacity"] = *config.QueueCapacity
|
||||
}
|
||||
if config.ExtraParams != nil {
|
||||
fields["extra_params"] = providerMapForMapping(config.ExtraParams)
|
||||
}
|
||||
definitions[id] = fields
|
||||
}
|
||||
return configkit.Mapping(label, map[string]any{"backends": definitions})
|
||||
}
|
||||
|
||||
// WithBackend adds one Backend registration to the constructed Engine.
|
||||
//
|
||||
// Registrations accumulate in option order. Every normalized ID must be unique
|
||||
// across consumer registrations and built-ins; a duplicate or invalid
|
||||
// definition makes NewEngine fail with ErrInvalidConfig. Built-in IDs,
|
||||
// including [BackendOpenRouter] and [BackendRakestrawHome], cannot be
|
||||
// replaced. The immutable registration is scoped to the resulting Engine and
|
||||
// cannot be enumerated, replaced, removed, or mutated after construction.
|
||||
// WithBackend does not install package-global state.
|
||||
func WithBackend(backend Backend) Option {
|
||||
queueCapacity := 0
|
||||
queueCapacitySet := backend.QueueCapacity != nil
|
||||
if queueCapacitySet {
|
||||
queueCapacity = *backend.QueueCapacity
|
||||
}
|
||||
return optionFunc(func(options *engineOptions) error {
|
||||
options.internal.AddBackend(domain.Backend{
|
||||
ID: backend.ID,
|
||||
Endpoint: backend.Endpoint,
|
||||
APIKeyEnv: backend.APIKeyEnv,
|
||||
ExtraParams: backend.ExtraParams,
|
||||
ConcurrencyLimit: backend.ConcurrencyLimit,
|
||||
QueueCapacity: queueCapacity,
|
||||
QueueCapacitySet: queueCapacitySet,
|
||||
})
|
||||
return nil
|
||||
})
|
||||
// LocalBackend supplies endpoint and concurrency pointers for a caller-chosen
|
||||
// backend ID; it does not select an ID or validate either value.
|
||||
func LocalBackend(endpoint string, concurrencyLimit int) BackendConfig {
|
||||
return BackendConfig{Endpoint: &endpoint, ConcurrencyLimit: &concurrencyLimit}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user