Add a roadmap and implementation plan for an initial public library package
This commit is contained in:
134
docs/roadmap/library.md
Normal file
134
docs/roadmap/library.md
Normal file
@@ -0,0 +1,134 @@
|
||||
# Library API Roadmap
|
||||
|
||||
This roadmap defines the target behavior for making Scriptorium usable as an imported Go library while retaining the current standalone CLI and HTTP application behavior.
|
||||
|
||||
The implementation plan for this feature lives in `docs/roadmap/implementation.md`.
|
||||
|
||||
## Motivation
|
||||
|
||||
Scriptorium is currently optimized for subprocess use by other applications. That contract remains useful because it is language-neutral, operationally simple, and process-isolated.
|
||||
|
||||
For Go callers, an imported library should provide:
|
||||
|
||||
- typed requests and results instead of stdout/stderr parsing;
|
||||
- direct `context.Context` cancellation;
|
||||
- lower overhead for repeated calls;
|
||||
- easier test integration through injected clients or fixtures;
|
||||
- direct access to prepared-run data without process management;
|
||||
- fewer integration points where secrets or output metadata can be mishandled.
|
||||
|
||||
The library is an additional adapter surface, not a replacement for the CLI or HTTP API.
|
||||
|
||||
## Target State
|
||||
|
||||
Scriptorium should expose a small public Go API suitable for common embedding use cases:
|
||||
|
||||
- construct an engine from app-level settings such as prompt, profile, and schema directories;
|
||||
- prepare a prompt request without calling an LLM;
|
||||
- run a prompt request and receive a typed result;
|
||||
- pass file and inline artifacts;
|
||||
- apply profile selection, runtime overrides, vars, validation behavior, cache-control behavior, and structured-output behavior consistently with CLI/HTTP;
|
||||
- inject a custom LLM client or HTTP client where needed;
|
||||
- preserve existing CLI and HTTP behavior by continuing to route all entry paths through the same use-case layer.
|
||||
|
||||
The public library API should be stable, narrow, and intentionally higher-level than the current `internal/*` package layout.
|
||||
|
||||
## Public Package Policy
|
||||
|
||||
The public package should be the module root:
|
||||
|
||||
```go
|
||||
import "gitea.maximumdirect.net/eric/scriptorium"
|
||||
```
|
||||
|
||||
Recommended usage shape:
|
||||
|
||||
```go
|
||||
engine, err := scriptorium.NewEngine(scriptorium.Config{
|
||||
PromptDir: "./prompts",
|
||||
ProfileDir: "./profiles",
|
||||
SchemaDir: "./schemas",
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
prepared, err := engine.Prepare(ctx, scriptorium.RunRequest{
|
||||
PromptID: "generic.markdown_summary",
|
||||
Inputs: map[string]scriptorium.ArtifactRef{
|
||||
"transcript": scriptorium.File("./transcript.md"),
|
||||
},
|
||||
})
|
||||
|
||||
result, err := engine.Run(ctx, scriptorium.RunRequest{
|
||||
PromptID: "generic.markdown_summary",
|
||||
Inputs: map[string]scriptorium.ArtifactRef{
|
||||
"transcript": scriptorium.File("./transcript.md"),
|
||||
},
|
||||
})
|
||||
```
|
||||
|
||||
## Policy Decisions
|
||||
|
||||
### Public Package Scope
|
||||
|
||||
Expose a narrow root facade package and keep existing `internal/*` packages internal.
|
||||
|
||||
Reasoning:
|
||||
|
||||
This gives callers the workflow they need without freezing the internal architecture as public API. It also preserves the current package-boundary policy and keeps future refactoring possible.
|
||||
|
||||
### Public Type Strategy
|
||||
|
||||
Define public facade types and map them to internal domain types.
|
||||
|
||||
Reasoning:
|
||||
|
||||
Public types can be designed around caller needs and long-term stability. Internal types can continue to evolve with implementation details such as adapter metadata, validation internals, and provider-specific behavior.
|
||||
|
||||
### CLI And HTTP Reuse
|
||||
|
||||
Keep CLI and HTTP on current internal wiring for the initial library release. Consider migrating them to the public facade only after the facade proves stable.
|
||||
|
||||
Reasoning:
|
||||
|
||||
This minimizes risk to the existing subprocess and HTTP contracts while adding the new API. It also avoids forcing the first public facade to satisfy every adapter edge case immediately.
|
||||
|
||||
### Error Surface
|
||||
|
||||
Expose public sentinel errors or typed error categories and map internal errors to them while preserving wrapped context.
|
||||
|
||||
Reasoning:
|
||||
|
||||
Library callers need stable, idiomatic error checks. Mapping internal errors avoids exposing internal package paths as public compatibility promises.
|
||||
|
||||
## Scope
|
||||
|
||||
In scope:
|
||||
|
||||
- Public facade package for library consumers.
|
||||
- Public request, result, prepared-run, artifact reference, execution override, validation, and config types.
|
||||
- Public constructors for common file and inline input references.
|
||||
- Public engine methods for `Prepare` and `Run`.
|
||||
- Optional dependency injection for LLM behavior and HTTP behavior.
|
||||
- Stable error behavior suitable for `errors.Is` and `errors.As`.
|
||||
- Tests proving public API behavior matches CLI/use-case behavior.
|
||||
- Documentation and examples for library usage after implementation.
|
||||
|
||||
Out of scope for the first library release:
|
||||
|
||||
- Making every `internal/*` package public.
|
||||
- Replacing or rewiring the CLI or HTTP adapters.
|
||||
- Adding a durable run store or workflow engine.
|
||||
- Adding broad provider-specific SDK surfaces.
|
||||
- Adding non-Go language bindings.
|
||||
- Adding global mutable configuration.
|
||||
|
||||
## Acceptance Criteria
|
||||
|
||||
- A Go caller can import the root module and run a prompt without invoking a subprocess.
|
||||
- A Go caller can prepare a prompt without invoking an LLM.
|
||||
- Public library behavior matches current CLI/HTTP use-case semantics for prompt/profile loading, artifact reading, rendering, validation, and model invocation.
|
||||
- Existing CLI and HTTP behavior remains unchanged.
|
||||
- Library tests use injected/fake LLM behavior and do not require real provider credentials.
|
||||
- Public documentation is concise and limited to implemented behavior once code exists.
|
||||
Reference in New Issue
Block a user