Clean up completed roadmap docs

This commit is contained in:
2026-07-04 17:24:02 -05:00
parent d60ef66f53
commit e1e5351c5d
26 changed files with 0 additions and 952 deletions

View File

@@ -1,142 +0,0 @@
# Built-In Profiles Roadmap
This roadmap defines the target behavior for adding built-in execution profiles to Scriptorium.
Built-in profiles are useful for CLI, HTTP, subprocess, and library consumers, and they provide a clean foundation for public-package ergonomics.
## Motivation
Scriptorium currently requires a profile source for every run path. That is appropriate for fully custom deployments, but it creates unnecessary setup for common model targets where stable profile definitions can be shipped with the application.
Built-in profiles should let callers select standard profile IDs without creating local profile files. Users and downstream applications should still be able to override any built-in profile by providing a custom profile with the same ID.
## Target Behavior
Scriptorium should include a built-in set of execution profiles compiled into the binary/package.
Profile lookup should use this precedence:
1. user-provided or downstream-provided profiles;
2. built-in profiles;
3. profile-not-found error.
If a user profile and a built-in profile share the same ID, the user profile wins. This is intentional override behavior and should not be treated as a duplicate-profile error.
Duplicate profile IDs within the user profile source should remain invalid. Duplicate profile IDs within the built-in profile set should be prevented by tests. Duplicate IDs across the user source and built-in source are valid because they express override intent.
Once built-ins exist, `profile_dir` should no longer be required for CLI, HTTP, or public library engine construction. When no custom profile source is configured, Scriptorium should use the built-in profile repository alone. When a custom profile source is configured, Scriptorium should overlay it on top of the built-in repository.
## Architecture
Built-in profiles should be modeled as another implementation of the existing `profile.Repository` boundary.
Recommended repository structure:
- filesystem or custom profile repository for user-provided profiles;
- built-in profile repository backed by embedded profile YAML;
- overlay repository that checks the primary repository first and falls back to built-ins only when the primary returns `profile.ErrProfileNotFound`.
The runner should continue to depend only on `profile.Repository`. It should not know whether a selected profile came from a file, a built-in definition, or a future public-package source.
### Built-In Repository
Built-in definitions should be stored as normal profile YAML and embedded into the binary with Go `embed`.
Recommended package shape:
- `internal/profile/builtin` owns embedded built-in profile assets and exposes a repository constructor.
- built-in profile files live under that package in a stable asset directory.
- the built-in repository reuses the same strict decoding and validation rules as normal profiles.
Using YAML for built-ins keeps the built-in profile format aligned with the documented profile format and lets maintainers add stable definitions without duplicating profile construction logic in Go.
### FS Repository
The implementation should introduce or reuse an `fs.FS`-based profile repository rather than making the built-in loader special-purpose.
That repository supports:
- embedded built-in profile assets;
- embedded or virtual profile sources in public library work;
- fixture-based tests without temporary directory setup where useful.
The existing filesystem repository can remain as a thin path-based adapter, or it can delegate internally to the `fs.FS` repository where that is clean and maintainable.
### Overlay Repository
An overlay repository should compose two repositories:
- primary: user-provided, custom, or downstream profile source;
- fallback: built-in profile source.
Lookup behavior:
- return the primary result if primary lookup succeeds;
- if primary returns `profile.ErrProfileNotFound`, try fallback;
- if primary returns any other error, return that error and do not try fallback;
- return fallback result or fallback error.
This preserves strict validation of user profile sources. A malformed selected user profile should not silently fall through to a built-in with the same ID.
## CLI And HTTP Behavior
The CLI and HTTP server should no longer require `profile_dir` once built-in profiles are available.
Expected behavior:
- `profile_dir` omitted: built-ins are available.
- `profile_dir` provided: profiles from that directory override built-ins with the same ID.
- selected profile ID present only in built-ins: run succeeds.
- selected profile ID present in both custom profiles and built-ins: custom profile is used.
- selected profile ID missing from both sources: existing profile-not-found behavior is preserved.
- selected profile ID matches a malformed custom profile: profile-load failure is returned, not fallback to built-in.
Configuration and CLI documentation should describe `profile_dir` as optional once built-in profiles are available.
## Public Library Interaction
This feature should support the current public package behavior and the production library roadmap.
For the current public engine, `ProfileDir` should become optional once built-ins exist. A caller that does not configure a custom profile directory should still be able to use built-in profile IDs.
The production library roadmap may add `fs.FS`, single-file, and in-memory profile sources. Those sources should become overlay primaries above the same built-in repository.
Credential behavior for built-ins should follow the active execution path:
- current CLI/HTTP behavior may continue to use `api_key_env` in profile definitions;
- the public library API may supply direct API-key values without changing built-in profile IDs;
- built-in profile files must never contain raw API keys.
## Scope
In scope:
- built-in execution profile assets;
- strict validation of all built-in profiles;
- `fs.FS` profile repository support where needed for embedded assets;
- overlay profile repository with user-over-built-in precedence;
- optional `profile_dir` for CLI, HTTP, and public engine construction;
- tests for lookup precedence, override behavior, duplicate handling, and error behavior;
- documentation of CLI/config/profile behavior.
Out of scope:
- changing the profile YAML format;
- accepting raw API keys in profile YAML;
- adding a mutable runtime profile registry;
- adding a provider/model catalog that must track rapidly changing model availability;
- changing prompt `default_profile` semantics beyond allowing built-in IDs;
- implementing the broader `fs.FS` prompt/schema/library source work from `docs/roadmap/library.md`.
## Acceptance Criteria
- Scriptorium can run or render using a built-in profile ID with no configured `profile_dir`.
- CLI `run`, CLI `render`, and HTTP `serve` no longer fail solely because `profile_dir` is omitted.
- A profile in `profile_dir` overrides a built-in profile with the same ID.
- Duplicate profile IDs inside `profile_dir` remain invalid.
- Duplicate profile IDs inside the built-in profile set are caught by tests.
- A malformed selected custom profile does not fall back to a built-in profile with the same ID.
- Profile-not-found behavior remains clear when an ID exists in neither custom profiles nor built-ins.
- Built-in profiles are loaded through the same validation rules as file-based profiles.
- Existing runtime override behavior continues to apply to built-in profiles.
- Existing CLI, HTTP, and public error mapping remains consistent with current profile-load and profile-not-found semantics.

