From e361c97bb5206d0f4b13536d5f9433e1f564f0cd Mon Sep 17 00:00:00 2001 From: Eric Rakestraw Date: Wed, 29 Jul 2026 23:22:38 +0000 Subject: [PATCH] Document the v0.2.0 upgrade path --- README.md | 5 + docs/policy/documentation.md | 28 +++- docs/releases/v0.2.0.md | 243 +++++++++++++++++++++++++++++++++++ 3 files changed, 273 insertions(+), 3 deletions(-) create mode 100644 docs/releases/v0.2.0.md diff --git a/README.md b/README.md index 297262c..fc1b83c 100644 --- a/README.md +++ b/README.md @@ -31,6 +31,11 @@ Contributors should start with the [development guide](docs/development.md). The [architecture policy](docs/policy/architecture.md) defines the library boundary and constraints that framework work must preserve. +## Release Guidance + +Consumers moving from `v0.1.0` to `v0.2.0` should read the +[v0.2.0 changelog and migration guide](docs/releases/v0.2.0.md). + ## Related Project [Scriptorium](https://gitea.maximumdirect.net/eric/scriptorium) is the CLI and diff --git a/docs/policy/documentation.md b/docs/policy/documentation.md index b0575a9..ae8fb40 100644 --- a/docs/policy/documentation.md +++ b/docs/policy/documentation.md @@ -78,6 +78,7 @@ mechanisms, not secret values. | 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. | | Consumer guidance | `docs/consumers/`, when consumer workflows require dedicated guidance | Task-oriented use of implemented public APIs, minimal examples, and consumer responsibilities. | Exact exported declarations and internal mechanics. | | Durable integration contracts | `docs/integrations/`, when integrations exist | External formats and protocols, compatibility behavior, and upstream or downstream responsibilities. | Internal transformations and public Go declarations. | +| Supplemental release guidance | None. `docs/releases/` may be used when a release benefits from a changelog or migration guide. | No canonical content. These files may briefly summarize release-specific changes, compatibility, and consumer migration paths, and may be corrected, consolidated, archived, or removed when no longer useful. | Public API and behavior contracts, formats, integrations, architecture, release procedure, and the authoritative annotated-tag release record. | | Implemented component inventory | `docs/internal/overview.md` | Current packages and components, their implemented responsibilities, and links to focused internal documents. | Normative architecture, contributor workflow, external contracts, and proposed components. | | Internal subsystem behavior | Other files under `docs/internal/`, when a subsystem needs durable detail | Implementation flow, internal collaborators and state transitions, package-local guarantees and failures, and relevant tests. | Global architecture invariants, public API definitions, and future package plans. | | Architectural decision history | `docs/adr/`, when repository-local decisions require records | Significant decisions, context, alternatives, rationale, consequences, and supersession history. | Current behavior reference, implementation status, and task sequencing. | @@ -85,9 +86,9 @@ mechanisms, not secret values. | Complete copyable artifacts | `examples/` | Valid inputs, Go programs, and other files intended to be copied or run. | Field-by-field reference, exact API declarations, and prose explanation. | Conditional owners do not require placeholder files or directories. Create a -consumer, integration, subsystem, ADR, roadmap, or example document only when -the corresponding implemented interface, decision, planned effort, or -maintained artifact exists. +consumer, integration, release, subsystem, ADR, roadmap, or example document +only when the corresponding implemented interface, release, decision, planned +effort, or maintained artifact exists. ## Boundary Rules @@ -109,6 +110,23 @@ but must link to its canonical definition rather than restate it. The [framework format reference](../formats.md) owns exact prompt, profile, and schema-file contracts. Integration documents own external wire formats. +### Supplemental Release Guidance + +Files under `docs/releases/` may provide changelog-style summaries and +migration guidance for a particular release. They are navigation and +orientation aids, not canonical owners of public APIs, behavior, formats, +integrations, architecture, release procedure, or other durable facts. When a +reader needs detail beyond a short release-specific note, the release document +must link to the applicable canonical documentation rather than reproduce its +contract. + +The annotated tag message required by the +[release procedure](../release.md#write-the-release-note) remains the +authoritative release record. Supplemental release documents may be corrected, +consolidated, archived, or removed at any time when they are no longer useful, +provided maintained documentation does not depend on them and the annotated +tag record remains intact. + ### Security Topics This policy owns what documentation and examples may contain. Architecture owns @@ -160,6 +178,10 @@ durable owners, update incoming links, and archive or remove the roadmap according to repository practice. Do not preserve completed roadmaps as a second current-state reference. +Supplemental release documents may likewise be removed without preserving a +replacement. Before removal, update maintained incoming links so current +documentation does not depend on an optional historical guide. + Before completing documentation work: - verify affected behavior and examples; diff --git a/docs/releases/v0.2.0.md b/docs/releases/v0.2.0.md new file mode 100644 index 0000000..2d31144 --- /dev/null +++ b/docs/releases/v0.2.0.md @@ -0,0 +1,243 @@ +# Promptkit v0.2.0 + +This supplemental changelog and migration guide summarizes the consumer-facing +changes from `v0.1.0` to `v0.2.0`. The annotated `v0.2.0` tag is the +authoritative release record. Exact current contracts belong to the linked +GoDoc and durable documentation. + +## Summary + +`v0.2.0` adds three major capabilities: + +- an engine-scoped registry for reusable OpenAI-compatible backend + definitions; +- bounded, backend-specific run admission and model-generation concurrency; + and +- direct per-run session IDs and tri-state reasoning-effort overrides. + +Existing endpoint-only profiles remain supported. Consumers can adopt backend +registration and runtime overrides incrementally rather than rewriting all +profiles during the upgrade. + +## Compatibility At A Glance + +Promptkit remains pre-`v1`, and this minor release includes source-level and +behavioral changes that deserve review. + +| Area | `v0.1.0` consumer impact | +| --- | --- | +| Endpoint-only profiles | Continue to work without migration. | +| Built-in profiles | Continue to use OpenRouter and `OPENROUTER_API_KEY`; they now select the built-in `openrouter` backend. | +| Custom backends | Registration is optional. Existing profiles may keep their endpoint and credential configuration. | +| Reasoning overrides | String assignments must migrate to the new pointer field. | +| `RunRequest.Metadata` | Removed; delete assignments to this field. | +| OpenRouter concurrency | Now limited to 16 active generations with waiting capacity of 1024 per engine. | +| Public JSON | `v0.2.0` formalizes supported JSON representations; consumers relying on `v0.1.0` encodings should review the notes below. | +| Unkeyed public struct literals | May require updates because fields were added. Keyed literals are recommended. | + +## Upgrade + +After the `v0.2.0` tag is published, update the module dependency with: + +```sh +go get gitea.maximumdirect.net/eric/promptkit@v0.2.0 +go mod tidy +``` + +Run the consuming project's ordinary tests and race-enabled tests after the +upgrade, especially if it calls one engine concurrently or persists Promptkit +JSON values. + +## Backend Registry + +Consumers may now register reusable OpenAI-compatible backend definitions with +`WithBackend`, then select them by ID from file-backed or in-memory profiles. +A backend can supply its endpoint, API-key environment-variable name, +request-wide extra parameters, and optional capacity policy. + +Registrations are immutable and belong to one engine. Consumer registrations +can add new IDs but cannot replace Promptkit's reserved `openrouter` backend. +Profiles that select a backend may still override its endpoint without losing +the backend's routing or capacity identity. + +An existing endpoint-only in-memory profile remains valid: + +```go +promptkit.Profile{ + ID: "local", + Endpoint: "http://localhost:8000/v1", + Model: "example-model", +} +``` + +Adopting the registry is optional and can be done when several profiles should +share connection or capacity settings: + +```go +engine, err := promptkit.NewEngine( + promptkit.Config{PromptDir: "prompts"}, + promptkit.WithBackend(promptkit.Backend{ + ID: "local", + Endpoint: "http://localhost:8000/v1", + APIKeyEnv: "LOCAL_LLM_API_KEY", + }), + promptkit.WithProfiles(promptkit.Profile{ + ID: "local-summary", + BackendID: "local", + Model: "example-model", + }), +) +``` + +See the +[custom-backend consumer guide](../consumers/pkg-promptkit.md#register-a-custom-backend) +for task-oriented usage. The +[`Backend` and `WithBackend` GoDoc](../../backends.go) owns exact registration, +validation, copying, defaulting, and uniqueness semantics. The +[framework format reference](../formats.md) owns the profile `backend` field +and execution precedence. + +## Backend-Specific Concurrency + +Each registered backend may now define: + +- an active model-generation limit; and +- a bounded number of additional admitted `Run` calls. + +Promptkit owns scheduling for both its built-in model client and an injected +`LLMClient`. `Run` remains synchronous: an admitted caller waits for its +ordinary result, while a call beyond the bounded admission capacity returns +`ErrCapacityExceeded`. Capacity is engine-local and keyed by backend ID. +Endpoint-only profiles and custom backends without a configured limit remain +unlimited. + +The built-in OpenRouter backend now permits 16 active generations and 1024 +additional admitted calls per engine. Applications that can exceed this bound +should handle capacity exhaustion separately from provider and request +failures: + +```go +result, err := engine.Run(ctx, request) +if errors.Is(err, promptkit.ErrCapacityExceeded) { + // Apply application-specific overload or retry policy. +} +``` + +Promptkit does not prescribe retries or map this error to an HTTP status. See +the +[concurrency consumer guidance](../consumers/pkg-promptkit.md#limit-backend-concurrency) +and the [`Backend` GoDoc](../../backends.go) for the canonical configuration +contract. Runtime behavior and public error identities belong to the +[`Engine.Run` GoDoc](../../engine.go). + +## Per-Run Session IDs + +`RunRequest.SessionID` can now supply a consumer-managed correlation ID for one +`Prepare` or `Run` invocation. A nonblank direct value overrides the prompt's +session template and is exposed in prepared values, results, injected-client +requests, and provider observability. Session IDs should therefore be stable, +non-secret values. + +```go +result, err := engine.Run(ctx, promptkit.RunRequest{ + PromptID: "meeting.summary", + SessionID: "conversation-42", +}) +``` + +The built-in OpenAI-compatible client sends a nonempty effective session as the +top-level `session_id` request-body field, not as an `x-session-id` header. See +the +[session and reasoning consumer guide](../consumers/pkg-promptkit.md#set-a-per-run-session-and-reasoning), +the [`RunRequest` GoDoc](../../types.go), and the +[OpenAI-compatible request contract](../integrations/openai-compatible-chat.md#request-body) +for exact normalization, length, exposure, and wire behavior. + +## Per-Run Reasoning Effort + +`ExecutionTargetOverride.ReasoningEffort` changed from `string` to `*string` so +one request can distinguish inheritance, replacement, and explicit clearing. + +Update a `v0.1.0` override like this: + +```go +// v0.1.0 +Execution: &promptkit.ExecutionTargetOverride{ + ReasoningEffort: "high", +} +``` + +to: + +```go +// v0.2.0 +reasoning := "high" +Execution: &promptkit.ExecutionTargetOverride{ + ReasoningEffort: &reasoning, +} +``` + +The three states are: + +- `nil` inherits the selected profile's value; +- a pointer to a nonblank string replaces it for that invocation; and +- a pointer to an empty or whitespace-only string clears it for that + invocation. + +This allows consumers to consolidate profiles that differed only by reasoning +effort. The [`ExecutionTargetOverride` GoDoc](../../types.go) owns the exact +override contract. + +## Other Migration Notes + +### Remove `RunRequest.Metadata` + +`RunRequest.Metadata` is no longer part of the public request. Remove any +assignment to that field. Use application-owned state keyed by `RunResult.RunID` +or a direct `SessionID` when correlation is needed; these identifiers have +different purposes, so choose according to the application's lifecycle. + +### Review Persisted JSON + +`v0.2.0` defines stable JSON representations for the public result, artifact, +execution, validation, and model-client values listed in the +[package documentation](../../doc.go). Consumers that treated `v0.1.0` +reflection-derived encodings as stable should update fixtures and stored-data +adapters. + +In particular: + +- `RunResult` encodes elapsed time as integer milliseconds in `duration_ms` + instead of encoding `time.Duration` under `duration`; +- result JSON can include the new `session_id` and `selected_backend_id` + fields; +- execution-target JSON can include `backend_id`; and +- artifact and target-presence fields now use their documented lower-case + names. + +The `v0.2.0` `RunResult` decoder reads `duration_ms`; it does not translate a +persisted `v0.1.0` `duration` field. Transform old payloads before decoding +when preserving their elapsed duration matters. + +### Prefer Keyed Struct Literals + +New fields were added to several public structs. Replace positional composite +literals with keyed literals so future additive fields do not cause another +source migration. + +## Migration Checklist + +- Update the module dependency and run the consumer's tests. +- Change reasoning overrides from strings to pointers. +- Remove uses of `RunRequest.Metadata`. +- Review unkeyed Promptkit struct literals. +- Decide whether shared endpoints should move into registered backends. +- If using built-in OpenRouter profiles at high concurrency, handle + `ErrCapacityExceeded` and review the new engine-local bound. +- Review stored JSON, fixtures, and downstream decoders. +- Optionally replace profile-specific session or reasoning variants with + per-run overrides. + +For complete consumer workflows, use the +[package consumer guide](../consumers/pkg-promptkit.md) and maintained +[offline execution example](../../examples/go-library/run/main.go).