135 lines
5.2 KiB
Markdown
135 lines
5.2 KiB
Markdown
# 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.
|