View File

@@ -1,451 +0,0 @@
# Built-In Profiles And Library API Implementation Plan
This plan implements the target states in:
- `docs/roadmap/builtins.md`
- `docs/roadmap/library.md`
Audience: LLM coding agents implementing the work in order. Review and follow `docs/policy/architecture.md`, `docs/policy/development.md`, and `docs/policy/documentation.md` before changing code.
## Global Constraints
- Implement built-in profiles before the public library production upgrades.
- Keep orchestration in `internal/usecase`; adapters and the public package should translate inputs and wire components.
- Keep `internal/*` packages internal. Public package types must remain facade types.
- Do not add a mutable global profile registry.
- Do not add a credential resolver or secret-manager abstraction.
- Do not accept raw API keys in YAML/JSON config, profile files, prompt files, CLI flags, or HTTP request bodies.
- Do not emit raw API keys in prepared output, run results, logs, or examples.
- Prefer standard library APIs. Do not add dependencies unless a later implementation prompt explicitly approves one.
- Keep each stage passing `go test ./...` before moving to the next stage.
## Stage 1: Profile Repository Foundations
Goal: add reusable profile repository primitives that support built-ins without changing runner behavior.
### Implementation Steps
1. Add an `fs.FS`-backed profile repository in `internal/profile`.
- Constructor shape should be similar to `NewFSRepository(fsys fs.FS, root string) Repository`.
- It must scan YAML files recursively below `root`.
- It must use the same strict YAML decoding, profile validation, raw `api_key` rejection, and duplicate-ID behavior as the existing filesystem repository.
- It must return the existing profile package sentinel errors where applicable.
2. Refactor shared profile-loading behavior.
- Avoid duplicating validation and metadata logic between filesystem and `fs.FS` repositories.
- The existing `NewFilesystemRepository(dir)` API should remain available.
- It may either delegate to the `fs.FS` repository through `os.DirFS` or share unexported loader helpers.
3. Add an overlay profile repository in `internal/profile`.
- Constructor shape should be similar to `NewOverlayRepository(primary, fallback Repository) Repository`.
- Lookup must return the primary result when primary succeeds.
- Lookup must fall back only when `errors.Is(err, profile.ErrProfileNotFound)` for the primary.
- Lookup must return primary load/validation errors directly and must not fall back after those errors.
- Nil repository inputs should be handled deliberately. Prefer treating nil primary as "no primary" and requiring a non-nil fallback for built-in-only operation.
### Tests
Add focused tests under `internal/profile`.
Required coverage:
- `NewFSRepository` loads valid profiles from nested directories.
- `NewFSRepository` rejects unknown YAML fields.
- `NewFSRepository` rejects raw `api_key` in the selected profile.
- `NewFSRepository` ignores raw `api_key` in non-selected profiles, matching current filesystem behavior.
- `NewFSRepository` rejects duplicate IDs within one source.
- `NewFilesystemRepository` still satisfies all existing repository tests.
- `NewOverlayRepository` returns primary matches before fallback matches.
- `NewOverlayRepository` falls back on primary not found.
- `NewOverlayRepository` does not fall back after a primary invalid YAML/profile/raw-key error.
- `NewOverlayRepository` returns not found when both sources miss.
### Verification
Run:
```bash
go test ./internal/profile
go test ./...
```
## Stage 2: Built-In Profile Assets And Wiring
Goal: compile built-in profiles into Scriptorium and make profile lookup use user-over-built-in precedence.
### Implementation Steps
1. Add an `internal/profile/builtin` package.
- Store built-in profile YAML files in a stable asset directory under that package.
- Use Go `embed` to compile those files into the binary/package.
- Expose a constructor such as `builtin.NewRepository() profile.Repository`.
- The repository should use the `fs.FS` profile repository from Stage 1.
2. Add built-in profile validation tests.
- Tests should load every built-in profile through the real profile loader.
- Tests should fail if the built-in profile set contains duplicate IDs.
- Tests should fail if any built-in profile contains raw `api_key`.
3. Add built-in profiles.
- Use `docs/roadmap/profiles/` as the source catalog for the initial built-in profile set.
- Copy those YAML files into the built-in profile asset directory, preserving provider subdirectories unless the implementation has a clear reason to flatten them.
- Do not invent a broad provider/model catalog.
- Do not add raw API keys.
- Built-in profiles may use `api_key_env` for CLI/HTTP compatibility when the provider requires authentication.
4. Wire repositories through a small helper.
- Add an internal helper near adapter wiring, or in `internal/profile`, that returns:
- built-in repository only when no custom profile source is configured;
- overlay repository when a custom profile source is configured.
- The runner should still receive only a `profile.Repository`.
5. Make `profile_dir` optional.
- CLI `run`, CLI `render`, and HTTP `serve` argument/config validation should require `prompt_dir` but no longer require `profile_dir`.
- Public `NewEngine` should no longer reject an empty `Config.ProfileDir`.
- When `profile_dir` is empty, wire only built-ins.
- When `profile_dir` is non-empty, wire filesystem profiles over built-ins.
### Tests
Add or update tests under `internal/adapter/cli`, `internal/adapter/http`, root public package tests, and profile/builtin tests.
Required coverage:
- CLI parse/config tests accept missing `profile_dir` when `prompt_dir` is present.
- HTTP serve parse/config tests accept missing `profile_dir` when `prompt_dir` is present.
- Public `NewEngine` accepts missing `ProfileDir`.
- A built-in profile ID can be selected with no custom `profile_dir`.
- A prompt `default_profile` can refer to a built-in profile ID.
- A custom profile in `profile_dir` overrides a built-in with the same ID.
- A malformed selected custom profile does not fall back to a built-in with the same ID.
- A missing profile ID still maps to the existing profile-not-found behavior.
- Existing duplicate-ID tests for filesystem profiles continue to fail within the custom source.
### Documentation
After code behavior exists, update non-roadmap docs:
- `docs/config.md`: document `profile_dir` as optional and describe built-in fallback/override behavior.
- `docs/cli.md`: remove claims that `--profile-dir` is required.
- `docs/internal/adapters.md`: document built-in profile repository composition.
- Any affected examples or README snippets that imply `profile_dir` is mandatory.
Do not document built-in profile IDs outside implemented assets.
### Verification
Run:
```bash
go test ./internal/profile ./internal/adapter/cli ./internal/adapter/http .
go test ./...
go run ./cmd/scriptorium render \
--config ./examples/config.yml \
--prompt generic.markdown_summary \
--input transcript=./examples/fixtures/transcript.md \
--input glossary=./examples/fixtures/glossary.yml \
--format json
```
Also run a new smoke command that uses a built-in profile without `--profile-dir` once a concrete built-in profile ID is available.
## Stage 3: Public API Credential Value
Goal: support the production library credential model: a direct API-key Go value, without adding a resolver or accepting raw keys in serialized config.
### Public API Decision
Use a single public credential-supply method:
```go
type RunRequest struct {
// existing fields...
APIKey string `json:"-"`
}
```
Do not add `Config.APIKey`, `WithAPIKey`, or a credential resolver in this stage. A request-level value avoids storing secrets on long-lived engines and supports per-tenant callers.
### Implementation Steps
1. Add direct API-key plumbing through internal request/target types.
- Add an internal direct API-key field where needed, with `json:"-"` and `yaml:"-"` tags.
- Convert public `RunRequest.APIKey` into the internal request.
- Carry the value to the effective execution target used for LLM generation.
- Do not include the value in prepared/run public results, formatted output, logs, hashes, or docs examples.
2. Update credential validation.
- If a selected/effective profile requires authentication and a direct API key is provided, do not require the environment variable to be set for the public path.
- Preserve existing CLI/HTTP behavior that uses `api_key_env`.
- Preserve existing errors for missing environment variables in CLI/HTTP paths.
3. Update the OpenAI-compatible LLM client.
- Prefer the direct API-key value when present.
- Fall back to existing `api_key_env` behavior for CLI/HTTP compatibility.
- Never serialize or log the direct API-key value.
4. Update public LLM injection conversion.
- Do not expose the raw API key to injected public `LLMClient` implementations unless that is strictly necessary for custom LLM execution.
- If custom LLM clients need the key, expose it only on the public `GenerateRequest` with `json:"-"` and document that fake/test clients should avoid logging it.
### Tests
Required coverage:
- Public `Run` can call the default OpenAI-compatible client path with a direct API key without requiring the configured `api_key_env` environment variable.
- CLI/HTTP behavior using `api_key_env` still works.
- Missing credentials still fail clearly when a selected profile requires authentication and neither direct key nor usable env value is available.
- Public `PreparedRun` and `RunResult` JSON do not include the direct API key.
- Formatted prepared output does not include the direct API key.
- Direct API-key values are not included in hashes.
- Injected fake LLM tests either receive no key or receive it only through a `json:"-"` field, depending on the implementation choice above.
### Verification
Run:
```bash
go test ./internal/llm ./internal/usecase ./internal/adapter/cli ./internal/adapter/http .
go test ./...
```
## Stage 4: Public Asset Source Options
Goal: let library consumers load standard Scriptorium prompt/profile/schema assets from directories, single files, and `fs.FS` sources.
### Public API
Keep existing `Config` directory fields working. Add options:
```go
func WithPromptFS(fsys fs.FS, root string) Option
func WithPromptFile(path string) Option
func WithProfileFS(fsys fs.FS, root string) Option
func WithProfileFile(path string) Option
func WithSchemaFS(fsys fs.FS, root string) Option
func WithSchemaFile(path string) Option
```
Rules:
- Directory `Config` fields remain the compatibility path.
- Explicit options override the corresponding `Config` directory field.
- Prompt source is required.
- Profile source is optional because built-in profiles exist.
- Schema source is optional and should default to the current schema default behavior when not configured.
- Nil `fs.FS` values, empty required roots, and invalid option combinations return `ErrInvalidConfig`.
### Implementation Steps
1. Add `fs.FS` prompt-definition support.
- Implement an `fs.FS` prompt repository that preserves existing strict prompt YAML behavior.
- Preserve prompt lookup by YAML `id`, not path.
- Preserve duplicate prompt ID errors within one source.
- Resolve `content_file` relative to the prompt file's directory inside the same source.
- Keep the existing filesystem repository API; it may delegate to the `fs.FS` implementation.
2. Add public prompt source wiring.
- `Config.PromptDir` wires the filesystem repository.
- `WithPromptFS` wires the `fs.FS` repository.
- `WithPromptFile(path)` wires a single-file source and must still select by prompt YAML `id`.
3. Add public profile source wiring.
- Reuse the Stage 1 profile `fs.FS` repository.
- `WithProfileFS` and `WithProfileFile` become overlay primaries above built-ins.
- Empty profile source still means built-ins only.
4. Add schema `fs.FS` support.
- Extend or wrap the standard validator so schema files can be loaded from an `fs.FS` source.
- `WithSchemaFS` should preserve existing `schema_path` semantics.
- `WithSchemaFile(path)` should expose the file by its base name; prompts using it should set `schema_path` to that base name.
5. Keep adapter scope narrow.
- This stage is for the public package and shared repositories/validators.
- Do not change CLI/HTTP request shapes for prompt or schema `fs.FS` sources.
### Tests
Required coverage:
- Public `Prepare` works with prompt definitions from `embed.FS`.
- `content_file` references resolve relative to the prompt file in `embed.FS`.
- Public `Prepare` works with `WithPromptFile`.
- Public `Run` or `Prepare` works with `WithProfileFS` over built-ins.
- Public `Run` or `Prepare` works with `WithProfileFile` over built-ins.
- Public structured-output schema validation works with `WithSchemaFS`.
- `WithSchemaFile` works when the prompt's `schema_path` is the schema file base name.
- Explicit source options override `Config` directory fields.
- Invalid/nil source options return `ErrInvalidConfig`.
- Existing filesystem prompt/profile/schema behavior remains unchanged.
### Verification
Run:
```bash
go test ./internal/promptdef ./internal/profile ./internal/validate .
go test ./...
```
## Stage 5: Public In-Memory Profiles And Profile Templates
Goal: allow library callers to provide typed profile values and construct stable OpenAI-compatible profile templates without generating YAML.
### Public API
Add a public profile facade aligned with the profile YAML contract:
```go
type Profile struct {
ID string
Endpoint string
Model string
Temperature float64
MaxTokens int
TopP float64
TimeoutSeconds int
ServiceTier string
ReasoningEffort string
APIKeyRequired bool
ExtraParams map[string]any
}
func WithProfiles(profiles ...Profile) Option
```
Add an OpenAI-compatible template constructor:
```go
type OpenAICompatibleProfileConfig struct {
ID string
Endpoint string
Model string
APIKeyRequired bool
Temperature float64
MaxTokens int
TopP float64
TimeoutSeconds int
ServiceTier string
ReasoningEffort string
ExtraParams map[string]any
}
func OpenAICompatibleProfile(cfg OpenAICompatibleProfileConfig) Profile
```
Rules:
- `WithProfiles` profiles override built-ins with the same ID.
- If `WithProfiles` and a file/FS profile source are both configured, in-memory profiles have highest precedence, then file/FS profiles, then built-ins.
- Public profile values must not include raw API-key fields.
- Use `APIKeyRequired` to indicate whether `RunRequest.APIKey` is required for the public path. Internal conversion may map this to the existing auth-required/profile credential model without exposing raw keys.
### Implementation Steps
1. Add a profile repository for public in-memory `Profile` values.
- Validate with the same effective rules as YAML profiles.
- Reject duplicate IDs within the provided values.
- Deep-copy `ExtraParams` across public/internal boundaries.
2. Compose profile sources in public `NewEngine`.
- Highest: in-memory public profiles.
- Next: configured profile file/FS/directory source.
- Fallback: built-in profiles.
3. Add template constructor conversion.
- `OpenAICompatibleProfile` should be a convenience constructor only.
- It should not register global state.
- It should not maintain a broad model catalog.
4. Ensure direct API-key behavior works with in-memory/template profiles.
- Profiles marked `APIKeyRequired` should require `RunRequest.APIKey` for public default LLM execution.
- Profiles not marked `APIKeyRequired` should not require an API key.
### Tests
Required coverage:
- Public `Prepare`/`Run` uses `WithProfiles` without any profile files.
- In-memory profiles override built-ins.
- In-memory profiles override file/FS profile sources when IDs collide.
- Duplicate in-memory profile IDs return `ErrInvalidConfig`.
- Template-created profiles execute through the same path as normal profiles.
- Template-created profiles requiring an API key work with `RunRequest.APIKey`.
- Template-created profiles that do not require an API key work without one.
- `ExtraParams` in public profiles are deep-copied and isolated from caller mutation.
### Verification
Run:
```bash
go test .
go test ./...
```
## Stage 6: Public Documentation And Examples
Goal: document implemented behavior in canonical locations after code exists.
### Documentation
Update:
- `README.md`: add a short pointer to library usage and built-in profiles without turning the README into a manual.
- `docs/config.md`: document optional `profile_dir`, built-in override behavior, and implemented built-in profile IDs.
- `docs/cli.md`: document optional `--profile-dir` and built-in profile selection.
- `docs/internal/adapters.md`: document repository composition and public library adapter surface.
- `docs/consumers/api.md`: describe public consumer surfaces at a high level.
- `docs/consumers/pkg-scriptorium.md`: document root package usage, source options, direct API-key value, errors, and examples.
- `docs/integrations/openai-compatible-chat.md`: update only if direct API-key plumbing changes provider-call semantics.
Do not document unimplemented roadmap behavior outside `docs/roadmap/`.
### Examples
Add or update copyable examples that require no real provider credentials:
- prepare with built-in profile and no `profile_dir`;
- public library prepare from `embed.FS`;
- public library run with injected fake LLM;
- public library run with typed/template profile and direct API key, using a fake/local provider path so no real secret is required.
Examples must be secret-free.
### Tests And Smoke Commands
Required verification:
```bash
go test ./...
go run ./cmd/scriptorium render \
--config ./examples/config.yml \
--prompt generic.markdown_summary \
--input transcript=./examples/fixtures/transcript.md \
--input glossary=./examples/fixtures/glossary.yml \
--format json
```
Also run smoke commands for any new examples, such as:
```bash
go run ./examples/go-library/prepare
```
If an example relies on a specific built-in profile ID, use an ID from `docs/roadmap/profiles/` and include that smoke command in the implementing change.
## Final Completion Checklist
Before marking the combined feature complete:
1. `go test ./...` passes.
2. Existing CLI render smoke command passes.
3. At least one smoke command proves built-in profile lookup works without `profile_dir`.
4. Public package examples compile/run without real provider credentials.
5. `profile_dir` is optional in CLI, HTTP, and public engine construction.
6. Custom profiles override built-ins with the same ID.
7. Malformed selected custom profiles do not fall back to built-ins.
8. Built-in profiles use the same validation rules as file profiles.
9. Public `RunRequest.APIKey` is the documented library credential method.
10. Raw API keys do not appear in YAML/JSON config, prepared output, run results, logs, hashes, or examples.
11. Non-roadmap docs describe only implemented behavior.

