Files
promptkit/docs/releases/v0.2.0.md

8.9 KiB

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:

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:

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:

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 local-endpoint consumer guide for task-oriented usage. The Backend and WithBackend GoDoc owns exact registration, validation, copying, defaulting, and uniqueness semantics. The framework format reference 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:

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 and the Backend GoDoc for the canonical configuration contract. Runtime behavior and public error identities belong to the Engine.Run GoDoc.

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.

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, the RunRequest GoDoc, and the OpenAI-compatible request contract 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:

// v0.1.0
Execution: &promptkit.ExecutionTargetOverride{
	ReasoningEffort: "high",
}

to:

// 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 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. 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 and maintained offline execution example.