Add execution control request contracts
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
package promptkit
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"unicode/utf8"
|
||||
|
||||
@@ -30,7 +31,15 @@ func toDomainRunRequest(req RunRequest) (domain.RunRequest, error) {
|
||||
callback := req.Observer
|
||||
observer = func(event domain.ExecutionEvent) { callback(fromDomainExecutionEvent(event)) }
|
||||
}
|
||||
var onPrepared func(context.Context, *domain.PreparedRun)
|
||||
if callback := req.OnPrepared; callback != nil {
|
||||
onPrepared = func(ctx context.Context, prepared *domain.PreparedRun) {
|
||||
callback(ctx, *fromDomainPreparedRun(prepared))
|
||||
}
|
||||
}
|
||||
return domain.RunRequest{
|
||||
OperationTimeout: req.OperationTimeout,
|
||||
OnPrepared: onPrepared,
|
||||
CaptureExchange: req.CaptureExchange || req.ContinueFrom != nil,
|
||||
PromptID: req.PromptID,
|
||||
ProfileID: req.ProfileID,
|
||||
|
||||
@@ -144,6 +144,8 @@ selected public behavior and ownership boundaries are what matter.
|
||||
|
||||
## Stage 1: Add The Request Contract And Conversion
|
||||
|
||||
Status: complete.
|
||||
|
||||
Outcome: the selected API and internal request plumbing exist, ready for the
|
||||
execution stages to consume.
|
||||
|
||||
|
||||
@@ -60,6 +60,10 @@ var (
|
||||
// A [CapacityError] reports the selected backend ID. It is not an invalid
|
||||
// request, an LLM or provider rate-limit response, or ErrLLMGenerate.
|
||||
ErrCapacityExceeded = errors.New("backend capacity exceeded")
|
||||
// ErrOperationTimeout identifies expiration of the timeout selected by
|
||||
// RunRequest.OperationTimeout. Such errors also match context.DeadlineExceeded
|
||||
// and retain their underlying operation and collaborator identities.
|
||||
ErrOperationTimeout = errors.New("operation timeout exceeded")
|
||||
// ErrLLMGenerate identifies a model-client failure or a nil successful
|
||||
// response. A built-in OpenAI-compatible non-2xx response is available as a
|
||||
// [GenerationError]. Errors returned by an injected LLMClient remain
|
||||
|
||||
38
execution_controls_conversion_test.go
Normal file
38
execution_controls_conversion_test.go
Normal file
@@ -0,0 +1,38 @@
|
||||
package promptkit
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"go.maximumdirect.net/promptkit/internal/domain"
|
||||
)
|
||||
|
||||
func TestExecutionControlsConversion(t *testing.T) {
|
||||
ctx := context.WithValue(context.Background(), struct{}{}, "selected")
|
||||
called := false
|
||||
req := RunRequest{OperationTimeout: 3 * time.Second, OnPrepared: func(got context.Context, details PreparedRun) {
|
||||
called = true
|
||||
if got != ctx || details.PromptID != "prompt" {
|
||||
t.Fatalf("unexpected notification: %v, %+v", got, details)
|
||||
}
|
||||
details.InputHashes["input"] = "changed"
|
||||
}}
|
||||
converted, err := toDomainRunRequest(req)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if converted.OperationTimeout != req.OperationTimeout || converted.OnPrepared == nil || called {
|
||||
t.Fatal("execution controls were not retained without invocation")
|
||||
}
|
||||
req.OnPrepared = func(context.Context, PreparedRun) { t.Fatal("callback selection changed") }
|
||||
prepared := &domain.PreparedRun{PromptID: "prompt", InputHashes: map[string]string{"input": "original"}}
|
||||
converted.OnPrepared(ctx, prepared)
|
||||
if !called || prepared.InputHashes["input"] != "original" {
|
||||
t.Fatal("notification did not deliver detached details")
|
||||
}
|
||||
withoutCallback, err := toDomainRunRequest(RunRequest{})
|
||||
if err != nil || withoutCallback.OnPrepared != nil {
|
||||
t.Fatalf("nil callback was adapted: %v", err)
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
package domain
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
)
|
||||
|
||||
@@ -60,6 +61,9 @@ type CacheControl struct {
|
||||
|
||||
// RunRequest represents a request to generate a single artifact.
|
||||
type RunRequest struct {
|
||||
OperationTimeout time.Duration
|
||||
// OnPrepared receives borrowed, read-only internal state.
|
||||
OnPrepared func(context.Context, *PreparedRun)
|
||||
CaptureExchange bool
|
||||
ContinueFrom *Exchange
|
||||
PromptID string
|
||||
|
||||
@@ -39,6 +39,7 @@ var (
|
||||
ErrPromptRender = errors.New("failed to render prompt")
|
||||
ErrLLMGenerate = errors.New("failed to generate output")
|
||||
ErrValidation = errors.New("failed to validate output")
|
||||
ErrOperationTimeout = errors.New("operation timeout exceeded")
|
||||
)
|
||||
|
||||
// Runner executes the Promptkit core use case.
|
||||
|
||||
15
types.go
15
types.go
@@ -85,6 +85,21 @@ const (
|
||||
// own private execution snapshot for RunPrepared. Excessively deep or large
|
||||
// JSON-shaped values are rejected for safety.
|
||||
type RunRequest struct {
|
||||
// OperationTimeout selects a whole-operation budget when positive. Run
|
||||
// applies it across preparation and execution; PrepareExecution retains it
|
||||
// for RunPrepared, without counting preparation or idle time. Zero or a
|
||||
// negative value selects no additional timeout. This is independent of the
|
||||
// provider generation timeout. Cancellation remains cooperative.
|
||||
OperationTimeout time.Duration `json:"-" yaml:"-"`
|
||||
// OnPrepared synchronously receives detached, credential-redacted details of
|
||||
// the logical request after preparation and admission, before generation,
|
||||
// if execution reaches that point. Admission remains held, and callback time
|
||||
// consumes OperationTimeout. The callback owns the delivered mutable values;
|
||||
// its closure is shared and must synchronize any shared state. It has no
|
||||
// error return, and a panic propagates after resource cleanup. Prepare and
|
||||
// ValidateRequest do not invoke it; PrepareExecution retains the selected
|
||||
// function for RunPrepared and Discard releases it.
|
||||
OnPrepared func(context.Context, PreparedRun) `json:"-" yaml:"-"`
|
||||
// CaptureExchange requests an immutable snapshot of a completed run, even
|
||||
// when its structural validation fails. Operational errors return no result.
|
||||
// The snapshot is returned in RunResult.Exchange; see [Exchange] for its
|
||||
|
||||
Reference in New Issue
Block a user