View File

@@ -1,176 +0,0 @@
# Library API Production Roadmap
This roadmap defines the target state for making Scriptorium's public Go package production-ready for downstream applications while preserving the existing CLI and HTTP behavior.
The library remains an additional adapter surface. It should not replace the subprocess, CLI, or HTTP contracts that already exist.
## Motivation
Many Go applications can use Scriptorium more cleanly as an imported package than as a subprocess. A downstream developer should be able to keep prompt assets in standard Scriptorium format, pass application data through a small adapter, and receive a typed response without reimplementing prompt rendering, profile resolution, validation, or OpenAI-compatible request construction.
The core consumer story is:
- the downstream app owns one or more `prompt.yml` files in standard Scriptorium format;
- those prompts may use inline content, `content_file` references, variables, cache-control markers, and structured-output schemas;
- the app may provide its own `profile.yml`, or select a standard built-in/profile-template configuration;
- the app supplies an API key as a normal Go value when the selected profile requires one;
- the app calls the public Go package to prepare or run the request and receives typed results.
## Current State
The public package already provides the first library facade:
- root package import;
- typed engine construction;
- typed prepare/run requests and results;
- file and inline artifact references;
- execution overrides;
- custom LLM injection for testing or alternate execution;
- public error categories that map internal failures to stable caller-facing errors.
The remaining production-readiness gaps are mostly about consumer ergonomics and asset sourcing:
- callers are still oriented around filesystem prompt/profile/schema directories;
- embedded prompt/profile/schema assets are not a first-class public use case;
- standard or built-in profile selection is not yet available;
- library credential supply is still tightly coupled to environment-variable lookup rather than direct API-key values;
- public documentation and examples need to show the intended downstream app adapter pattern.
## Target State
The public package should let a downstream Go application use standard Scriptorium assets without temporary directories, subprocess invocation, or internal package imports.
### Prompt Assets
The library should support prompt definitions from:
- existing prompt directories;
- a single prompt file;
- `fs.FS`, including `embed.FS`.
Prompt syntax should remain the standard Scriptorium prompt YAML format. `content_file` references should continue to be supported and should resolve relative to the prompt definition's source location within the same asset source.
The public API should not introduce a separate in-code prompt DSL as the primary path. YAML remains the canonical authoring format so prompts can be shared between CLI, HTTP, subprocess, and library usage.
### Schema Assets
Structured-output schemas should be loadable from the same kinds of sources as prompt definitions:
- existing schema directories;
- a single schema file where appropriate;
- `fs.FS`, including `embed.FS`.
Schema references should retain the existing prompt-format semantics. A schema referenced by a prompt should resolve through the configured schema source, not through ad hoc caller code.
### Profile Assets
The library should support both custom and standard profile configuration:
- existing profile directories;
- a single profile file;
- `fs.FS`, including `embed.FS`;
- direct public profile values for applications that already have profile configuration in memory;
- built-in/profile-template helpers for common OpenAI-compatible targets.
Custom profiles and built-in/template profiles should flow through the same internal profile resolution and request-construction path. The built-in path should not become a separate execution mode.
### Built-In Profile Templates
Built-in support should favor stable profile templates over a large registry of fixed model IDs.
For example, the public package should make it easy to construct or select an OpenAI-compatible profile by supplying the durable parts of the profile:
- profile ID or name;
- base URL;
- model;
- whether the profile requires an API key;
- default numeric parameters where desired;
- structured-output and extra-parameter behavior consistent with normal profiles.
The package may include a small set of named helpers for common OpenAI-compatible services, but those helpers should avoid hard-coding a broad and fast-changing list of model names.
### Credentials
The public package must keep raw API keys out of prompt/profile YAML, prepared-run output, run results, logs, and examples.
For the public library API, the single supported credential-supply method should be a direct API-key value passed by the consuming Go application. The consuming application is responsible for loading and managing its own secrets before calling Scriptorium.
This may be exposed as a field such as `Config.APIKey`, an option such as `WithAPIKey`, or an equivalent request/engine-level value that is easy to pass through an application adapter. The exact API should avoid accidental serialization in prepared output, run results, logs, and examples.
The public package should not encourage raw API-key storage in prompt/profile YAML. Existing CLI behavior may continue to use environment-variable references for compatibility, but the production library path should not introduce a separate credential resolver or secret-manager abstraction.
### Public API Shape
The public API should remain narrow, idiomatic, and stable. Recommended additions include:
- engine options for prompt/profile/schema directories;
- engine options for prompt/profile/schema `fs.FS` sources;
- engine options for single prompt/profile/schema files where useful;
- public profile/template constructors that map to internal profile definitions;
- a direct API-key value for profiles that require authentication;
- examples showing `embed.FS`, custom profile files, template profile selection, and fake LLM testing.
The public package should continue to expose facade types rather than exporting internal package types. Internal package layout should remain free to evolve.
## Scope
In scope:
- first-class `fs.FS` support for public library prompt, profile, and schema sources;
- ergonomic single-file asset options where they reduce caller boilerplate;
- built-in/profile-template helpers for common OpenAI-compatible usage;
- in-memory public profile values where appropriate;
- direct API-key value support for the public library path;
- consumer-facing examples under `examples/`;
- consumer package documentation under `docs/consumers/` once behavior is implemented;
- tests proving library behavior matches existing CLI/use-case behavior.
Out of scope:
- changing standard prompt, profile, or schema file formats;
- exposing internal packages as public API;
- replacing or removing CLI, HTTP, or subprocess support;
- adding a multi-step workflow engine;
- adding non-Go bindings;
- maintaining a comprehensive provider/model catalog;
- accepting raw API keys in serialized YAML/JSON configuration;
- adding a credential resolver or secret-manager abstraction.
## Acceptance Criteria
- A Go caller can import the root package and run a standard Scriptorium prompt without invoking a subprocess.
- A Go caller can use prompt definitions from `embed.FS`, including prompts with `content_file` references.
- A Go caller can use structured-output schemas from `embed.FS` or filesystem sources.
- A Go caller can provide a custom profile from filesystem, `fs.FS`, or public in-memory profile values.
- A Go caller can select a standard OpenAI-compatible profile template without writing a full profile file.
- A Go caller can supply an API key as a normal Go value without raw secrets appearing in serialized config, prepared output, or results.
- Public library behavior remains consistent with CLI/HTTP semantics for rendering, validation, profile resolution, runtime overrides, structured output, cache control, and LLM invocation.
- Existing CLI and HTTP behavior remains unchanged.
- Library tests use fake or local LLM boundaries and do not require real provider credentials.
- Public docs outside `docs/roadmap/` describe only implemented behavior after the feature is built.
## Design Decisions
### Asset Source API
Add first-class `fs.FS` options for prompt, profile, and schema sources while keeping existing directory-based configuration. Also add single-file convenience options where they remove meaningful caller boilerplate. Resolve `content_file` references relative to the prompt file's location inside the same source.
Reasoning:
This is the most idiomatic path for production Go libraries because it supports `embed.FS`, `os.DirFS`, tests, and in-memory fixture files through the same abstraction. It also avoids requiring downstream applications to unpack embedded assets into temporary directories.
### Built-In Profile Strategy
Provide stable profile-template helpers for OpenAI-compatible endpoints rather than a broad registry of fixed provider/model profiles. Let callers choose the model and endpoint where those values are service-specific or fast-changing.
Reasoning:
Endpoint shape and credential mechanics are relatively stable; model catalogs change frequently. Templates give consumers a short, correct path without making Scriptorium responsible for tracking every provider's model list.
### In-Memory Profile Values
Expose a small public profile facade type for in-memory profile configuration and map it to internal profile definitions. Keep it intentionally aligned with the existing profile YAML contract.
Reasoning:
Many applications already hold configuration in typed structs and should not need to generate YAML files just to call Scriptorium. A public facade keeps internal types private while making the library practical for production use.

