# Package scriptorium Import path: ```go 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 ```go 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. For `WithPromptFS`, the configured root is a containment boundary: prompt `content_file` paths resolve relative to the prompt file and must remain inside that root. Profile options overlay custom profiles above built-ins. For `WithSchemaFS`, prompt `schema_path` values resolve inside the configured root. Absolute paths and relative traversal outside those `fs.FS` roots are rejected. 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: ```go 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. ```go 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. ```go 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: ```go 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: ```go 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: ```go if errors.Is(err, scriptorium.ErrPromptNotFound) { return err } ``` ## Examples Run the prepare-only example from the repository root: ```bash go run ./examples/go-library/prepare ```