942 lines
39 KiB
Markdown
942 lines
39 KiB
Markdown
# Package `promptkit`
|
|
|
|
## Purpose
|
|
|
|
This guide helps Go consumers assemble Promptkit and choose the main
|
|
preparation or execution workflow. The declarations and GoDoc in the
|
|
[root package](../../doc.go) own exact field, option, serialization,
|
|
concurrency, ownership, failure, and cancellation semantics. The
|
|
[framework format reference](../formats.md) owns prompt, profile, and schema
|
|
file contracts.
|
|
|
|
Import the package as:
|
|
|
|
```go
|
|
import "gitea.maximumdirect.net/eric/promptkit"
|
|
```
|
|
|
|
The following Go fragments are illustrative and omit surrounding package,
|
|
import, and error-handling code. Use the maintained examples for complete
|
|
programs.
|
|
|
|
## Go Compatibility
|
|
|
|
The `go` directive in the selected release's [module metadata](../../go.mod)
|
|
declares its minimum supported consumer Go version. Maintainers build and
|
|
validate releases with the locally selected Go patch release; the minimum
|
|
does not simply track that patch version.
|
|
|
|
Successful release validation on the selected patch is sufficient to presume
|
|
support across its Go series, subject to the declared minimum and concrete
|
|
evidence of incompatibility. Specific compatibility concerns are investigated
|
|
as they arise. Minor releases may raise the minimum; patch releases preserve
|
|
it. Release notes explain increases and any required consumer action. See the
|
|
[release policy](../policy/release.md#consumer-minimum-and-release-toolchain).
|
|
|
|
## Construct An Engine
|
|
|
|
Create an engine with [`NewEngine`](../../engine.go). Select each directory
|
|
explicitly and place its inputs in an ordered ConfigKit underlay. A group
|
|
strictly composes its files; later groups can overlay earlier ones:
|
|
|
|
```go
|
|
promptInputs, err := promptkit.DirectoryInputs("prompts")
|
|
if err != nil { return err }
|
|
profileInputs, err := promptkit.DirectoryInputs("profiles")
|
|
if err != nil { return err }
|
|
engine, err := promptkit.NewEngine(promptkit.Config{
|
|
Sources: promptkit.ConfigurationSources{Underlays: []configkit.Underlay{
|
|
{Inputs: promptInputs},
|
|
{Inputs: profileInputs},
|
|
}},
|
|
SchemaDir: "schemas",
|
|
})
|
|
```
|
|
|
|
For a single highest-priority document, set `Sources.Main` to a
|
|
`*configkit.Input`; omit it for ordinary layered construction. A single file
|
|
can use `configkit.File` directly. `ProfileSource`, `BackendSource`, and raw
|
|
`configkit.Mapping` supply programmatic definitions. Schema and runtime
|
|
collaborator options remain available. Construction validates the final
|
|
effective configuration and captures referenced prompt content; construct a
|
|
new engine to observe configuration edits. See the [constructor GoDoc](../../engine.go)
|
|
for the exact public contract and the [framework format reference](../formats.md)
|
|
for definition fields.
|
|
|
|
## Supply Embedded Application Defaults
|
|
|
|
Use `FilesystemInputs` for embedded defaults and `DirectoryInputs` for an
|
|
explicitly chosen operator directory. A later programmatic group can override
|
|
both. No directory name has built-in precedence:
|
|
|
|
```go
|
|
defaults, err := promptkit.FilesystemInputs("embedded defaults", applicationFS, "config")
|
|
if err != nil { return err }
|
|
operator, err := promptkit.DirectoryInputs(operatorDirectory)
|
|
if err != nil { return err }
|
|
profile := promptkit.ProfileSource("application choices", map[string]promptkit.ProfileConfig{
|
|
"summary": {Model: &selectedModel},
|
|
})
|
|
engine, err := promptkit.NewEngine(promptkit.Config{Sources: promptkit.ConfigurationSources{
|
|
Underlays: []configkit.Underlay{
|
|
{Inputs: defaults},
|
|
{Inputs: operator},
|
|
{Inputs: []configkit.Input{{Source: profile}}},
|
|
},
|
|
}})
|
|
```
|
|
|
|
Only add the operator group when the application selects that directory; an
|
|
explicitly selected missing or unreadable root is an error. Each helper finds
|
|
`.yaml` and `.yml` files recursively and disables imports by default. The
|
|
caller may enable imports on individual inputs. A nil `Sources.Main` is an
|
|
empty main source, not another directory lookup.
|
|
|
|
For a programmatic document with explicit null or a prompt definition, use a
|
|
labeled mapping as an input. A mapping owns a snapshot of supported values:
|
|
|
|
```go
|
|
document := configkit.Mapping("application configuration", map[string]any{
|
|
"profiles": map[string]any{
|
|
"local-summary": map[string]any{
|
|
"backend": "local", "model": "example-model",
|
|
"temperature": nil, // Suppress an inherited provider control.
|
|
},
|
|
},
|
|
})
|
|
input := configkit.Input{Source: document}
|
|
// Add input to a later underlay, or set Sources.Main = &input.
|
|
```
|
|
|
|
`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.
|
|
|
|
When configuration or message files change, build and validate a new engine,
|
|
then switch the application to that engine for subsequent work. Existing
|
|
engines and prepared handles retain their captured definitions; schema and
|
|
request artifact resources still follow their runtime lifecycle. Inspection
|
|
does not watch or reload sources automatically.
|
|
|
|
## Discover Available Profiles And Prompts
|
|
|
|
Use [`Engine.ListProfiles`](../../listing.go) and
|
|
[`Engine.ListPrompts`](../../listing.go) to discover exact identities from the
|
|
engine's final captured configuration, then inspect a selected prompt:
|
|
|
|
```go
|
|
profiles, err := engine.ListProfiles(ctx)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if len(profiles) > 0 {
|
|
_, err := engine.InspectProfile(ctx, profiles[0])
|
|
if err != nil {
|
|
return err
|
|
}
|
|
}
|
|
prompts, err := engine.ListPrompts(ctx)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if len(prompts) > 0 {
|
|
selected := prompts[0]
|
|
inspection, err := engine.InspectPrompt(ctx, selected.ID, selected.Version)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
_ = inspection
|
|
}
|
|
```
|
|
|
|
These lists describe effective membership, not ConfigKit contribution history,
|
|
and do not promise that a profile is ready to execute. Use inspection for
|
|
details and preparation to resolve request resources. The linked method GoDoc
|
|
owns ordering, ownership, concurrency, and error behavior; identifiers may be
|
|
sensitive application data.
|
|
|
|
## Inspect The Captured Configuration
|
|
|
|
Use [`Engine.InspectConfiguration`](../../configuration_inspection.go) for an
|
|
operator-selected report of effective configuration, source descriptions,
|
|
diagnostics, and path history. For a literal ID containing a slash, construct
|
|
the semantic path with `configkit.Keys`:
|
|
|
|
```go
|
|
view, err := engine.InspectConfiguration()
|
|
if err != nil { return err }
|
|
path := configkit.Keys("profiles", "team/summary", "model")
|
|
explanation, found := view.Explain(path)
|
|
if found && explanation.Effective.Redacted {
|
|
// Apply the application's disclosure policy before sharing the report.
|
|
}
|
|
```
|
|
|
|
`WriteEffective` and `WriteExplanation` produce ConfigKit-redacted reports;
|
|
the application decides whether those reports may be disclosed. Credential-
|
|
shaped provider keys are redacted, but arbitrary prompt and provider content
|
|
is not necessarily safe to share. Inspection uses the engine's captured result
|
|
and does not reopen sources or resolve credentials. `InspectProfile` instead
|
|
shows backend-completed execution settings for one profile, and `Prepare` or
|
|
`PreparedExecution.Details` shows request-specific rendered messages, inputs,
|
|
and validation resources. None of those views is a substitute for the others.
|
|
|
|
To review changes between two independently captured configurations, compare
|
|
their inspection views:
|
|
|
|
```go
|
|
earlier, err := earlierEngine.InspectConfiguration()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
later, err := candidateEngine.InspectConfiguration()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
differences, err := earlier.Diff(later)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
for _, difference := range differences {
|
|
if difference.HiddenChanged {
|
|
// Report a protected change without inferring its value.
|
|
}
|
|
if difference.Left.Redacted || difference.Right.Redacted {
|
|
// Apply the application's disclosure policy before presenting snapshots.
|
|
}
|
|
}
|
|
```
|
|
|
|
See the [`ConfigurationInspection.Diff` GoDoc](../../configuration_inspection.go)
|
|
and ConfigKit's [v0.7.0 effective-configuration diff guidance](https://gitea.maximumdirect.net/eric/ConfigKit/src/tag/v0.7.0/docs/config.md#effective-configuration-diffs)
|
|
for the exact comparison contract. The operation compares only frozen effective
|
|
configuration values: it does not reread external files or compare their
|
|
contents, resolved credential contents, later execution preparation, or
|
|
request-specific overrides. An empty diff is not proof of equivalent execution.
|
|
Records can contain ordinary application data, so use the application's
|
|
disclosure policy when presenting them.
|
|
|
|
## Run Explicit Smoke Diagnostics
|
|
|
|
[`Engine.SmokeTest`](../../smoke_diagnostics.go) runs one explicit diagnostic
|
|
using a target resolved from the engine's captured configuration. It does not
|
|
execute the selected application prompt or its inputs, session, artifacts, or
|
|
output schema. Unlike the offline examples and default test suite, calling it
|
|
can contact the selected provider and use configured credentials. It is a
|
|
consumer operation, not an automatic readiness verdict or a release check.
|
|
|
|
For example, a caller can request the built-in text inference check and treat
|
|
its optional fact as an observation rather than assuming that a nil Go error
|
|
means success:
|
|
|
|
```go
|
|
report, err := engine.SmokeTest(ctx, promptkit.SmokeTestRequest{
|
|
Kind: promptkit.SmokeTestTextInference,
|
|
ProfileID: profileID,
|
|
APIKey: apiKey,
|
|
})
|
|
if err != nil {
|
|
if report != nil {
|
|
retainDisclosureSafeSummary(report)
|
|
}
|
|
return err
|
|
}
|
|
if report != nil {
|
|
matched := report.Facts.ExpectedTextMatched
|
|
switch {
|
|
case matched == nil:
|
|
// The comparison was not evaluated or is unknown/inapplicable.
|
|
case *matched:
|
|
// The private expected-text comparison matched.
|
|
default:
|
|
// It did not match; the operation may still have concluded normally.
|
|
}
|
|
}
|
|
```
|
|
|
|
A nil Go error means the diagnostic concluded, not that its finding was
|
|
positive. Fact pointers are tri-state: nil means unknown, not evaluated, or
|
|
inapplicable. Reports contain factual observations and effective settings, not
|
|
generated text or provider bodies; identifiers and settings can still be
|
|
sensitive. The
|
|
[`SmokeTest` GoDoc](../../smoke_diagnostics.go) owns selection precedence,
|
|
exact fields, timeout and retry behavior, supported transports, and disclosure
|
|
details.
|
|
|
|
Caller cancellation can return both an error and a partial report. Preserve the
|
|
context error identity and inspect any available observations before returning:
|
|
|
|
```go
|
|
report, err := engine.SmokeTest(ctx, request)
|
|
if report != nil {
|
|
retainDisclosureSafeSummary(report)
|
|
}
|
|
if err != nil {
|
|
if errors.Is(err, context.Canceled) || errors.Is(err, context.DeadlineExceeded) {
|
|
// The report may describe work completed before interruption.
|
|
}
|
|
return err
|
|
}
|
|
```
|
|
|
|
## Inspect A Prompt Before Preparation
|
|
|
|
Use [`Engine.InspectPrompt`](../../engine.go) to check one configured prompt's
|
|
declared inputs and output workflow without creating placeholder inputs or
|
|
resolving a profile:
|
|
|
|
```go
|
|
inspection, err := engine.InspectPrompt(ctx, "meeting.summary", "")
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
for _, input := range inspection.Inputs {
|
|
// Compare the declared input with application configuration.
|
|
}
|
|
```
|
|
|
|
Use this configuration-time boundary when the application needs only the
|
|
declared prompt interface. Use `InspectProfile` separately when it must also
|
|
check a configured profile. Use `Prepare` when it needs inputs, schemas, or
|
|
rendered messages, and use prepared execution when that work must remain tied
|
|
to later execution. The method's [GoDoc](../../engine.go) owns exact fields,
|
|
hash, ownership, and error semantics.
|
|
|
|
## Inspect Requirements Before Preparation
|
|
|
|
Use [`Engine.InspectRequirements`](../../requirements.go) when the application
|
|
needs the prompt's recognizable input and caller-variable dependencies for a
|
|
particular context before it has input artifacts. Unlike `InspectPrompt`, it
|
|
also walks captured template syntax and selects a profile identity; unlike
|
|
`Prepare`, it does not load artifacts or render messages.
|
|
|
|
```go
|
|
requirements, err := engine.InspectRequirements(ctx, promptkit.RequirementInspectionRequest{
|
|
PromptID: "meeting.summary",
|
|
Vars: map[string]string{"audience": "project leads"},
|
|
})
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
for _, input := range requirements.Inputs {
|
|
if input.Kind == promptkit.RequirementRequired {
|
|
// Plan to bind this input before preparation.
|
|
}
|
|
if input.Kind == promptkit.RequirementOptional && input.Explanation != "" {
|
|
// A recognizable reference may occur only on a known skipped path.
|
|
}
|
|
}
|
|
for _, variable := range requirements.Variables {
|
|
if variable.Kind == promptkit.RequirementRequired && !variable.Supplied {
|
|
// The key is required even though this inspection did not receive it.
|
|
}
|
|
}
|
|
if !requirements.Complete {
|
|
// Preserve the partial inventory and account for its discovery issues.
|
|
}
|
|
```
|
|
|
|
Optional entries can include recognizable inputs and variables found only in
|
|
inactive branches. A missing required variable is reported separately from
|
|
discovery completeness: missing a value used only for direct text substitution
|
|
does not by itself make the inventory incomplete. Conversely, an unresolved
|
|
dependency-bearing branch can make discovery incomplete while retaining known
|
|
conditional entries. Do not treat an empty partial inventory as proof that no
|
|
input bindings are needed. See the method's [GoDoc](../../requirements.go) for
|
|
the exact classifications, supported syntax, ownership, and error contract.
|
|
|
|
## Validate A Concrete Request
|
|
|
|
Use [`Engine.ValidateRequest`](../../engine.go) when the application has a
|
|
complete `RunRequest` and needs to know whether that concrete invocation passes
|
|
preparation:
|
|
|
|
```go
|
|
request := promptkit.RunRequest{
|
|
PromptID: "meeting.summary",
|
|
Vars: map[string]string{"audience": "project leads"},
|
|
Inputs: map[string]promptkit.ArtifactRef{
|
|
"note": promptkit.Inline("Synthetic meeting notes"),
|
|
},
|
|
}
|
|
if err := engine.ValidateRequest(ctx, request); err != nil {
|
|
return err
|
|
}
|
|
```
|
|
|
|
Unlike `InspectRequirements`, this checks supplied values, the selected template
|
|
branches, and request bindings through the existing preparation path. It checks
|
|
only this invocation, does not aggregate errors, and loads every supplied input
|
|
according to ordinary preparation behavior, even if a branch does not use it.
|
|
It makes no model call and returns no prepared snapshot, but artifact and schema
|
|
resource I/O and its time and memory costs remain. Success does not certify
|
|
provider acceptance or later execution success. See the method's GoDoc for its
|
|
exact error and context behavior.
|
|
|
|
Scriptorium can map the returned error to its own diagnostics and exit status.
|
|
Its `validate` and `run` commands should construct equivalent requests for
|
|
equivalent caller inputs so their preparation behavior agrees. Separate
|
|
subprocess calls prepare independently; an opaque `PrepareExecution` handle
|
|
cannot be transferred from `validate` to `run`. Use `PrepareExecution` when one
|
|
process and engine must execute the exact prepared snapshot.
|
|
|
|
## Prepare Without Model Execution
|
|
|
|
[`Engine.Prepare`](../../engine.go) resolves the selected prompt and profile,
|
|
loads inputs and any structured-output schema, and renders messages without
|
|
calling a model client. Choose it when the prepared value is the final
|
|
inspection or persistence result and no later execution must be tied to that
|
|
exact snapshot:
|
|
|
|
```go
|
|
prepared, err := engine.Prepare(ctx, promptkit.RunRequest{
|
|
PromptID: "meeting.summary",
|
|
Inputs: map[string]promptkit.ArtifactRef{
|
|
"note": promptkit.Inline("Synthetic meeting notes"),
|
|
},
|
|
})
|
|
```
|
|
|
|
The maintained
|
|
[offline preparation example](../../examples/go-library/prepare/main.go)
|
|
shows a complete runnable setup with a prompt file, in-memory profile, and
|
|
inline input. It demonstrates validation and preparation with the same request
|
|
to make the two operations concrete; this performs preparation twice and is
|
|
illustrative, not a requirement to validate before `Prepare` or `Run`. Exact
|
|
request requirements and prepared-result fields belong to the
|
|
[`RunRequest` and `PreparedRun` GoDoc](../../types.go).
|
|
|
|
## Prepare Now And Execute The Same Snapshot Later
|
|
|
|
Use [`Engine.PrepareExecution`](../../engine.go) when an application must
|
|
inspect or persist preflight details before deciding whether to start model
|
|
work, while ensuring that later execution uses those exact rendered messages,
|
|
inputs, target settings, and validation resources:
|
|
|
|
```go
|
|
preparedExecution, err := engine.PrepareExecution(ctx, promptkit.RunRequest{
|
|
PromptID: "meeting.summary",
|
|
Inputs: map[string]promptkit.ArtifactRef{
|
|
"note": promptkit.Inline("Synthetic meeting notes"),
|
|
},
|
|
})
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer preparedExecution.Discard()
|
|
|
|
details := preparedExecution.Details()
|
|
// Inspect or persist an application-selected safe subset of details.
|
|
|
|
result, err := engine.RunPrepared(ctx, preparedExecution)
|
|
```
|
|
|
|
Preparation does not call the model or reserve backend capacity.
|
|
`RunPrepared` executes from the retained snapshot rather than reloading
|
|
consumer sources. The handle is opaque in-process state, while `Details`
|
|
contains rendered content and remains subject to the application's data
|
|
handling policy. The
|
|
[`PreparedExecution` and method GoDoc](../../prepared_execution.go) and
|
|
[engine operation GoDoc](../../engine.go) own exact lifecycle, engine-binding,
|
|
credential, cancellation, timing, and error semantics.
|
|
|
|
If the request supplied `Observer`, successful preparation retains that
|
|
function for `RunPrepared`; preparation itself emits nothing. Observation
|
|
starts only when the owning engine successfully claims the handle. Reused,
|
|
discarded, foreign, and invalid handles emit nothing. `Discard` clears the
|
|
retained callback without invoking it. The function's closure is retained by
|
|
identity, so synchronize any shared state it uses. Prepared start events can
|
|
include identities already resolved in the frozen snapshot.
|
|
|
|
## Execute And Validate
|
|
|
|
[`Engine.Run`](../../engine.go) performs the same preparation, invokes the
|
|
configured model client, classifies the generated artifact, and validates the
|
|
content in one call. Choose it when the application does not need a preflight
|
|
boundary tied to the eventual execution. A completed content check may return
|
|
`ValidationFailed` in the result; an operational inability to validate returns
|
|
an error.
|
|
|
|
The maintained
|
|
[offline execution example](../../examples/go-library/run/main.go) injects a
|
|
deterministic model client and exercises `Run` without credentials, network
|
|
access, or paid calls. It is intentionally separate from the preparation
|
|
example so each workflow and its small prompt fixture can be copied and run on
|
|
its own.
|
|
|
|
Use the [`RunResult` and `ValidationResult` GoDoc](../../types.go) for the
|
|
returned data and the `Engine.Run` GoDoc for failure and cancellation
|
|
semantics. The
|
|
[OpenAI-compatible integration contract](../integrations/openai-compatible-chat.md)
|
|
owns the built-in client's outbound HTTP behavior.
|
|
|
|
### Observe An Ordinary Run
|
|
|
|
Set `RunRequest.Observer` to receive synchronous metadata events from `Run`:
|
|
|
|
```go
|
|
request.Observer = func(event promptkit.ExecutionEvent) {
|
|
// Translate selected metadata into an application-owned record.
|
|
}
|
|
result, err := engine.Run(ctx, request)
|
|
```
|
|
|
|
An ordinary run emits `started` before request conversion, followed by any
|
|
attempt and retry-delay completions, then one `finished` event on a normal
|
|
return. `Prepare` and `PrepareExecution` emit no events. A start event has no
|
|
resolved prompt, profile, or model; later events gain identities only as
|
|
selection succeeds. The terminal event has dispatched-attempt, transient-retry,
|
|
repair-call, duration, and presence-aware usage totals. Its usage is an
|
|
aggregate, not an additional billable attempt. A known zero differs from an
|
|
unknown count, and incomplete totals must not be treated as complete cost
|
|
accounting. A failed operation still returns no partial `RunResult`.
|
|
An injected client that leaves `UsagePresence` nil can report known nonzero
|
|
counts, but its zero counts remain unknown to observations; supply explicit
|
|
presence when measured zeros matter. Existing `RunResult.Usage` sums returned
|
|
values independently of observation presence.
|
|
|
|
Callbacks run synchronously, in order for one operation, and can overlap for
|
|
different operations. Synchronize shared callback state and avoid blocking on
|
|
work that needs the same run's admission slot. Callback panics propagate;
|
|
there is no durable event history or promise of a terminal event after panic.
|
|
Events omit content and error objects, but selected identifiers may still be
|
|
sensitive. See the [observation GoDoc](../../observations.go) for exact fields.
|
|
The [offline proposal and assessment example](../../examples/go-library/observe/main.go)
|
|
shows application-owned event collection and a separate semantic acceptance
|
|
decision using the built-in HTTP path without network access.
|
|
|
|
### Repair A Structured Result
|
|
|
|
Set a small additional-call budget when a structurally invalid result can be
|
|
corrected automatically:
|
|
|
|
```go
|
|
request.Validation = &promptkit.OutputContract{
|
|
Format: promptkit.FormatJSON,
|
|
ValidationMode: promptkit.ValidationJSONSchema,
|
|
SchemaPath: "events.schema.json",
|
|
RepairAttempts: 1,
|
|
}
|
|
```
|
|
|
|
Each repair attempt is another model generation, so it can increase latency and
|
|
usage; `RunResult.Usage` is cumulative and `Validation.RepairAttempts` reports
|
|
repair generations rather than transient HTTP attempts. Exhaustion still
|
|
returns the final failed validation result. `basic` validation can also repair
|
|
an empty candidate, but structural
|
|
validity is not evidence of factual or domain correctness. See the
|
|
[output-contract format reference](../formats.md#output-contract) and
|
|
[`OutputContract` GoDoc](../../types.go) for the exact budget and eligibility
|
|
rules.
|
|
|
|
The built-in client also retries eligible temporary HTTP statuses. By default,
|
|
each generation can make two additional attempts; a repair generation receives
|
|
its own budget. Set `Config.TransientRetries` to a pointer to zero for the
|
|
previous single-attempt behavior, or set `RunRequest.TransientRetries` to
|
|
override one execution. A custom HTTP transport still uses this policy;
|
|
injected `LLMClient` implementations retain their own retry policy. Application
|
|
semantic retries are separate runs and can multiply provider traffic further.
|
|
The caller context covers capacity and retry waiting as well as attempts;
|
|
without a deadline a valid provider delay can be long. Promptkit does not
|
|
infer exactly-once or complete cost accounting from a retry outcome.
|
|
See the [provider integration contract](../integrations/openai-compatible-chat.md#transient-status-retries)
|
|
for eligible statuses, waits, timeouts, and provider timing.
|
|
|
|
### Append Already-Rendered Messages
|
|
|
|
An application can include an earlier assistant response and its own corrective
|
|
instruction in a fresh request without changing the configured prompt:
|
|
|
|
```go
|
|
request.AppendedMessages = []promptkit.RenderedMessage{
|
|
{Role: promptkit.RoleAssistant, Content: previousResponse},
|
|
{Role: promptkit.RoleUser, Content: correction},
|
|
}
|
|
result, err := engine.Run(ctx, request)
|
|
```
|
|
|
|
These messages are already rendered: Promptkit does not template or resolve
|
|
files in them, and they can contain sensitive model output or application
|
|
feedback. Every `Run` call uses the engine's captured configuration; the
|
|
application owns any semantic retry budget. When a
|
|
pre-execution equality check is required, use `PrepareExecution` and compare
|
|
its opaque rendered-prompt hash before invoking `RunPrepared`.
|
|
|
|
## Inputs, Profiles, And Overrides
|
|
|
|
Use `File`, `Inline`, or `InlineWithURI` to construct request inputs. A request
|
|
can select a profile explicitly or use the prompt's default profile, and can
|
|
replace execution settings or the complete output contract.
|
|
|
|
The [public value GoDoc](../../types.go) defines nil, empty, zero, replacement,
|
|
copy, and credential behavior. The
|
|
[framework format reference](../formats.md) defines how those request values
|
|
interact with prompt definitions, layered profiles, built-ins, schemas, and
|
|
framework defaults.
|
|
|
|
For programmatic profiles, use [`ProfileSource`](../../profiles.go) with keyed
|
|
`ProfileConfig` values. Nil fields are omitted; pointers preserve explicit
|
|
zero, false, and blank values. Raw `configkit.Mapping` expresses explicit null.
|
|
|
|
### Migrate From The Previous Configuration API
|
|
|
|
Replace `Config.PromptDir` and `Config.ProfileDir` with selected ConfigKit
|
|
inputs in `Config.Sources`. The removed `WithPromptFS`, `WithPromptFile`,
|
|
`WithProfileFS`, `WithProfileFile`, `WithFallbackProfileFS`, `WithProfiles`,
|
|
and `WithBackend` options have no compatibility wrappers. Use
|
|
`DirectoryInputs` or `FilesystemInputs` for a strict-composition file group,
|
|
ConfigKit file inputs for individual files, and `ProfileSource`,
|
|
`BackendSource`, or `configkit.Mapping` for programmatic values. Backend IDs
|
|
are keys in configuration, not registrations. Choose caller underlays in
|
|
fallback-to-operator-to-programmatic order; use `Sources.Main` for one final
|
|
highest-priority input.
|
|
|
|
Move embedded `id` and `version` fields into keyed `profiles` and `prompts`
|
|
maps; convert `base_profile` to ConfigKit `extends`. IDs and references are
|
|
exact literal keys, without trimming. A supplied zero, false, or empty string
|
|
is now distinguishable from omission; null can clear or suppress an inherited
|
|
field according to the [field table](../formats.md#defaults-and-overrides).
|
|
Maps merge recursively across inheritance and layers. `{}` does not clear an
|
|
inherited map: use null to clear the whole map. At request time, a literal
|
|
`extra_params["extends"]` is ordinary provider data, not an inheritance
|
|
instruction; a nonempty request ExtraParams map replaces configured params.
|
|
Changing a model or backend does not clear unrelated explicit fields, though
|
|
backend-derived defaults are applied after inheritance to the final backend.
|
|
|
|
Invalid final definitions, including unused ones, now fail `NewEngine`; only
|
|
request-dependent failures remain at preparation or execution. Prompt text
|
|
and static templates are captured at construction, not automatically reloaded
|
|
between runs. Replace the engine to pick up changed definitions or message
|
|
files. The [format reference](../formats.md) owns exact field and presence
|
|
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.
|
|
|
|
### Inspect A Profile Before Prompt Work
|
|
|
|
Use [`Engine.InspectProfile`](../../engine.go) to validate one configured
|
|
profile without constructing a synthetic prompt or placeholder inputs. It
|
|
resolves the profile's effective target but does not prepare or execute a
|
|
prompt:
|
|
|
|
```go
|
|
inspection, err := engine.InspectProfile(ctx, profileID)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
target := inspection.EffectiveModelParams
|
|
if target.APIKeyEnv != "" {
|
|
// This is a configured optional environment lookup source.
|
|
} else if inspection.APIKeyRequired {
|
|
// Arrange a direct credential before later execution.
|
|
}
|
|
```
|
|
|
|
Use this configuration-time boundary when only the profile and its target need
|
|
checking. Use `Prepare` when the application also needs prompt, input, schema,
|
|
or rendering work; use prepared execution when that work must remain tied to a
|
|
later execution. A reported `APIKeyEnv` is a configured optional source, while
|
|
`APIKeyRequired` is the explicit local requirement. The
|
|
[credential format reference](../formats.md#credentials) and the method's
|
|
[GoDoc](../../engine.go) own the exact precedence, timing, result, and error
|
|
contracts.
|
|
|
|
### Set A Per-Run Session And Reasoning
|
|
|
|
Supply a direct session ID when one prompt should be correlated with a
|
|
consumer-managed conversation or workflow without changing prompt variables:
|
|
|
|
```go
|
|
reasoning := "high"
|
|
result, err := engine.Run(ctx, promptkit.RunRequest{
|
|
PromptID: "meeting.summary",
|
|
SessionID: "conversation-42",
|
|
Inputs: map[string]promptkit.ArtifactRef{
|
|
"note": promptkit.Inline("Synthetic meeting notes"),
|
|
},
|
|
Execution: &promptkit.ExecutionTargetOverride{
|
|
ReasoningEffort: &reasoning,
|
|
},
|
|
})
|
|
```
|
|
|
|
A nil reasoning pointer inherits the selected profile, a pointer to a
|
|
nonblank string replaces it, and a pointer to a blank string disables
|
|
reasoning for that run. Session IDs are correlation metadata, not credentials;
|
|
use stable, non-secret values that are safe to expose to collaborators and
|
|
providers. The
|
|
[`RunRequest` and `ExecutionTargetOverride` GoDoc](../../types.go) owns the
|
|
exact normalization, precedence, error, copying, and exposure contract.
|
|
|
|
### Configure A Local OpenAI-Compatible Endpoint
|
|
|
|
Choose the smallest configuration that fits how the endpoint will be reused.
|
|
|
|
#### Use An Endpoint-Only Profile
|
|
|
|
Put the endpoint directly on a programmatic profile when only that profile needs
|
|
it and shared backend identity or capacity policy is unnecessary:
|
|
|
|
```go
|
|
endpoint, model := "http://localhost:8000/v1", "example-model"
|
|
profile := promptkit.ProfileSource("local profile", map[string]promptkit.ProfileConfig{
|
|
"local-summary": {Endpoint: &endpoint, Model: &model},
|
|
})
|
|
// Add configkit.Input{Source: profile} to a caller underlay.
|
|
```
|
|
|
|
Endpoint-only profiles have an empty backend ID and remain unrestricted by
|
|
backend capacity policy.
|
|
|
|
#### Use The Conventional Local Backend
|
|
|
|
Use `LocalBackend` when profiles should share the conventional `local`
|
|
identity, endpoint, and concurrency limit:
|
|
|
|
```go
|
|
backendID, model := promptkit.BackendLocal, "example-model"
|
|
backend := promptkit.BackendSource("local backend", map[string]promptkit.BackendConfig{
|
|
backendID: promptkit.LocalBackend("http://localhost:8000/v1", 2),
|
|
})
|
|
profile := promptkit.ProfileSource("local profile", map[string]promptkit.ProfileConfig{
|
|
"local-summary": {Backend: &backendID, Model: &model},
|
|
})
|
|
// Add both sources as inputs in a caller underlay.
|
|
```
|
|
|
|
The helper does not choose an ID or read environment variables. A positive
|
|
concurrency limit with omitted queue capacity selects the default waiting
|
|
capacity of 1024.
|
|
|
|
#### Configure A Complete Backend
|
|
|
|
Use a keyed `BackendConfig` value for authentication, extra request parameters, an
|
|
explicit queue capacity, a custom ID, or multiple local endpoints:
|
|
|
|
```go
|
|
endpoint, keyName, limit, noWaiting := "http://gpu-host:8000/v1", "LOCAL_GPU_API_KEY", 2, 0
|
|
backend := promptkit.BackendSource("GPU backend", map[string]promptkit.BackendConfig{
|
|
"local-gpu": {
|
|
Endpoint: &endpoint, APIKeyEnv: &keyName,
|
|
ConcurrencyLimit: &limit, QueueCapacity: &noWaiting,
|
|
ExtraParams: map[string]any{"provider_option": "enabled"},
|
|
},
|
|
})
|
|
```
|
|
|
|
Place `backend` in a caller underlay and select `"local-gpu"` from a
|
|
`ProfileConfig.Backend` pointer or a keyed file profile. All backend
|
|
contributions use the same ConfigKit layering rules, including overlays of
|
|
maintained definitions. The [`BackendConfig` and `LocalBackend` GoDoc](../../backends.go)
|
|
defines exact omission and construction behavior.
|
|
|
|
Both file-backed and programmatic profiles select a backend through
|
|
`backend` or `ProfileConfig.Backend`. Profile and request endpoint overrides retain
|
|
that routing and capacity identity. `PreparedRun.SelectedBackendID`,
|
|
`RunResult.SelectedBackendID`, and the effective `ExecutionTarget.BackendID`
|
|
expose it to consumers and injected model clients. Endpoint-only profiles
|
|
remain supported and expose an empty backend ID.
|
|
|
|
### Limit Backend Concurrency
|
|
|
|
Set `BackendConfig.ConcurrencyLimit` when a backend needs protection from too many
|
|
simultaneous model calls. Leaving `QueueCapacity` nil, as in the local-backend
|
|
example above, selects the default waiting capacity of 1024.
|
|
|
|
To accept no waiting backlog beyond the active calls, provide an explicit
|
|
zero:
|
|
|
|
```go
|
|
noWaiting := 0
|
|
backend := promptkit.BackendConfig{
|
|
Endpoint: &endpoint,
|
|
ConcurrencyLimit: &limit,
|
|
QueueCapacity: &noWaiting,
|
|
}
|
|
```
|
|
|
|
The pointer distinguishes an explicit zero from omission. Use keyed
|
|
`BackendConfig` literals inside `BackendSource` for programmatic definitions.
|
|
Capacity belongs to one engine and the selected backend ID; endpoint-only
|
|
profiles and custom backends without a configured limit remain unrestricted.
|
|
Exact validation, defaulting, ownership, and concurrency semantics belong to
|
|
the [`BackendConfig` GoDoc](../../backends.go).
|
|
|
|
## Credentials
|
|
|
|
File-backed and programmatic profiles can name an environment variable or
|
|
require a direct request key. Direct keys are request-scoped and are excluded
|
|
from supported JSON values and the package's `String` and `GoString`
|
|
summaries. The exact precedence and redaction guarantees belong to
|
|
[`RunRequest`, `GenerateRequest`, and the profile GoDoc](../../types.go).
|
|
|
|
## Protect Files And Generated Data
|
|
|
|
The default artifact reader opens a `File` reference as a caller-selected
|
|
operating-system path. It does not constrain paths to an application root,
|
|
impose an inbound request-size policy, or establish an untrusted-input security
|
|
boundary. Applications must validate and restrict untrusted paths and payloads
|
|
before constructing a request, or inject an artifact reader that enforces
|
|
their filesystem, authorization, and size policies.
|
|
|
|
Rendered messages, input and output artifact bodies, raw model output, and
|
|
validation diagnostics can contain sensitive data. API-key redaction does not
|
|
sanitize those values. Treat prepared values, results, collaborator requests,
|
|
errors, and logs according to the application's data-access, retention, and
|
|
secret-handling policies.
|
|
|
|
## Extension Interfaces
|
|
|
|
Inject an [`LLMClient` or `ArtifactReader`](../../types.go) when the built-in
|
|
behavior does not fit the application. Their GoDoc defines concurrent use,
|
|
context handling, ownership of copied values, nil responses, and preservation
|
|
of collaborator errors. Implementations must honor cancellation, safely manage
|
|
copies they retain, avoid unsafe logging of content or credentials, and enforce
|
|
the application policy that motivated the injection.
|
|
|
|
## Handle Errors
|
|
|
|
Use `errors.Is` with the
|
|
[public error sentinels and operation GoDoc](../../engine.go). The declarations
|
|
distinguish invalid construction, invalid requests, absent selections,
|
|
collaborator failures, and operational validation
|
|
failures. Specific request conditions may also match the broader
|
|
`ErrInvalidRequest`, and injected collaborator identities are preserved where
|
|
documented. Invalid effective definitions, including unknown backend
|
|
references, match `ErrInvalidConfig` during construction. ConfigKit load
|
|
errors remain available through `errors.As`.
|
|
|
|
When a limited backend has admitted all active and waiting calls, handle
|
|
`ErrCapacityExceeded` separately from request errors and provider failures:
|
|
|
|
```go
|
|
result, err := engine.Run(ctx, request)
|
|
if errors.Is(err, promptkit.ErrCapacityExceeded) {
|
|
var capacityErr *promptkit.CapacityError
|
|
if errors.As(err, &capacityErr) {
|
|
// Record capacityErr.BackendID using application-owned diagnostics.
|
|
}
|
|
|
|
// Apply application policy: shed work, report overload, or retry later.
|
|
}
|
|
```
|
|
|
|
A rejected call returns no partial result and does not invoke the model
|
|
client. Promptkit does not prescribe retries or map this error to an HTTP
|
|
status; those choices remain with the consuming application. The
|
|
[`CapacityError` GoDoc](../../capacity_error.go) owns the exact typed-error
|
|
contract, while the [`Engine.Run` and error GoDoc](../../engine.go) owns broad
|
|
error and cancellation identities.
|
|
|
|
For a non-2xx response from the built-in OpenAI-compatible client, inspect the
|
|
status and deliberately selected provider diagnostic when useful:
|
|
|
|
```go
|
|
var generationErr *promptkit.GenerationError
|
|
if errors.As(err, &generationErr) {
|
|
status := generationErr.StatusCode()
|
|
message := generationErr.ProviderMessage()
|
|
_, _ = status, message // Apply application presentation policy.
|
|
}
|
|
```
|
|
|
|
All provider fields are untrusted and can contain sensitive request or schema
|
|
fragments. Do not log, display, or return them without an application-specific
|
|
disclosure policy. Promptkit does not assign presentation behavior.
|
|
The [`GenerationError` GoDoc](../../generation_error.go) owns the exact typed
|
|
error contract.
|
|
|
|
Generation failures also match `ErrTransport` when the built-in HTTP request
|
|
fails, or `ErrMalformedResponse` when a successful provider response cannot be
|
|
decoded or an injected client returns a nil successful response. Both retain
|
|
`ErrLLMGenerate`; transport failures retain their original causes for
|
|
`errors.Is` and `errors.As`. Caller cancellation and deadlines retain their
|
|
standard context identities. Arbitrary injected-client errors retain their own
|
|
identities without Promptkit guessing a failure category from their text.
|
|
An eligible built-in status that consumes a positive retry budget additionally
|
|
matches `ErrRetriesExhausted`; use `errors.As` to read
|
|
`RetryExhaustedError.Attempts()` and the final `GenerationError` status. With
|
|
explicit zero retries, the first eligible status is an ordinary generation
|
|
error, not exhaustion. Completed structural rejection remains a normal result
|
|
with failed validation, while an operational error returns no partial result.
|
|
|
|
## Application Boundary
|
|
|
|
Promptkit is an importable library. It does not own a command, inbound HTTP
|
|
API, process configuration, or deployment policy. Applications map the root
|
|
package's results and errors into those concerns, including inbound size and
|
|
trust policy.
|