View File

@@ -1,9 +0,0 @@
id: aion-2
endpoint: https://openrouter.ai/api/v1
model: aion-labs/aion-2.0
temperature: 0.72
reasoning_effort: high
top_p: 0.95
timeout_seconds: 180
api_key_env: OPENROUTER_API_KEY
service_tier: flex

View File

@@ -1,7 +0,0 @@
id: claude-fable-latest
endpoint: https://openrouter.ai/api/v1
model: "~anthropic/claude-fable-latest"
reasoning_effort: high
timeout_seconds: 600
api_key_env: OPENROUTER_API_KEY
service_tier: flex

View File

@@ -1,7 +0,0 @@
id: claude-haiku-latest
endpoint: https://openrouter.ai/api/v1
model: "~anthropic/claude-haiku-latest"
reasoning_effort: medium
timeout_seconds: 240
api_key_env: OPENROUTER_API_KEY
service_tier: flex

View File

@@ -1,7 +0,0 @@
id: claude-opus-latest
endpoint: https://openrouter.ai/api/v1
model: "~anthropic/claude-opus-latest"
reasoning_effort: high
timeout_seconds: 240
api_key_env: OPENROUTER_API_KEY
service_tier: flex

View File

@@ -1,7 +0,0 @@
id: claude-sonnet-latest
endpoint: https://openrouter.ai/api/v1
model: "~anthropic/claude-sonnet-latest"
reasoning_effort: high
timeout_seconds: 240
api_key_env: OPENROUTER_API_KEY
service_tier: flex

