Files
scriptorium/docs/consumers/pkg-scriptorium.md

6.6 KiB

Package scriptorium

Import path:

import "gitea.maximumdirect.net/eric/scriptorium"

The root package is a public facade over Scriptorium's prompt execution use case. It keeps internal/* packages private while exposing typed construction, preparation, execution, inputs, results, and errors.

Construct An Engine

engine, err := scriptorium.NewEngine(scriptorium.Config{
	PromptDir:  "./examples/prompts",
	ProfileDir: "./examples/profiles",
	SchemaDir:  "./examples/schemas",
})
if err != nil {
	return err
}

PromptDir is required unless an explicit prompt source option is supplied. ProfileDir is optional; omit it to use built-in profiles only, or set it to overlay custom profiles above built-ins. SchemaDir defaults to the built-in schema directory. Timeout and HTTPClient configure the default OpenAI-compatible client used by Run when no custom LLM client is supplied.

Asset Sources

Directory fields on Config remain the compatibility path. Explicit source options override the matching directory field:

  • WithPromptFS(fsys, root) and WithPromptFile(path)
  • WithProfileFS(fsys, root) and WithProfileFile(path)
  • WithSchemaFS(fsys, root) and WithSchemaFile(path)

Prompt and profile sources load standard Scriptorium YAML with the same strict validation as directory sources. Prompt content_file paths resolve relative to the prompt file in the same source. Profile options overlay custom profiles above built-ins. Schema fs.FS sources preserve prompt schema_path semantics; schema file options expose the file by its base name.

In-Memory Profiles

Use WithProfiles when the consuming application already has profile settings in typed Go configuration:

profile := scriptorium.OpenAICompatibleProfile(scriptorium.OpenAICompatibleProfileConfig{
	ID:             "app.default",
	Endpoint:       "https://openrouter.ai/api/v1",
	Model:          "mistralai/mistral-small-3.2-24b-instruct",
	APIKeyRequired: true,
})

engine, err := scriptorium.NewEngine(cfg, scriptorium.WithProfiles(profile))

In-memory profiles have highest precedence, followed by configured profile file/FS/directory sources, then built-in profiles. Duplicate IDs in one WithProfiles call return ErrInvalidConfig.

Profile and OpenAICompatibleProfileConfig include endpoint, model, numeric defaults, service tier, reasoning effort, APIKeyRequired, and JSON-compatible ExtraParams. WithProfiles validates ExtraParams and returns ErrInvalidConfig for unsupported values such as functions, channels, non-string map keys, non-finite floats, or cyclic values. Raw API-key fields are not accepted. When APIKeyRequired is true, pass the secret with RunRequest.APIKey.

Prepare A Prompt

Prepare resolves the prompt definition, profile, inputs, variables, output contract, structured-output metadata, and rendered messages without calling an LLM.

prepared, err := engine.Prepare(ctx, scriptorium.RunRequest{
	PromptID: "generic.markdown_summary",
	Inputs: map[string]scriptorium.ArtifactRef{
		"transcript": scriptorium.File("./examples/fixtures/transcript.md"),
		"glossary":   scriptorium.File("./examples/fixtures/glossary.yml"),
	},
})
if err != nil {
	return err
}
_ = prepared.Messages

Input helpers:

  • scriptorium.File(path) loads an input artifact from a file.
  • scriptorium.Inline(body) passes inline input content.
  • scriptorium.InlineWithURI(uri, body) passes inline content with URI metadata.

Run A Prompt

Run prepares the prompt, calls the configured LLM client, builds the output artifact, and validates the output.

result, err := engine.Run(ctx, scriptorium.RunRequest{
	PromptID: "generic.markdown_summary",
	APIKey:   apiKey,
	Inputs: map[string]scriptorium.ArtifactRef{
		"transcript": scriptorium.File("./examples/fixtures/transcript.md"),
		"glossary":   scriptorium.File("./examples/fixtures/glossary.yml"),
	},
})
if err != nil {
	return err
}
_ = result.Artifact

RunResult includes the run ID, output artifact, raw output, validation result, prompt/profile/model metadata, effective model parameters, input hashes, token/cache usage, and timing fields. Validation content failures return a successful RunResult with failed validation status. Runtime validation errors return ErrValidation.

For the public Go API, pass provider credentials with RunRequest.APIKey. The value is request-scoped, uses json:"-", is preferred over profile api_key_env by the default OpenAI-compatible client, and is not included in PreparedRun or RunResult JSON. Normal Go string formatting of RunRequest reports only whether a direct key is set. Do not store raw keys in config, prompt files, or profile YAML.

Avoid logging raw request structs with reflection-based debug dumpers; exported fields remain visible to tools that bypass String and GoString methods.

Inject An LLM Client

Use WithLLMClient for tests or custom model integrations:

type fakeLLM struct{}

func (fakeLLM) Generate(ctx context.Context, req scriptorium.GenerateRequest) (*scriptorium.GenerateResponse, error) {
	return &scriptorium.GenerateResponse{
		Content: "generated text",
		Usage:   scriptorium.TokenUsage{TotalTokens: 12},
	}, nil
}

engine, err := scriptorium.NewEngine(cfg, scriptorium.WithLLMClient(fakeLLM{}))

The injected client receives the rendered prompt, effective execution target, target presence metadata for explicit numeric overrides, structured-output spec, and request API key when provided. GenerateRequest.APIKey also uses json:"-", and normal Go string formatting reports only whether a direct key is set. Custom and fake clients should avoid logging or serializing it. WithLLMClient(nil) returns ErrInvalidConfig.

Request Overrides

RunRequest.Execution accepts per-request overrides. Numeric override fields are pointers so explicit zero values are preserved:

zero := 0
req.Execution = &scriptorium.ExecutionTargetOverride{
	MaxTokens: &zero,
}

ExecutionTargetOverride.ExtraParams accepts JSON-compatible values and copies typed maps/slices so later caller mutation does not affect the run. Unsupported values, non-string map keys, non-finite floats, and cycles return ErrInvalidRequest.

Errors

Public methods wrap context while preserving stable sentinel checks with errors.Is:

  • ErrInvalidConfig
  • ErrInvalidRequest
  • ErrPromptNotFound
  • ErrProfileNotFound
  • ErrPromptLoad
  • ErrProfileLoad
  • ErrArtifactLoad
  • ErrPromptRender
  • ErrLLMGenerate
  • ErrValidation

Example:

if errors.Is(err, scriptorium.ErrPromptNotFound) {
	return err
}

Examples

Run the prepare-only example from the repository root:

go run ./examples/go-library/prepare