diff --git a/backends.go b/backends.go index d64ed0a..34850b1 100644 --- a/backends.go +++ b/backends.go @@ -5,10 +5,10 @@ import ( "gitea.maximumdirect.net/eric/promptkit/internal/backend" ) -// BackendOpenRouter identifies the maintained OpenRouter backend. +// BackendOpenRouter is the conventional ID for an OpenRouter backend. const BackendOpenRouter = backend.OpenRouterID -// BackendRakestrawHome identifies the maintained Rakestrawhome backend. +// BackendRakestrawHome is the conventional ID for a Rakestrawhome backend. const BackendRakestrawHome = backend.RakestrawHomeID // BackendLocal is a conventional ID for a caller-configured local backend. diff --git a/construction_contract_test.go b/construction_contract_test.go index 18df25e..35761e0 100644 --- a/construction_contract_test.go +++ b/construction_contract_test.go @@ -107,6 +107,36 @@ func TestPublicConstructionWithLocalBackendAndProgrammaticProfile(t *testing.T) } } +func TestFormerCatalogBackendIDsWorkWhenConfigured(t *testing.T) { + openRouterID, rakestrawhomeID := promptkit.BackendOpenRouter, promptkit.BackendRakestrawHome + openRouterEndpoint, rakestrawhomeEndpoint := "https://openrouter.example/v1", "https://inference.example/v1" + model := "configured-model" + engine, err := promptkit.NewEngine(promptkit.Config{Sources: promptkit.ConfigurationSources{ + Underlays: []configkit.Underlay{{Inputs: []configkit.Input{ + {Source: promptkit.BackendSource("application backends", map[string]promptkit.BackendConfig{ + openRouterID: {Endpoint: &openRouterEndpoint}, + rakestrawhomeID: {Endpoint: &rakestrawhomeEndpoint}, + })}, + {Source: promptkit.ProfileSource("application profiles", map[string]promptkit.ProfileConfig{ + "openrouter-profile": {Backend: &openRouterID, Model: &model}, + "rakestrawhome-profile": {Backend: &rakestrawhomeID, Model: &model}, + })}, + }}}, + }}) + if err != nil { + t.Fatalf("construct engine with explicitly configured provider IDs: %v", err) + } + for profileID, backendID := range map[string]string{ + "openrouter-profile": openRouterID, + "rakestrawhome-profile": rakestrawhomeID, + } { + inspection, err := engine.InspectProfile(context.Background(), profileID) + if err != nil || inspection.EffectiveModelParams.BackendID != backendID { + t.Fatalf("inspect %q = (%+v, %v), want backend %q", profileID, inspection, err, backendID) + } + } +} + func TestPublicConstructionCombinesEmbeddedOperatorAndProgrammaticInputs(t *testing.T) { embedded, err := promptkit.FilesystemInputs("embedded defaults", fstest.MapFS{ "config/default.yaml": &fstest.MapFile{Data: []byte(` diff --git a/docs/consumers/pkg-promptkit.md b/docs/consumers/pkg-promptkit.md index 955cb49..80c5f68 100644 --- a/docs/consumers/pkg-promptkit.md +++ b/docs/consumers/pkg-promptkit.md @@ -109,8 +109,8 @@ input := configkit.Input{Source: document} ``` `ProfileSource` and `BackendSource` are smaller typed conveniences when null -is not needed. A caller underlay can overlay catalog backend fields just as it -can overlay profiles; there is no registration-only backend namespace. +is not needed. Backend and profile definitions are supplied by the caller; +there is no registration-only backend namespace. When configuration or message files change, build and validate a new engine, then switch the application to that engine for subsequent work. Existing @@ -614,75 +614,14 @@ rules; the maintained examples show complete runnable workflows. ### Choose A Profile Ownership Model -Choose the profile source that matches the application's continuity needs: - -- Select `promptkit-openrouter-standard` or `promptkit-openrouter-auto` when - the maintained literal name is the desired consumer contract. Their current - selections are in the [format reference](../formats.md#built-in-profile-catalog). -- Select an external catalog profile directly when the application accepts that - profile's catalog volatility. The selected external inventories are linked - from the [format reference](../formats.md#external-profile-inventories). -- Define a complete application-owned profile with an explicit backend or - endpoint, model, and settings when the application owns those choices. - -An application profile that inherits an external base follows later changes to -that base and fails if the base disappears; it is not a frozen copy. Promptkit -tests the catalog versions selected in its module graph. Independently advancing -a catalog dependency can remove an inherited base and produce an untested -combination. Pinning the resolved module graph preserves the embedded data for -a build, but cannot preserve provider availability. Running binaries neither -download catalog updates nor automatically replace withdrawn models. See the -[release policy](../policy/release.md#catalog-profile-compatibility) for the exact -cross-version guarantees. - -### Alias A Built-In Profile - -Give an application-owned profile ID a built-in base when prompts should select -the application ID while inheriting the built-in target. The child can override -only the setting it owns: - -```go -profile := promptkit.ProfileSource("application profiles", map[string]promptkit.ProfileConfig{ - "weather-light": { - Extends: &standardID, - ReasoningEffort: &high, - }, -}) -``` - -Place `profile` in a ConfigKit underlay, with `standardID` set to -`"promptkit-openrouter-standard"` and `high` to `"high"`. Select -`weather-light` in a prompt or `RunRequest.ProfileID`; it remains the -reported selected profile. Definition IDs and inheritance references are exact -keys. If the base is an external profile, apply the volatility guidance -above when deciding whether to use an application-owned complete definition -instead. - -### Use The Rakestrawhome Built-In Profile - -Set `RAKESTRAWHOME_INFERENCE_API_KEY` in the application environment, then -select `rakestrawhome-gemma-4-31b` as an ordinary profile ID. For example, a -prepared result identifies the selected built-in through -`BackendRakestrawHome`: - -```go -prepared, err := engine.Prepare(ctx, promptkit.RunRequest{ - PromptID: "meeting.summary", - ProfileID: "rakestrawhome-gemma-4-31b", - Inputs: inputs, -}) -if err != nil { - return err -} -if prepared.SelectedBackendID != promptkit.BackendRakestrawHome { - return fmt.Errorf("unexpected backend %q", prepared.SelectedBackendID) -} -``` - -The maintained catalog supplies this backend, and callers may overlay its -effective configuration through a later ConfigKit source. Direct request credentials -and runtime endpoint overrides remain supported under their ordinary GoDoc and -format contracts. +Supply backend and profile definitions through the configuration sources your +application selects. PromptKit does not install provider catalogs or built-in +profile inventories; conventional backend ID constants are names only. Define +the model, endpoint, credentials lookup, and settings that your application +intends to support. Profiles may inherit another configured profile using an +exact ID, and caller-selected source layers determine the final values. The +[format reference](../formats.md) describes the definition fields and +composition rules. ### Inspect A Profile Before Prompt Work diff --git a/docs/development.md b/docs/development.md index 384944d..5b0fc8b 100644 --- a/docs/development.md +++ b/docs/development.md @@ -43,13 +43,13 @@ Before starting work: | Root public API | The [architecture policy](policy/architecture.md), [consumer guide](consumers/pkg-promptkit.md), [testing policy](policy/testing.md), and existing GoDoc. | | Prompt, profile, or schema formats | The [framework format reference](formats.md), owning parser or validator package, and [documentation policy](policy/documentation.md). | | Source loading or validation | The [framework format reference](formats.md), [internal source document](internal/sources.md), and owning package tests. | -| Maintained external catalogs or built-ins | The [internal source document](internal/sources.md), [framework format reference](formats.md), [testing policy](policy/testing.md), both catalog module repositories, each catalog's release procedure, and the selected module versions in `go.mod`. | +| Caller-supplied configuration sources | The [internal source document](internal/sources.md), [framework format reference](formats.md), and [testing policy](policy/testing.md). | | Model-client behavior | The [OpenAI-compatible integration contract](integrations/openai-compatible-chat.md), [internal model-client document](internal/llm.md), and owning package tests. | | Internal package implementation | The [architecture policy](policy/architecture.md), [internal component overview](internal/overview.md), and focused internal document listed for that package. | | Tests or test fixtures | The [testing policy](policy/testing.md), owning package, and focused internal document listed by the component overview. | | Maintained example | The [preparation](../examples/go-library/prepare/main.go), [execution](../examples/go-library/run/main.go), and [observation](../examples/go-library/observe/main.go) examples, [consumer guide](consumers/pkg-promptkit.md), [framework format reference](formats.md), and [documentation policy](policy/documentation.md). | | Documentation | The [documentation policy](policy/documentation.md) and canonical owner of every affected contract. | -| Licensing, dependencies/catalog updates, copied material, or attribution | The [OSS policy](policy/oss.md), [project grant](../COPYRIGHT), [license texts](../LICENSE), and [attribution entries](oss/attribution.md); inspect the selected upstream source's notices. | +| Licensing, dependencies, copied material, or attribution | The [OSS policy](policy/oss.md), [project grant](../COPYRIGHT), [license texts](../LICENSE), and [attribution entries](oss/attribution.md); inspect the selected upstream source's notices. | | Release preparation or publication, including release notes | The [release policy](policy/release.md), [release procedure](release.md), and [OSS discoverability rules](policy/oss.md#discoverability). | For cross-cutting changes, follow every applicable row. Do not create diff --git a/docs/formats.md b/docs/formats.md index 344d743..3748e8c 100644 --- a/docs/formats.md +++ b/docs/formats.md @@ -155,16 +155,11 @@ last candidate and its failed validation result. Operational errors remain errors. A request-level `OutputContract` replaces the whole configured one, not individual fields; an empty request format defaults to `text`. -## Built-In Backends +## Backend Definitions -The selected catalog modules contribute these backend definitions as the -earliest underlay. Their settings can be overlaid by caller sources; these IDs -are not reserved against configuration changes. - -| ID | Catalog endpoint | API-key environment name | Active generation limit | -| --- | --- | --- | ---: | -| `openrouter` | `https://openrouter.ai/api/v1` | `OPENROUTER_API_KEY` | 16 | -| `rakestrawhome` | `https://inference.ai.rakestrawhome.com/v1` | `RAKESTRAWHOME_INFERENCE_API_KEY` | 4 | +Backend definitions are supplied through caller-selected configuration +sources. The conventional backend IDs are names only; PromptKit does not +install definitions for them. A backend definition may have `endpoint`, `api_key_env`, `concurrency_limit`, `queue_capacity`, `extra_params`, and `smoke_tests`. The @@ -184,7 +179,7 @@ It may contain `default_profile`, `reachability`, and `authentication`: backends: openrouter: smoke_tests: - default_profile: promptkit-openrouter-standard + default_profile: application-profile reachability: endpoint: https://status.example/ready?check=1 authentication: @@ -218,17 +213,6 @@ its documented fallback, while an explicit blank endpoint is invalid. The metadata does not change ordinary generation credential requirements or provider settings. -The selected v3 catalog modules ship diagnostic defaults in a separate, -opt-in `smoke-tests` root rather than changing the existing `catalog` root. -This Promptkit build selects [OpenRouter v3 `v3.20261013.0`](oss/attribution.md#promptkit-openrouter-catalog-v3) -with default profile `promptkit-openrouter-standard` and adapter -`openrouter_key`, and [Rakestrawhome v3 `v3.20261013.0`](oss/attribution.md#promptkit-rakestrawhome-catalog-v3) -with default profile `rakestrawhome-gemma-4-31b` and adapter `none`. The -attribution entries identify the selected module commits; these catalog -candidates are local and may not be publicly available. A consumer selecting -the extra root needs a compatible loader. These defaults do not promise -provider availability, account access, or free test requests. - ## Profile Definitions Profiles are keyed by literal ID and may inherit a named parent with @@ -289,11 +273,10 @@ does not disable the caller context or HTTP transport timeout. ### Source And Profile Precedence -`NewEngine` composes catalog definitions first, Promptkit-owned definitions -next, then caller underlays in their given order, with an optional main input -last. Later tiers overlay earlier ones. Inputs within one underlay group are -strictly composed: conflicting contributions are errors, not priority -signals. The directory helpers discover `.yaml` and `.yml` files recursively +`NewEngine` composes caller underlays in their given order, followed by an +optional main input. Later layers overlay earlier ones. Inputs within one +underlay group are strictly composed: conflicting contributions are errors, +not priority signals. The directory helpers discover `.yaml` and `.yml` files recursively in sorted order, but that order does not give one file higher precedence. An explicitly selected missing/unreadable root fails; an empty root contributes nothing. Imports are disabled by these helpers unless the caller enables them @@ -315,31 +298,6 @@ second base-chain resolver. Missing references and cycles fail at construction. For generic inheritance and layer mechanics, consult [ConfigKit](https://gitea.maximumdirect.net/eric/ConfigKit). -## Built-In Profile Catalog - -Promptkit contributes two opt-in definitions; neither is selected by default: - -| ID | Current definition | -| --- | --- | -| `promptkit-openrouter-standard` | Extends the selected OpenRouter catalog's `gemini-flash-latest` profile. | -| `promptkit-openrouter-auto` | Selects backend `openrouter` and model `openrouter/auto`. | - -Select one through request `ProfileID` or prompt `default_profile`. -Caller layers may overlay these definitions or their catalog bases. The -[release policy](policy/release.md#catalog-profile-compatibility) owns their -cross-version compatibility boundary. - -## External Profile Inventories - -The selected catalog modules, not this document, own their profile inventories: - -- [OpenRouter catalog v3.20261013.0 profiles](https://gitea.maximumdirect.net/eric/promptkit-backend-openrouter/src/tag/v3.20261013.0/catalog/profiles) -- [Rakestrawhome catalog v3.20261013.0 profiles](https://gitea.maximumdirect.net/eric/promptkit-backend-rakestrawhome/src/tag/v3.20261013.0/catalog/profiles) - -These are tested local data, not a promise of live provider availability. -The [release policy](policy/release.md#catalog-profile-compatibility) owns -the compatibility boundary. - ## Schemas Schemas are JSON documents selected by a prompt or request `schema_path`. diff --git a/docs/internal/overview.md b/docs/internal/overview.md index 57eb230..34d5bfa 100644 --- a/docs/internal/overview.md +++ b/docs/internal/overview.md @@ -16,11 +16,10 @@ contributor workflow and validation. | `examples/go-library/run` | Demonstrates an offline downstream consumer using a prompt file, programmatic profile, inline input, an injected deterministic model client, and `Run`. It is not a public library package. | [Example program](../../examples/go-library/run/main.go) | | `examples/go-library/observe` | Demonstrates offline built-in HTTP retry and synchronous metadata observation across structured proposal and assessment runs using embedded sources, two profiles on one finite backend, and an application decision. It is not a public library package. | [Example program](../../examples/go-library/observe/main.go) | | `internal/backend` | Holds a defensive, immutable lookup of final backend definitions, including diagnostic metadata, plus capacity policies validated by the configuration adapter. | [Backend registry](../../internal/backend/registry.go) | -| `internal/catalog` | Selects released keyed catalog assets and PromptKit-owned profile definitions as ConfigKit underlays; effective validation belongs to the configuration loader. | [Catalog adapter](../../internal/catalog/catalog.go), [internal sources](sources.md#profiles-and-built-ins) | | `internal/configuration` | Loads keyed backend, profile, and prompt configuration through ConfigKit; records effective field presence, validates final values including optional diagnostic metadata, captures indexed prompt definitions, referenced message content, compiled templates, input metadata snapshots, sorted identity-only profile and prompt inventories, backend and presence-aware profile snapshots, and an explicit read-only inspection view. | [Typed loader](../../internal/configuration/load.go), [identity listing](../../internal/configuration/listing.go), [internal sources](sources.md#typed-configuration-loading) | | `internal/capacity` | Owns engine-local bounded execution admission and FIFO model-generation permits for limited backend IDs, including cancellation-safe waiter removal and client wrapping. | [Internal capacity management](capacity.md) | | `internal/domain` | Defines internal framework values for requests, artifacts, prompt definitions, requirement inspection, profiles, execution targets, rendering, generation, validation, and source-neutral smoke diagnostics, and owns source-neutral invariants for shared execution settings, OpenAI-compatible base endpoints, session identifiers, output contracts, and resource-identity syntax and qualification. Source parsing, required fields, other source-specific normalization, defaulting, and boundary-specific error classification remain with their callers. | [Domain declarations](../../internal/domain/domain.go), [requirement values](../../internal/domain/requirements.go), [endpoint invariant](../../internal/domain/endpoint.go) | -| `internal/engine` | Assembles selected catalog and caller configuration into one snapshot, then constructs shared capacity, rendering, validation, generation, repair, the ordinary runner, and the smoke tester for each engine. Built-in engines provide the diagnostic HTTP collaborator; injected-client engines do not. It delegates identity listing directly to the captured configuration inventory rather than the runner. | [Engine assembly](../../internal/engine/engine.go), [internal sources](sources.md#profiles-and-built-ins), [internal runner](runner.md) | +| `internal/engine` | Assembles caller-selected configuration into one snapshot, then constructs shared capacity, rendering, validation, generation, repair, the ordinary runner, and the smoke tester for each engine. Built-in engines provide the diagnostic HTTP collaborator; injected-client engines do not. It delegates identity listing directly to the captured configuration inventory rather than the runner. | [Engine assembly](../../internal/engine/engine.go), [internal sources](sources.md#typed-configuration-loading), [internal runner](runner.md) | | `internal/generation` | Coordinates frozen built-in HTTP attempts and one-shot injected calls through active backend permits, including bounded status retries, cancellable backoff, ordinary attempt observations, and invocation-local evidence for diagnostics. | [Internal generation coordination](generation.md) | | `internal/defaults` | Defines application-neutral framework constants and constructs the default execution target. It contains no CLI, server, or inbound HTTP limits. | [Framework defaults](../../internal/defaults/defaults.go) | | `internal/filecatalog` | Provides deterministic YAML discovery and path helpers for operating-system filesystems and `fs.FS` sources. | [File catalog](../../internal/filecatalog/catalog.go) | diff --git a/docs/internal/sources.md b/docs/internal/sources.md index 315c03d..13e8bc4 100644 --- a/docs/internal/sources.md +++ b/docs/internal/sources.md @@ -39,8 +39,8 @@ of those inventories without consulting ConfigKit or copying definitions. The root `DirectoryInputs` and `FilesystemInputs` helpers discover sorted YAML files under an explicit root, provide resource context, and leave imports disabled. Each returned slice belongs to one strict-composition group; file -order does not confer priority. The engine prepends selected catalog and -Promptkit-owned definitions, then caller underlays and optional main. The +order does not confer priority. Caller underlays are composed in their given +order, followed by the optional main input. The [configuration tests](../../internal/configuration/load_test.go) exercise the real loader and final-value handoff. @@ -112,19 +112,7 @@ dependency-bearing range scopes make discovery incomplete. Uncalled definitions and bypassed session templates are not traversed. This internal walker does not change rendering behavior. -## Profiles And Built-Ins - -`internal/catalog` selects the tagged OpenRouter `v3.20261013.0` and -Rakestrawhome `v3.20261013.0` assets as one ConfigKit underlay and PromptKit-owned -profiles as the next. The selected catalog tags are local candidates and may not -be publicly available. It discovers YAML inputs, including each catalog's -opt-in `smoke-tests` root in addition to its existing `catalog` root, and -supplies passive filesystem context without parsing or pre-validating catalog -definitions. Catalog metadata remains separate from existing `/v3` assets for -strict-loader compatibility. The configuration loader -validates only the final effective backend and profile values, including -consumer overlays of maintained bases or backends. There is no catalog-only -preflight, reserved source namespace, or replay of backend registration. +## Profiles ConfigKit resolves profile inheritance before the handoff. The configuration adapter validates effective references and field values, then builds target @@ -135,8 +123,7 @@ provider work. Preparation applies runtime overrides and then checks target completeness. Identity-only profile selection can identify a partial captured profile without copying its target or requiring readiness. The [target tests](../../internal/configuration/targets_test.go) cover effective -target interpretation, while the [catalog tests](../../internal/catalog/catalog_test.go) -cover selected released assets. +target interpretation. The internal engine delegates listing through a narrow identity-inventory interface directly to the configuration snapshot. The execution runner has no diff --git a/docs/oss/attribution.md b/docs/oss/attribution.md index 9b733d4..c53413a 100644 --- a/docs/oss/attribution.md +++ b/docs/oss/attribution.md @@ -38,36 +38,6 @@ License and notices: [LGPL 3.0](../../LICENSE) | [Source](https://gitea.maximumdirect.net/eric/ConfigKit/src/commit/137769f3f18a370c68444242fb5d88e620e307e5). -## Promptkit OpenRouter Catalog v3 - -Selected module: `gitea.maximumdirect.net/eric/promptkit-backend-openrouter/v3` -at `v3.20261013.0`. - -Copyright (C) 2026 Eric Rakestraw - -SPDX-License-Identifier: MIT - -License and notices: [MIT](licenses/MIT.txt) | -[COPYRIGHT](https://gitea.maximumdirect.net/eric/promptkit-backend-openrouter/src/commit/2a69672b1bb220f1a730153341470f5ac67e15f4/COPYRIGHT) | -[LICENSE](https://gitea.maximumdirect.net/eric/promptkit-backend-openrouter/src/commit/2a69672b1bb220f1a730153341470f5ac67e15f4/LICENSE). - -[Source](https://gitea.maximumdirect.net/eric/promptkit-backend-openrouter/src/commit/2a69672b1bb220f1a730153341470f5ac67e15f4). - -## Promptkit Rakestrawhome Catalog v3 - -Selected module: `gitea.maximumdirect.net/eric/promptkit-backend-rakestrawhome/v3` -at `v3.20261013.0`. - -Copyright (C) 2026 Eric Rakestraw - -SPDX-License-Identifier: MIT - -License and notices: [MIT](licenses/MIT.txt) | -[COPYRIGHT](https://gitea.maximumdirect.net/eric/promptkit-backend-rakestrawhome/src/commit/d40a8aad50111c15d279469170f7b5032ac22529/COPYRIGHT) | -[LICENSE](https://gitea.maximumdirect.net/eric/promptkit-backend-rakestrawhome/src/commit/d40a8aad50111c15d279469170f7b5032ac22529/LICENSE). - -[Source](https://gitea.maximumdirect.net/eric/promptkit-backend-rakestrawhome/src/commit/d40a8aad50111c15d279469170f7b5032ac22529). - ## jsonschema Selected module: `github.com/santhosh-tekuri/jsonschema/v6` at `v6.0.2`. diff --git a/docs/policy/architecture.md b/docs/policy/architecture.md index 922938f..743690e 100644 --- a/docs/policy/architecture.md +++ b/docs/policy/architecture.md @@ -51,8 +51,8 @@ The [internal component overview](../internal/overview.md) owns the inventory of implemented packages and their concrete responsibilities. This policy owns the boundaries and dependency rules those components must preserve. -The internal engine composes selected catalog and caller ConfigKit inputs once, -then assembles the captured definitions, capacity manager, renderer, validator, +The internal engine composes caller-selected ConfigKit inputs once, then +assembles the captured definitions, capacity manager, renderer, validator, outbound client, and use-case runner. The configuration adapter interprets and validates only the final effective values, including presence, null, and resource context. It retains source history for explicit operator inspection, diff --git a/docs/policy/documentation.md b/docs/policy/documentation.md index b741244..a5350c2 100644 --- a/docs/policy/documentation.md +++ b/docs/policy/documentation.md @@ -88,7 +88,7 @@ secret values. | Release-note history | `docs/releases/` | One checked-in summary per source release under the procedure; the note at its immutable tag is the release record. | Current commands, behavior, contracts, and compatibility definitions. | | Complete copyable artifacts | `examples/` | Maintained configuration, inputs, and other copyable or runnable files. | Field-by-field reference, command reference, prose explanation. | | Exact exported Go API | Go declarations and GoDoc, as APIs are implemented | Exported names, signatures, types, values, errors, and exact behavioral contracts. | Task-oriented consumer walkthroughs, implementation details, and future API proposals. | -| Framework file formats | `docs/formats.md` | Prompt-definition and profile YAML fields, schema references, defaults, validation modes, built-in profiles, credentials, and file-to-request precedence. | Exported Go declarations, outbound wire behavior, internal parsing mechanics, and application configuration. | +| Framework file formats | `docs/formats.md` | Prompt-definition and profile YAML fields, schema references, defaults, validation modes, credentials, and file-to-request precedence. | Exported Go declarations, outbound wire behavior, internal parsing mechanics, and application configuration. | Create supporting documents for existing interfaces and responsibilities when substantive content is available. diff --git a/docs/policy/release.md b/docs/policy/release.md index b36e172..4387c7b 100644 --- a/docs/policy/release.md +++ b/docs/policy/release.md @@ -278,29 +278,3 @@ captured publication endpoint before retrying; the repository-specific [procedure](../release.md#failure-recovery) links to the upstream recovery instructions. Do not wait for CI or Go module availability. Licensing footer URLs are preparation work, not a network gate. - -## Catalog Profile Compatibility - -External catalog profile data may be added, removed, renamed, or modified -without deprecation or advance notice. This exception is limited to profile -data imported from the maintained catalog modules. It does not weaken ordinary -compatibility for Promptkit's public Go API, configuration and format semantics, -runtime behavior, catalog-module APIs, manifest schema, backend definitions, or -consumer-owned definitions. - -Within the catalog versions selected and tested by a Promptkit release, and -unless a consumer overrides the name or its base, Promptkit keeps the literal -profile IDs `promptkit-openrouter-standard` and -`promptkit-openrouter-auto` available. The names do not promise a permanent -model, backend, routing behavior, settings, cost, output, or upstream -availability. The [framework format reference](../formats.md#built-in-profile-catalog) -owns their current definitions. - -`v0.10.0` is the initial minor release for this compatibility transition. -Afterward, catalog-only updates and adjustments to the current selections may -ship in a patch release when all other compatibility obligations remain intact. -External profile removals can require consumer action; they are not automatic -migrations. - -Catalog adoption and release validation remain local and offline. They do not -probe provider endpoints, request model lists, or make inference calls. diff --git a/docs/release.md b/docs/release.md index 388e2cd..41461cb 100644 --- a/docs/release.md +++ b/docs/release.md @@ -97,11 +97,6 @@ the push outcome remains unknown. Successful publication needs no remote read-back, CI wait, or installation check. Provider probes and inference calls are not part of validation. -## Catalog Profile Compatibility - -The [release policy](policy/release.md#catalog-profile-compatibility) owns the -catalog compatibility boundary. This heading preserves older incoming links. - ## Failure Recovery For status 1, use the diagnostic to distinguish a validation failure from a diff --git a/docs/roadmap/implementation.md b/docs/roadmap/implementation.md index 1507bb9..f6620bf 100644 --- a/docs/roadmap/implementation.md +++ b/docs/roadmap/implementation.md @@ -566,6 +566,17 @@ backend names still work when configured, and inference smoke tests retain their selection/capability behavior with caller-supplied metadata. Review `go.mod`, `go.sum`, imports, and notices for residual automatic catalog coupling. +Completion: engine construction now composes only caller-selected underlays and +the optional main input; an empty configuration has empty profile and prompt +inventories. Removed the catalog adapter and its tests, provider catalog module +requirements and checksums, catalog attribution entries, and the built-in +profile compatibility exception. Current architecture, source, format, and +consumer documentation now describe caller-supplied definitions; historical +release notes remain unchanged. Explicit OpenRouter and Rakestrawhome backend +IDs continue to work when configured. Validation passed: `GOWORK=off go test +./...`, `GOWORK=off go vet ./...`, `GOWORK=off python3 +scripts/check-examples.py`, and `git diff --check`. + ## Stage 11: Add The Explicit Collection Input Helper Scope: the single public convenience function and its GoDoc. diff --git a/engine.go b/engine.go index 7410b96..66997dd 100644 --- a/engine.go +++ b/engine.go @@ -208,11 +208,11 @@ func WithSchemaFile(path string) Option { // NewEngine constructs an Engine from configuration and options. // -// ConfigKit composes maintained catalog inputs, PromptKit-owned definitions, -// caller underlays, and the optional main input in that order. Construction -// validates the final effective backend, profile, and prompt definitions and -// captures referenced prompt content. A missing prompt collection is allowed. -// Schema documents and request artifacts remain runtime resources. +// ConfigKit composes caller-selected underlays in their given order, followed +// by the optional main input. Construction validates the final effective +// backend, profile, and prompt definitions and captures referenced prompt +// content. A missing prompt collection is allowed. Schema documents and +// request artifacts remain runtime resources. // // NewEngine returns an error matching ErrInvalidConfig for invalid // configuration or options, preserving a ConfigKit LoadError when loading diff --git a/go.mod b/go.mod index 7041f5c..a2450de 100644 --- a/go.mod +++ b/go.mod @@ -4,8 +4,6 @@ go 1.26.0 require ( gitea.maximumdirect.net/eric/ConfigKit v0.8.0 - gitea.maximumdirect.net/eric/promptkit-backend-openrouter/v3 v3.20261013.0 - gitea.maximumdirect.net/eric/promptkit-backend-rakestrawhome/v3 v3.20261013.0 github.com/santhosh-tekuri/jsonschema/v6 v6.0.2 ) diff --git a/go.sum b/go.sum index 47c635f..b50e438 100644 --- a/go.sum +++ b/go.sum @@ -2,10 +2,6 @@ gitea.maximumdirect.net/eric/ConfigKit v0.8.0 h1:xCIhYnOHjw7rFvB+vIremPKGeROOxKT gitea.maximumdirect.net/eric/ConfigKit v0.8.0/go.mod h1:C82sbyKHYMV1OwaD2GJanFrwTfcZfjlW2JsGze5b0kA= gitea.maximumdirect.net/eric/ReleaseKit v0.4.1 h1:Qn+mdebYOzCVCEXgCbuGSO/WHmJtItwaQ8e3XwY3qtI= gitea.maximumdirect.net/eric/ReleaseKit v0.4.1/go.mod h1:LVodqNQ3OIKv9NBq+fFeyMByd1lHY25YOmmpah/NaNI= -gitea.maximumdirect.net/eric/promptkit-backend-openrouter/v3 v3.20261013.0 h1:zhqxVDpQETGzCxYMsVGSWQcjsG4Xw+ftjEL/rCeh0GY= -gitea.maximumdirect.net/eric/promptkit-backend-openrouter/v3 v3.20261013.0/go.mod h1:HjS0f+3q0IdHCTEz+VVAEgIwIWJFAZUjuKdAtvtqKdg= -gitea.maximumdirect.net/eric/promptkit-backend-rakestrawhome/v3 v3.20261013.0 h1:J10u3x+/siHz5ASC+Is4rfYA/ohW6colBT/8OuCA4qQ= -gitea.maximumdirect.net/eric/promptkit-backend-rakestrawhome/v3 v3.20261013.0/go.mod h1:OEFBeKdjTsLzqJwLmXI2O5gG3r64FktcVJVb3mBrRfg= github.com/dlclark/regexp2 v1.11.0 h1:G/nrcoOa7ZXlpoa/91N3X7mM3r8eIlMBBJZvsz/mxKI= github.com/dlclark/regexp2 v1.11.0/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8= github.com/santhosh-tekuri/jsonschema/v6 v6.0.2 h1:KRzFb2m7YtdldCEkzs6KqmJw4nqEVZGK7IN2kJkjTuQ= @@ -18,5 +14,3 @@ golang.org/x/mod v0.40.0 h1:hUv+3cXcdRHz08UmSiOob7sadHig73uo5bkXxQ/tvUs= golang.org/x/mod v0.40.0/go.mod h1:0/weTWkPWGBikyTWAX3dkjVztMmBA5hM0DH6BElSupE= golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= -gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= -gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/internal/backend/registry.go b/internal/backend/registry.go index 0c82903..0a77435 100644 --- a/internal/backend/registry.go +++ b/internal/backend/registry.go @@ -10,9 +10,9 @@ import ( ) const ( - // OpenRouterID identifies the maintained OpenRouter backend. + // OpenRouterID is the conventional OpenRouter backend identifier. OpenRouterID = "openrouter" - // RakestrawHomeID identifies the maintained Rakestrawhome backend. + // RakestrawHomeID is the conventional Rakestrawhome backend identifier. RakestrawHomeID = "rakestrawhome" ) diff --git a/internal/catalog/catalog.go b/internal/catalog/catalog.go deleted file mode 100644 index f543418..0000000 --- a/internal/catalog/catalog.go +++ /dev/null @@ -1,62 +0,0 @@ -// Package catalog selects the maintained configuration assets supplied with -// PromptKit. ConfigKit owns their composition and effective validation. -package catalog - -import ( - "context" - "fmt" - "io/fs" - - "gitea.maximumdirect.net/eric/ConfigKit" - openrouter "gitea.maximumdirect.net/eric/promptkit-backend-openrouter/v3" - rakestrawhome "gitea.maximumdirect.net/eric/promptkit-backend-rakestrawhome/v3" - "gitea.maximumdirect.net/eric/promptkit/internal/backend" - "gitea.maximumdirect.net/eric/promptkit/internal/filecatalog" -) - -// Underlays selects external catalogs followed by PromptKit-owned profiles. -// Neither group is interpreted before the final effective configuration loads. -func Underlays() ([]configkit.Underlay, error) { - openrouterInputs, err := assetInputs("OpenRouter catalog", openrouter.FS(), openrouter.Root) - if err != nil { - return nil, err - } - rakestrawhomeInputs, err := assetInputs("Rakestrawhome catalog", rakestrawhome.FS(), rakestrawhome.Root) - if err != nil { - return nil, err - } - catalogInputs := append(openrouterInputs, rakestrawhomeInputs...) - openrouterSmokeTestInputs, err := assetInputs("OpenRouter smoke-test catalog", openrouter.FS(), openrouter.SmokeTestsRoot) - if err != nil { - return nil, err - } - rakestrawhomeSmokeTestInputs, err := assetInputs("Rakestrawhome smoke-test catalog", rakestrawhome.FS(), rakestrawhome.SmokeTestsRoot) - if err != nil { - return nil, err - } - catalogInputs = append(catalogInputs, openrouterSmokeTestInputs...) - catalogInputs = append(catalogInputs, rakestrawhomeSmokeTestInputs...) - owned := configkit.Input{Source: configkit.Mapping("PromptKit-owned profiles", map[string]any{ - "profiles": map[string]any{ - "promptkit-openrouter-standard": map[string]any{"extends": "gemini-flash-latest"}, - "promptkit-openrouter-auto": map[string]any{"backend": backend.OpenRouterID, "model": "openrouter/auto"}, - }, - })} - return []configkit.Underlay{{Inputs: catalogInputs}, {Inputs: []configkit.Input{owned}}}, nil -} - -func assetInputs(label string, filesystem fs.FS, root string) ([]configkit.Input, error) { - files, err := filecatalog.FindFSYAMLFiles(context.Background(), filesystem, root) - if err != nil { - return nil, fmt.Errorf("discover %s configuration: %w", label, err) - } - namespace := configkit.NewFilesystem(label, filesystem) - inputs := make([]configkit.Input, 0, len(files)) - for _, name := range files { - inputs = append(inputs, configkit.Input{ - Source: namespace.File(name), - Filesystem: &configkit.FilesystemContext{FS: &namespace, Root: root}, - }) - } - return inputs, nil -} diff --git a/internal/catalog/catalog_test.go b/internal/catalog/catalog_test.go deleted file mode 100644 index 9308252..0000000 --- a/internal/catalog/catalog_test.go +++ /dev/null @@ -1,116 +0,0 @@ -package catalog - -import ( - "context" - "errors" - "testing" - - "gitea.maximumdirect.net/eric/ConfigKit" - "gitea.maximumdirect.net/eric/promptkit/internal/configuration" -) - -func TestSelectedCatalogsResolveThroughEffectiveConfiguration(t *testing.T) { - underlays, err := Underlays() - if err != nil { - t.Fatal(err) - } - state, err := configuration.Load(configkit.Input{Source: configkit.NoSource()}, underlays) - if err != nil { - t.Fatal(err) - } - openrouter, err := state.GetBackend("openrouter") - if err != nil || openrouter.Endpoint != "https://openrouter.ai/api/v1" || openrouter.ConcurrencyLimit != 16 || openrouter.QueueCapacity != 1024 { - t.Fatalf("OpenRouter backend: %+v, %v", openrouter, err) - } - if openrouter.SmokeTests == nil || openrouter.SmokeTests.DefaultProfile == nil || - *openrouter.SmokeTests.DefaultProfile != "promptkit-openrouter-standard" || - openrouter.SmokeTests.Authentication == nil || openrouter.SmokeTests.Authentication.Kind == nil || - *openrouter.SmokeTests.Authentication.Kind != "openrouter_key" { - t.Fatalf("OpenRouter smoke-test defaults: %+v", openrouter.SmokeTests) - } - rakestrawhome, err := state.GetBackend("rakestrawhome") - if err != nil || rakestrawhome.Endpoint != "https://inference.ai.rakestrawhome.com/v1" || rakestrawhome.ConcurrencyLimit != 4 { - t.Fatalf("Rakestrawhome backend: %+v, %v", rakestrawhome, err) - } - if rakestrawhome.SmokeTests == nil || rakestrawhome.SmokeTests.DefaultProfile == nil || - *rakestrawhome.SmokeTests.DefaultProfile != "rakestrawhome-gemma-4-31b" || - rakestrawhome.SmokeTests.Authentication == nil || rakestrawhome.SmokeTests.Authentication.Kind == nil || - *rakestrawhome.SmokeTests.Authentication.Kind != "none" { - t.Fatalf("Rakestrawhome smoke-test defaults: %+v", rakestrawhome.SmokeTests) - } - for id, want := range map[string]string{ - "promptkit-openrouter-standard": "~google/gemini-flash-latest", - "promptkit-openrouter-auto": "openrouter/auto", - "rakestrawhome-gemma-4-31b": "google/gemma-4-31b-it", - } { - profile, err := state.GetProfile(context.Background(), id) - if err != nil || profile.ResolvedTarget.Model != want { - t.Fatalf("profile %s: %+v, %v", id, profile, err) - } - } -} - -func TestConsumerCanReplaceOrClearSmokeTestDefaults(t *testing.T) { - underlays, err := Underlays() - if err != nil { - t.Fatal(err) - } - replacement := configkit.Input{Source: configkit.Mapping("operator replacement", map[string]any{ - "backends": map[string]any{"openrouter": map[string]any{ - "smoke_tests": map[string]any{ - "default_profile": "operator-profile", - "authentication": nil, - }, - }}, - })} - state, err := configuration.Load(replacement, underlays) - if err != nil { - t.Fatalf("load replacement: %v", err) - } - backend, err := state.GetBackend("openrouter") - if err != nil || backend.SmokeTests == nil || backend.SmokeTests.DefaultProfile == nil || - *backend.SmokeTests.DefaultProfile != "operator-profile" || backend.SmokeTests.Authentication != nil { - t.Fatalf("caller smoke-test replacement/null was not effective: %+v, %v", backend.SmokeTests, err) - } - - cleared := configkit.Input{Source: configkit.Mapping("operator null", map[string]any{ - "backends": map[string]any{"openrouter": map[string]any{"smoke_tests": nil}}, - })} - state, err = configuration.Load(cleared, underlays) - if err != nil { - t.Fatalf("load null smoke_tests: %v", err) - } - backend, err = state.GetBackend("openrouter") - if err != nil || backend.SmokeTests != nil { - t.Fatalf("caller null did not clear smoke-test defaults: %+v, %v", backend.SmokeTests, err) - } -} - -func TestConsumerOverlayReplacesCatalogDefaultsBeforeValidation(t *testing.T) { - underlays, err := Underlays() - if err != nil { - t.Fatal(err) - } - underlays = append(underlays, configkit.Underlay{Inputs: []configkit.Input{{Source: configkit.Mapping("operator", map[string]any{ - "backends": map[string]any{"openrouter": map[string]any{"endpoint": "invalid lower value"}}, - })}}}) - main := configkit.Input{Source: configkit.Mapping("application", map[string]any{ - "backends": map[string]any{"openrouter": map[string]any{"endpoint": "https://alternate.example/v1"}}, - "profiles": map[string]any{"gemini-flash-latest": map[string]any{"model": "consumer-model"}}, - })} - state, err := configuration.Load(main, underlays) - if err != nil { - t.Fatalf("replaced lower-layer value failed final validation: %v", err) - } - profile, err := state.GetProfile(context.Background(), "promptkit-openrouter-standard") - if err != nil || profile.ResolvedTarget.Model != "consumer-model" || profile.ResolvedTarget.Endpoint != "https://alternate.example/v1" { - t.Fatalf("maintained profile did not observe final configuration: %+v, %v", profile, err) - } - _, err = configuration.Load(configkit.Input{Source: configkit.Mapping("invalid final", map[string]any{ - "backends": map[string]any{"openrouter": map[string]any{"endpoint": "invalid final value"}}, - })}, underlays[:2]) - var loadError *configkit.LoadError - if !errors.As(err, &loadError) { - t.Fatalf("invalid final backend did not produce a ConfigKit load error: %v", err) - } -} diff --git a/internal/engine/engine.go b/internal/engine/engine.go index 3f91f1f..7857a3c 100644 --- a/internal/engine/engine.go +++ b/internal/engine/engine.go @@ -14,7 +14,6 @@ import ( "gitea.maximumdirect.net/eric/ConfigKit" "gitea.maximumdirect.net/eric/promptkit/internal/artifact" "gitea.maximumdirect.net/eric/promptkit/internal/capacity" - "gitea.maximumdirect.net/eric/promptkit/internal/catalog" "gitea.maximumdirect.net/eric/promptkit/internal/configuration" "gitea.maximumdirect.net/eric/promptkit/internal/defaults" "gitea.maximumdirect.net/eric/promptkit/internal/domain" @@ -98,11 +97,7 @@ func New(cfg Config, options Options) (*Engine, error) { if err != nil { return nil, err } - underlays, err := catalog.Underlays() - if err != nil { - return nil, fmt.Errorf("select maintained configuration: %w", err) - } - underlays = append(underlays, cfg.Sources.Underlays...) + underlays := cfg.Sources.Underlays main := configkit.Input{Source: configkit.NoSource()} if cfg.Sources.Main != nil { main = *cfg.Sources.Main diff --git a/listing_contract_test.go b/listing_contract_test.go index 89a27d7..a79e0c7 100644 --- a/listing_contract_test.go +++ b/listing_contract_test.go @@ -185,11 +185,7 @@ func TestListIdentitiesUseEffectiveLayeredMembership(t *testing.T) { } func TestListIdentitiesEmptyEffectiveConfiguration(t *testing.T) { - main := configkit.Input{Source: configkit.Mapping("clear effective identities", map[string]any{ - "profiles": nil, - "prompts": nil, - })} - engine, err := promptkit.NewEngine(promptkit.Config{Sources: promptkit.ConfigurationSources{Main: &main}}) + engine, err := promptkit.NewEngine(promptkit.Config{}) if err != nil { t.Fatal(err) }