View File

@@ -1,7 +0,0 @@
id: deepseek-3-2
endpoint: https://openrouter.ai/api/v1
model: deepseek/deepseek-v3.2
reasoning_effort: high
timeout_seconds: 180
api_key_env: OPENROUTER_API_KEY
service_tier: flex

View File

@@ -1,7 +0,0 @@
id: deepseek-4-pro
endpoint: https://openrouter.ai/api/v1
model: deepseek/deepseek-v4-pro
reasoning_effort: high
timeout_seconds: 180
api_key_env: OPENROUTER_API_KEY
service_tier: flex

View File

@@ -1,9 +0,0 @@
id: gemini-2-flash-lite
endpoint: https://openrouter.ai/api/v1
model: "google/gemini-2.5-flash-lite"
#temperature: 0.15
reasoning_effort: high
#top_p: 0.98
timeout_seconds: 240
api_key_env: OPENROUTER_API_KEY
service_tier: flex

View File

@@ -1,9 +0,0 @@
id: gemini-2-flash
endpoint: https://openrouter.ai/api/v1
model: "google/gemini-2.5-flash"
#temperature: 0.15
reasoning_effort: high
#top_p: 0.98
timeout_seconds: 240
api_key_env: OPENROUTER_API_KEY
service_tier: flex

