Documentation
¶
Overview ¶
Package transform is mdedit's pluggable LLM text transformations: a prompt corpus (embedded markdown documents, one per transformation) and the one call that runs a prompt over a piece of the buffer.
It is a SIBLING package rather than part of mdedit for the reason ADR-0120 §SD2 records for play's Ask panel: the app package itself never imports the LLM client, so the network dependency is one seam wide, and the capability gate (capslock) attributes the egress to this package rather than to everything mdedit touches.
A transformation is a markdown document: YAML frontmatter naming it, and a body that IS the system prompt — a prompt is prose, and unlike an applet's SQL it has no surrounding commentary to fence it off from. Discovery is the applet-book shape (sqlapplet.RegisterBook): packages contribute an fs.FS of prompt docs at init, the filesystem is the index, and the filename base is the slug. The in-tree corpus is gated by a test that parses every embedded document; a third-party book that fails to parse logs and skips rather than breaking the bar, because the registry is open by design.
Nothing here touches imzero2 — every function is callable from a worker goroutine, which is where mdedit runs Run (behind a bgjob.Runner).
Index ¶
Constants ¶
This section is empty.
Variables ¶
var ( // Endpoint gates the whole feature: unset (or an unset Model) means the // transform surface is absent from the bar. Endpoint = env.NewString(env.Spec{ Name: "BOXER_MDEDIT_LLM_ENDPOINT", Description: "OpenAI-compatible chat-completions base URL for mdedit's text transformations (e.g. http://localhost:1234/v1); unset hides the transform surface entirely", Category: env.CategoryE("boxer-mdedit"), }) // Model has no default on purpose: a wrong default is worse than a // refusal, and the endpoint knows its own models. Model = env.NewString(env.Spec{ Name: "BOXER_MDEDIT_LLM_MODEL", Description: "model id for mdedit's text transformations; unset hides the transform surface even with the endpoint set", Category: env.CategoryE("boxer-mdedit"), }) // ApiKey is its own variable rather than a fallback chain over // LLM_API_KEY (a commitdigest CLI flag alias, not a registered spec) or // GEMINI_API_KEY (provider-specific): a sensitive value should have // exactly one name per consumer. Empty is valid — local endpoints // (LM Studio, llama.cpp, Ollama) take no key. ApiKey = env.NewString(env.Spec{ Name: "BOXER_MDEDIT_LLM_APIKEY", Description: "API key sent to the transformation endpoint; empty for local endpoints that take none", Category: env.CategoryE("boxer-mdedit"), Sensitive: true, }) // MaxTokens must cover reasoning AND answer on models that think inline. MaxTokens = env.NewInt(env.Spec{ Name: "BOXER_MDEDIT_LLM_MAXTOKENS", Default: "4096", Description: "completion token ceiling per transformation run; a prompt doc's own max-tokens frontmatter wins", Category: env.CategoryE("boxer-mdedit"), }) // Timeout is generous because local models are slow; the run is // cancellable from the progress row throughout. Timeout = env.NewDuration(env.Spec{ Name: "BOXER_MDEDIT_LLM_TIMEOUT", Default: "120s", Description: "wall-clock bound on one transformation run; the client has no timeout of its own", Category: env.CategoryE("boxer-mdedit"), }) )
Functions ¶
func EndpointHost ¶
EndpointHost is the endpoint reduced to its host, for the egress-visibility label the UI shows beside the picker (the ADR-0120 §SD3 reasoning: where text goes should be readable where it is sent from).
func FailureLine ¶
FailureLine maps a Run error onto the one-line explanation the result pane leads with; the wrapped detail stays underneath for whoever wants it.
func NewClient ¶
func NewClient(cfg Config) (client openaichat.ClientI, err error)
NewClient builds the one client mdedit uses. No retry policy on purpose: this backs an interactive gesture, and a failed attempt should surface as a line the reader can act on rather than as half a minute of silent backoff — the re-click IS the retry.
func RegisterPromptBook ¶
RegisterPromptBook contributes a prompt corpus: an fs.FS whose *.md files each define one transformation. Packages call it from init (the sqlapplet.RegisterBook shape); mdedit enumerates everything registered the first time its transform surface renders. The id names the book in diagnostics and must be unique.
Types ¶
type Config ¶
type Config struct {
Endpoint string // OpenAI-compatible base URL, e.g. http://localhost:1234/v1
Model string
ApiKey string // empty is valid — local endpoints take no key
// MaxTokens is the per-run default; a PromptDef's own value wins.
MaxTokens int32
// Timeout bounds one Complete call. The client has no timeout of its own
// — the context is the only clock.
Timeout time.Duration
}
Config is the endpoint the transformations run against, resolved from the ADR-0009 env registry (transform_env.go).
func ConfigFromEnv ¶
ConfigFromEnv resolves the config. enabled is the registration gate: endpoint AND model must both be set — a wrong default model is worse than a refusal, so neither has one.
type PromptDef ¶
type PromptDef struct {
// BookId is the corpus the definition came from and Slug the filename
// base — together the definition's identity, and durably public the way
// an applet slug is.
BookId string
Slug string
// Title and Summary are the picker's line and its tooltip; both required.
// Icon is optional and conventionally an emoji.
Title string
Summary string
Icon string
Scope ScopeE
// Temperature overrides the provider default when non-nil; MaxTokens
// overrides Config.MaxTokens when non-zero.
Temperature *float32
MaxTokens int32
// System is the whole document body after the frontmatter — the system
// prompt, verbatim.
System string
}
PromptDef is one parsed transformation.
func All ¶
All parses every registered book and returns the definitions sorted by (book, slug). Errors are per-document and returned beside the definitions that did parse: the in-tree corpus is test-gated to zero errors, and a contributed book that fails should cost its own entries, not the bar.
type Result ¶
type Result struct {
Content string
Elapsed time.Duration
InputTokens int32
OutputTokens int32
// Truncated marks a completion that hit the token ceiling with content
// already produced. Shown, not hidden: the reader sees exactly what they
// would apply and decides.
Truncated bool
}
Result is one completed run.
func Run ¶
func Run(ctx context.Context, client openaichat.ClientI, cfg Config, def PromptDef, input string) (res Result, err error)
Run is one prompt over one input: a system+user round-trip (the commitdigest summarizeOnce shape). Blocks until the provider answers, the timeout ends it, or ctx is cancelled — never call it on the render goroutine.
type ScopeE ¶
type ScopeE uint8
ScopeE is what a transformation wants as its input.
const ( // ScopeSelection runs over the selection when there is one and falls back // to the whole document — the default, and what an "improve this" prompt // wants. ScopeSelection ScopeE = iota // ScopeDocument always runs over the whole document, selection or not — // what a summary wants. ScopeDocument )