6.4 KiB
Local Backend Convenience
Status: Complete.
Purpose
Make the common case of using a local OpenAI-compatible endpoint concise and easy to discover without introducing implicit configuration or a separate backend abstraction.
The existing Backend type and registry remain the canonical, fully
configurable interface. A small convenience constructor will cover the usual
local-network case, while improved consumer documentation will make it clear
when an endpoint-only profile, the convenience constructor, or a complete
Backend value is appropriate.
Motivation
Consumers can already use a local endpoint by setting Profile.Endpoint, or
register one as a backend with WithBackend. The first option is concise but
does not provide shared backend-level concurrency control. The second supports
the complete backend feature set but requires consumers to understand and
populate several fields for a common configuration.
Most consumers adding a local backend need only:
- a stable backend ID;
- an OpenAI-compatible endpoint; and
- a concurrency limit appropriate for the local server.
Promptkit should provide a direct path for that case while keeping all configuration explicit and preserving the full registry interface for advanced needs.
Consumer Paths
Documentation should present three progressively more configurable paths:
- Set
Profile.Endpointwhen a profile only needs to target a local endpoint and does not need shared backend policy. - Use the local-backend convenience constructor when profiles should share a named local endpoint and its concurrency limit.
- Construct a complete
Backendvalue when the consumer needs a custom backend ID, authentication, extra request parameters, an explicit queue capacity, or multiple local backends.
These are complementary interfaces. The convenience constructor must return an
ordinary Backend, so it does not create a second configuration model.
Public Convenience API
The public package should expose:
const BackendLocal = "local"
func LocalBackend(endpoint string, concurrencyLimit int) Backend
LocalBackend should return a Backend with:
IDset toBackendLocal;Endpointset to the supplied endpoint;ConcurrencyLimitset to the supplied limit; and- all other fields left at their zero values.
The returned value is passed to WithBackend and follows the same copying,
normalization, validation, and registration rules as any consumer-constructed
Backend.
The constructor should be a transparent value constructor. It should not read
environment variables, mutate global state, register the backend, validate
arguments independently, or create profiles. Consumers may inspect or modify
the returned value before registration, although documentation should direct
substantially customized configurations to the full Backend form.
Identity and Registration
BackendLocal is a conventional ID used by the convenience constructor. It is
not pre-registered and should not become a specially reserved registry ID.
Consumers remain responsible for registering the returned backend with
WithBackend and naming it from profiles through BackendID.
This distinction preserves compatibility with consumers that may already
register their own backend using the ID "local". Normal duplicate-ID rules
still apply if a consumer attempts to register more than one backend with that
ID.
Consumers that need multiple local endpoints should choose distinct IDs and
use complete Backend values rather than the single conventional helper ID.
Concurrency and Queue Semantics
The constructor must preserve the existing backend concurrency contract:
- a positive concurrency limit bounds simultaneous requests and uses the
existing default queue capacity because
QueueCapacityremainsnil; - a zero concurrency limit leaves the backend unconstrained; and
- a negative concurrency limit is rejected through the existing engine configuration validation path.
The constructor should not select a hidden default concurrency limit. Local servers vary substantially in capacity, so the consumer should make this choice explicitly.
Documentation
The final documentation state has two canonical surfaces:
- Public Go documentation describes the exact contract of
BackendLocalandLocalBackend, including their conventional, non-pre-registered nature. - The promptkit consumer guide includes a task-oriented local-endpoint section that shows the three consumer paths, explains the decision between them, and provides concise examples of the endpoint-only and convenience-constructor forms.
The consumer guide continues to document the full Backend interface as
the advanced path rather than attempting to reproduce every configuration
variation through convenience APIs.
Compatibility
This feature is additive:
- existing endpoint-only profiles continue to work unchanged;
- existing
Backendvalues andWithBackendregistrations remain the canonical general-purpose interface; - existing registrations using the literal ID
"local"remain valid; and - OpenRouter defaults and all other backend behavior remain unchanged.
No consumer is required to adopt the convenience constructor.
Non-Goals
This work does not include:
- pre-registering or implicitly enabling a local backend;
- discovering a local endpoint, API key, or concurrency limit from environment variables;
- adding local-backend fields to
Config; - selecting a default local model or creating a profile automatically;
- adding a combined backend-and-profile constructor;
- adding convenience parameters for API keys, extra request parameters, or queue capacity;
- replacing or redesigning the backend registry;
- adding support for non-OpenAI-compatible local APIs; or
- changing backend routing, scheduling, or queue behavior.
Target End State
After this work:
- consumers with a simple one-profile local endpoint can continue to configure it directly on the profile;
- consumers needing a shared local endpoint and concurrency policy can express
it with one
LocalBackendcall and register the returned value normally; - consumers with advanced or multiple-local-backend requirements have a clear
path to the complete
Backendinterface; - all local configuration remains explicit, inspectable, and compatible with dependency injection; and
- canonical documentation makes the simplest suitable interface easy to find without obscuring the underlying registry model.