View File

@@ -1,9 +0,0 @@
id: gemini-2-pro
endpoint: https://openrouter.ai/api/v1
model: "google/gemini-2.5-pro"
#temperature: 0.15
reasoning_effort: high
#top_p: 0.98
timeout_seconds: 240
api_key_env: OPENROUTER_API_KEY
service_tier: flex

View File

@@ -1,9 +0,0 @@
id: gemini-3-flash-lite
endpoint: https://openrouter.ai/api/v1
model: "google/gemini-3.1-flash-lite"
#temperature: 0.15
reasoning_effort: high
#top_p: 0.98
timeout_seconds: 240
api_key_env: OPENROUTER_API_KEY
service_tier: flex

View File

@@ -1,9 +0,0 @@
id: gemini-flash-latest
endpoint: https://openrouter.ai/api/v1
model: "~google/gemini-flash-latest"
#temperature: 0.15
reasoning_effort: high
#top_p: 0.98
timeout_seconds: 240
api_key_env: OPENROUTER_API_KEY
service_tier: flex

View File

@@ -1,9 +0,0 @@
id: gemini-pro-latest
endpoint: https://openrouter.ai/api/v1
model: "~google/gemini-pro-latest"
#temperature: 0.15
reasoning_effort: high
#top_p: 0.98
timeout_seconds: 240
api_key_env: OPENROUTER_API_KEY
service_tier: flex

