179 lines
8.4 KiB
Markdown
179 lines
8.4 KiB
Markdown
# Architecture Policy
|
|
|
|
## Purpose
|
|
|
|
This document defines Promptkit's current high-level architecture and the
|
|
durable boundaries that implementation changes must preserve. The
|
|
[internal component overview](../internal/overview.md) inventories concrete
|
|
implemented packages without redefining these rules.
|
|
|
|
## System Shape
|
|
|
|
Promptkit is an importable Go library. It does not ship a command, an HTTP
|
|
service, or another application process. Repository examples demonstrate
|
|
library use but are not Promptkit applications or release artifacts.
|
|
|
|
The module root contains package `promptkit`, which is the public facade. It
|
|
provides the supported engine, configuration and source options, requests,
|
|
results, public values, extension interfaces, profiles, and error sentinels.
|
|
|
|
The implemented internal components consist of:
|
|
|
|
- `internal/domain`, which owns framework data values and source-neutral
|
|
invariants shared by later internal components;
|
|
- `internal/backend`, which owns validated immutable OpenAI-compatible backend
|
|
definitions and the built-in OpenRouter definition;
|
|
- `internal/capacity`, which owns engine-local bounded run admission and
|
|
model-generation scheduling for limited backends;
|
|
- `internal/defaults`, which owns application-neutral framework defaults and
|
|
constructs the default execution target;
|
|
- `internal/filecatalog`, which discovers YAML files and provides source-path
|
|
helpers for filesystem and `fs.FS` consumers;
|
|
- `internal/jsonvalue`, which validates and defensively copies JSON-compatible
|
|
extra-parameter trees;
|
|
- `internal/promptdef`, which loads and validates prompt definitions from
|
|
filesystem and `fs.FS` sources;
|
|
- `internal/profile`, which loads, validates, and overlays execution profiles
|
|
from filesystem and `fs.FS` sources;
|
|
- `internal/profile/builtin`, which embeds the built-in execution profile
|
|
catalog;
|
|
- `internal/prompt`, which renders prompt messages from Go templates;
|
|
- `internal/artifact`, which resolves ordinary inline and unrestricted
|
|
caller-selected file references;
|
|
- `internal/validate`, which validates basic, JSON, and JSON Schema output
|
|
using filesystem and `fs.FS` schema sources;
|
|
- `internal/llm`, which defines the provider-neutral generation boundary and
|
|
implements outbound OpenAI-compatible chat requests; and
|
|
- `internal/usecase`, which coordinates preparation and execution across the
|
|
internal framework components.
|
|
|
|
The `examples/go-library/prepare` and `examples/go-library/run` packages are
|
|
maintained downstream consumers of the root facade. They do not expose library
|
|
packages or participate in internal assembly.
|
|
|
|
The root facade assembles one immutable backend registry, one capacity manager,
|
|
the internal repositories, renderer, validator, outbound client, and use-case
|
|
runner while translating public values and errors at the library boundary. The
|
|
registry contains built-ins plus validated engine-scoped consumer additions.
|
|
The facade constructs the capacity manager from the registry's immutable
|
|
policy snapshot, wraps the selected built-in or injected model client, and
|
|
supplies bounded admission to the runner. The defaults and renderer depend on
|
|
the domain model. Prompt-definition and profile repositories use the domain
|
|
model, file catalog, and YAML decoder. The built-in profile repository supplies
|
|
an embedded `fs.FS` to the profile package. Artifact reading uses the domain
|
|
model and application-neutral defaults. Validation uses the domain model, file
|
|
catalog, and JSON Schema implementation. The model client uses the domain
|
|
model, application-neutral defaults, and an injected or standard-library HTTP
|
|
client. The use-case runner depends on the narrow interfaces owned by each
|
|
internal component, including backend lookup and run admission.
|
|
|
|
The current implementation follows this dependency direction:
|
|
|
|
```text
|
|
downstream consumers, including Scriptorium
|
|
|
|
|
v
|
|
root promptkit public facade
|
|
|
|
|
v
|
|
internal framework components
|
|
|
|
|
v
|
|
narrow injected abstractions
|
|
```
|
|
|
|
The backend registry depends on the domain model and shared JSON-value
|
|
validation, has no mutation API after construction, and consumes the
|
|
OpenAI-compatible reserved request-field rule owned by the model client. The
|
|
capacity component depends on the domain model and the narrow internal
|
|
model-client boundary, not on provider transport implementation. The model
|
|
client does not depend on registry or capacity configuration. The facade
|
|
coordinates internal components and adapts the supported public extension
|
|
interfaces to narrow internal abstractions. Internal components must not depend
|
|
on consumers or on Scriptorium.
|
|
|
|
`internal/domain` owns source-neutral invariants for values shared across
|
|
multiple input and execution boundaries, including execution-setting bounds,
|
|
session identifiers, and output-contract legality. Callers retain source
|
|
parsing, required-field rules, source-specific normalization, defaulting,
|
|
error classification, and other policy specific to their own boundary.
|
|
|
|
## Repository And Consumer Boundary
|
|
|
|
Scriptorium is a downstream application that consumes Promptkit through
|
|
the supported public facade. It is not a Promptkit package and must not become
|
|
an internal dependency.
|
|
|
|
Promptkit owns reusable, application-neutral library behavior. It does not own:
|
|
|
|
- binaries or executable packaging;
|
|
- CLI commands, parsing, streams, or exit codes;
|
|
- HTTP routes, servers, request DTOs, status mapping, or deployment policy;
|
|
- application configuration discovery or precedence;
|
|
- process lifecycle, operational state, or application logging; or
|
|
- consumer-specific filesystem or security policy.
|
|
|
|
Those concerns remain with Scriptorium or another consuming application.
|
|
|
|
## Package Ownership
|
|
|
|
The module root is the supported public facade. Framework implementation
|
|
packages belong under Go's `internal/` boundary unless a demonstrated, stable
|
|
consumer contract requires a public package.
|
|
|
|
Each package must have one cohesive responsibility and a clear dependency
|
|
direction. Internal packages must not expose their types merely to simplify
|
|
wiring, and the public facade must not leak internal representations through
|
|
exported signatures. New public packages require a durable consumer need that
|
|
cannot be served cleanly by the root facade.
|
|
|
|
The [internal component overview](../internal/overview.md) must be updated as
|
|
packages are implemented or their responsibilities change.
|
|
|
|
## Exported API Discipline
|
|
|
|
Export the smallest contract required by real consumers. Exported declarations
|
|
must have accurate GoDoc, stable semantics, and tests proportionate to their
|
|
compatibility risk. Avoid speculative extension points, aliases for internal
|
|
types, and public constructors that expose assembly details.
|
|
|
|
Once an exported API exists, its Go declaration and GoDoc own its exact public
|
|
contract. Architecture documentation owns boundary rules, not a duplicate API
|
|
reference.
|
|
|
|
## Error Boundaries
|
|
|
|
Internal failures must cross the public facade as errors meaningful to a Go
|
|
consumer without exposing private package types or transport-specific policy.
|
|
Wrapping should add useful context while preserving any public error identity
|
|
needed with `errors.Is` or `errors.As`.
|
|
|
|
Promptkit must not assign CLI exit codes or HTTP status codes. Consumers map
|
|
public library outcomes into their own transport behavior.
|
|
|
|
## Dependency Injection
|
|
|
|
External effects and consumer-selected policy must enter through narrow
|
|
interfaces or functions at the boundary that uses them. Dependencies should be
|
|
explicitly supplied during construction or invocation rather than read from
|
|
consumer configuration or hidden process-global state.
|
|
|
|
Interfaces should be owned by the code that consumes the behavior and should
|
|
contain only the operations that code requires. Provide defaults only for
|
|
application-neutral behavior; consumer-specific restrictions and adapters
|
|
remain injected from the consuming project.
|
|
|
|
## Repository Independence
|
|
|
|
Promptkit must build, test, and validate independently of Scriptorium. Do not
|
|
commit `go.work`, `go.work.sum`, or a local filesystem `replace` directive.
|
|
Temporary workspace or replacement configuration may support coordinated local
|
|
development, but it is not part of either repository's architecture or release
|
|
state.
|
|
|
|
## Current-State Maintenance
|
|
|
|
Do not list planned packages as implemented components. When implementation
|
|
introduces a package, update the internal inventory and the owning contract or
|
|
subsystem document in the same change.
|