View File

@@ -1,9 +0,0 @@
id: gemma-4-31b
endpoint: https://openrouter.ai/api/v1
model: google/gemma-4-31b-it:exacto
temperature: 0.15
reasoning_effort: high
top_p: 0.98
timeout_seconds: 240
api_key_env: OPENROUTER_API_KEY
service_tier: flex

View File

@@ -1,9 +0,0 @@
id: minimax-m2
endpoint: https://openrouter.ai/api/v1
model: minimax/minimax-m2.5
temperature: 0.5
reasoning_effort: high
top_p: 0.95
timeout_seconds: 180
api_key_env: OPENROUTER_API_KEY
service_tier: flex

View File

@@ -1,9 +0,0 @@
id: minimax-m3
endpoint: https://openrouter.ai/api/v1
model: minimax/minimax-m3
#temperature: 0.5
reasoning_effort: high
#top_p: 0.95
timeout_seconds: 180
api_key_env: OPENROUTER_API_KEY
service_tier: flex

View File

@@ -1,7 +0,0 @@
id: mistral-large-2512
endpoint: https://openrouter.ai/api/v1
model: mistralai/mistral-large-2512
temperature: 0.15
top_p: 0.98
timeout_seconds: 180
api_key_env: OPENROUTER_API_KEY

View File

@@ -1,8 +0,0 @@
id: mistral-medium-3-5
endpoint: https://openrouter.ai/api/v1
model: mistralai/mistral-medium-3-5
temperature: 0.15
reasoning_effort: high
top_p: 0.98
timeout_seconds: 180
api_key_env: OPENROUTER_API_KEY

View File

@@ -1,7 +0,0 @@
id: mistral-small-3
endpoint: https://openrouter.ai/api/v1
model: mistralai/mistral-small-3.2-24b-instruct
temperature: 0.05
top_p: 1.0
timeout_seconds: 180
api_key_env: OPENROUTER_API_KEY

View File

@@ -1,8 +0,0 @@
id: mistral-small-4
endpoint: https://openrouter.ai/api/v1
model: mistralai/mistral-small-2603
temperature: 0.1
reasoning_effort: high
top_p: 0.98
timeout_seconds: 180
api_key_env: OPENROUTER_API_KEY

View File

@@ -1,7 +0,0 @@
id: nemotron-3-ultra
endpoint: https://openrouter.ai/api/v1
model: nvidia/nemotron-3-ultra-550b-a55b
reasoning_effort: high
timeout_seconds: 180
api_key_env: OPENROUTER_API_KEY
service_tier: flex

View File

@@ -1,7 +0,0 @@
id: gpt-5-mini
endpoint: https://openrouter.ai/api/v1
model: "openai/gpt-5.4-mini"
reasoning_effort: high
timeout_seconds: 240
api_key_env: OPENROUTER_API_KEY
service_tier: flex

View File

@@ -1,7 +0,0 @@
id: gpt-5-nano
endpoint: https://openrouter.ai/api/v1
model: "openai/gpt-5.4-nano"
reasoning_effort: high
timeout_seconds: 240
api_key_env: OPENROUTER_API_KEY
service_tier: flex