Documentation
¶
Overview ¶
Package promptr is the runtime that generated .promptr code calls into: a minimal, provider-agnostic Provider interface plus the parse-with-repair loop that turns a model's loose reply into a typed Go value via the coerce kernel.
The compiler (cmd/promptr) turns each `function` in a .promptr file into a Go function whose body is a single call to Extract — so the generated code stays thin and readable, and all the retry/coercion logic lives here, tested once.
Index ¶
- func Extract[T any](ctx context.Context, p Provider, prompt string, opts Options) (T, error)
- func ExtractStream[T any](ctx context.Context, p Provider, prompt string, opts Options) (<-chan Partial[T], error)
- func ExtractUnion[I any](ctx context.Context, p Provider, prompt string, opts Options, u *Union) (I, error)
- func Render(tmpl string, ctx map[string]any) (string, error)
- func RunTools[T any](ctx context.Context, p Provider, prompt string, tools []Tool, opts Options) (T, error)
- type AfterFunc
- type Call
- type CallInfo
- type CallKind
- type Collector
- type Hook
- type Message
- type Middleware
- type Options
- type Outcome
- type Part
- type PartKind
- type Partial
- type Provider
- type ProviderFunc
- type Registry
- type Reply
- type Stats
- type StreamProvider
- type TemplateError
- type Tool
- type ToolCall
- type ToolDef
- type ToolProvider
- type Union
- type UsageReporter
Examples ¶
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func Extract ¶
Extract runs prompt against the provider and coerces the reply into T. If the reply will not coerce, it re-asks — appending the unparseable reply and the parse error so the model can correct itself — up to Options.Attempts times.
This is the function every generated .promptr function calls.
func ExtractStream ¶ added in v0.4.0
func ExtractStream[T any](ctx context.Context, p Provider, prompt string, opts Options) (<-chan Partial[T], error)
ExtractStream runs prompt against the provider and emits a progressively completed T after each chunk, coercing the accumulated text with the tolerant kernel so callers can render partial state. If the provider implements StreamProvider the reply streams token-by-token; otherwise a single Complete call yields one final Partial. The returned channel closes when the model is done.
This is what a generated `-> stream T` function calls.
func ExtractUnion ¶ added in v0.3.0
func ExtractUnion[I any](ctx context.Context, p Provider, prompt string, opts Options, u *Union) (I, error)
ExtractUnion runs prompt against the provider and classifies the reply into one of the union's variants, returned as the sealed interface I. Like Extract it re-asks on a parse miss, feeding the error back, up to Options.Attempts.
This is what a generated function with a union return type calls.
func Render ¶ added in v0.2.0
Render expands a prompt template against a context map and returns the finished prompt. It is the small, dependency-free template engine the compiler targets: generated functions call Render with the call's parameters (plus a "ctx" entry carrying the baked output-schema) so prompts can use control flow over runtime values.
Supported syntax (all inside {{ }}):
{{ name }} value lookup, with dotted paths: {{ user.name }}
{{ ctx.output_schema }} the compiler-injected schema description
{{ if cond }}…{{ end }} conditional, with optional {{ else }}
{{ for x in items }}…{{ end }} iterate a slice, binding x in the body
A condition is a path (truthy test), `not <path>`, or `<path> == "lit"` / `<path> != "lit"`. Lookups miss softly: an unknown name renders as empty rather than erroring, so a template can never panic on real model context. Render only returns an error for a structurally broken template (an unterminated {{ if }}/{{ for }}).
func RunTools ¶ added in v0.6.0
func RunTools[T any](ctx context.Context, p Provider, prompt string, tools []Tool, opts Options) (T, error)
RunTools runs prompt against a tool-capable provider, executing the Go tools the model asks for and feeding their results back, until the model returns a final answer that coerces into T. The loop is bounded by Options.MaxSteps (default 8).
An unknown tool name or a handler error is fed back to the model as a tool result so it can recover, rather than aborting the run. If the model's final answer will not coerce into T, the parse error is fed back for one repair re-ask (within the step budget), mirroring Extract.
This is what a generated tool-using function calls. The provider must implement ToolProvider; otherwise RunTools returns an error.
Types ¶
type AfterFunc ¶ added in v0.7.0
type AfterFunc func(Outcome)
AfterFunc observes a call's Outcome. A nil AfterFunc is fine — it is skipped.
type Call ¶ added in v0.5.0
type Call struct {
Start time.Time
Duration time.Duration
// PromptTokens / ReplyTokens are exact when the provider reports usage (see
// UsageReporter); otherwise they are a chars/4 estimate.
PromptTokens int
ReplyTokens int
Estimated bool // true when token counts are estimated, not reported
Err error
}
Call is one observed Provider.Complete invocation.
type CallInfo ¶ added in v0.7.0
type CallInfo struct {
Kind CallKind
Messages []Message
Tools []ToolDef // set only when Kind is KindTools
Start time.Time
}
CallInfo describes a provider call as a Hook sees it begin.
type CallKind ¶ added in v0.7.0
type CallKind string
CallKind identifies which provider capability a Hook is observing.
type Collector ¶ added in v0.5.0
type Collector struct {
// contains filtered or unexported fields
}
Collector aggregates per-call latency and token usage across every Complete it wraps. It is safe for concurrent use. Wire it as middleware with Collect.
func (*Collector) Collect ¶ added in v0.5.0
Collect returns a Middleware that records every Complete into c. It captures only the Complete path; to record streaming and tool calls as well, wire the collector with WithHooks(p, c.Hook()) instead.
func (*Collector) CollectTools ¶ added in v0.6.0
func (c *Collector) CollectTools(p ToolProvider) Provider
CollectTools wraps a ToolProvider so each CompleteTools call is recorded into c on the same latency/usage path as Collect. The returned Provider also implements ToolProvider, so it can be passed straight to RunTools while staying observable.
func (*Collector) Hook ¶ added in v0.7.0
Hook adapts a Collector to the Hook seam so it records every Complete, Stream and CompleteTools call when wired via WithHooks — capturing the streaming and tool paths that the older Collect/CollectTools wrappers miss.
type Hook ¶ added in v0.7.0
Hook observes provider calls for cross-cutting concerns — logging, metrics, tracing — without having to implement a Provider. BeforeCall fires just before a call; the AfterFunc it returns (which may be nil) fires when the call returns or, for a stream, when the channel closes. Register hooks with WithHooks, which preserves the wrapped provider's streaming and tool-calling capabilities — the problem a plain Provider-to-Provider Middleware cannot solve.
func LogHook ¶ added in v0.7.0
LogHook returns a Hook that logs each call to logger (slog.Default when nil): a debug line before the call and an info/error line after, with kind, latency and token counts. It is the zero-dependency reference Hook; an OpenTelemetry span hook is a handful of lines following the same shape.
type Message ¶
type Message struct {
Role string
Content string
// Parts, when non-empty, carries a multimodal message (text + images/etc).
// Providers that support multimodal input map Parts to their content array;
// otherwise Content is used. Text-only callers leave Parts nil.
Parts []Part
// ToolCalls, on an "assistant" turn, are the tool invocations the model
// requested (see ToolProvider). ToolCallID, on a "tool" turn, correlates a
// tool result back to the call that produced it. Both are zero for ordinary
// text turns, so non-tool callers are unaffected.
ToolCalls []ToolCall
ToolCallID string
}
Message is one turn in a chat-style exchange. Roles follow the usual "system" / "user" / "assistant" convention; a Provider maps them to whatever its backend expects.
type Middleware ¶ added in v0.5.0
Middleware wraps a Provider to add cross-cutting behaviour — logging, metrics, caching, rate limiting — without touching the generated code. A Middleware is just a Provider-to-Provider function, so they compose by nesting and a wrapped provider is still a plain Provider the runtime can call.
Middleware only intercepts Complete: the wrapped value does not re-expose StreamProvider or ToolProvider, so wrapping a streaming or tool-calling provider hides those capabilities. To observe streaming and tool calls too, prefer WithHooks (hooks.go), which is capability-preserving.
type Options ¶
type Options struct {
// Attempts is the maximum number of model calls (default 2): the first
// try plus repair re-asks when the reply will not coerce into T.
Attempts int
// System, when non-empty, is prepended as a system message.
System string
// UserParts, when non-empty, makes the user turn multimodal: the rendered
// prompt becomes the leading text Part, followed by these (images, files…).
UserParts []Part
// MaxSteps bounds the tool-calling agent loop (default 8): the most
// model⇄tool round-trips RunTools will take before giving up. Ignored by the
// non-tool Extract paths.
MaxSteps int
}
Options tunes an Extract call.
type Outcome ¶ added in v0.7.0
type Outcome struct {
// Text is the final text reply (KindComplete/KindStream, or KindTools when
// the model answered without requesting a tool). For a stream it is the full
// accumulated text once the channel closes.
Text string
// Calls is the tool calls the model requested (KindTools only).
Calls []ToolCall
// PromptTokens/ReplyTokens are exact when the provider implements
// UsageReporter; otherwise they are a chars/4 estimate and Estimated is true.
PromptTokens int
ReplyTokens int
Estimated bool
Duration time.Duration
Err error
}
Outcome describes how a provider call ended. A Hook's AfterFunc receives it.
type Part ¶ added in v0.4.0
type Part struct {
Kind PartKind
Text string // PartText
MIME string // media: e.g. "image/png", "application/pdf"
Data []byte // inline media bytes (base64-encoded by the provider)
URL string // or a reference to remote media (used when Data is empty)
}
Part is one piece of a multimodal message: text, an image, audio, or a file. A media Part carries either inline Data (+MIME) or a URL reference. Providers map Parts to their own content-array formats; a provider that does not support a Part kind may ignore or reject it.
func FilePart ¶ added in v0.4.0
FilePart builds an inline document Part (e.g. a PDF) from bytes and a MIME.
func ImagePart ¶ added in v0.4.0
ImagePart builds an inline image Part from raw bytes and a MIME type.
type Partial ¶ added in v0.4.0
Partial carries a best-effort value parsed from an as-yet-incomplete stream. Complete flips to true once the payload parses cleanly. It mirrors coerce.Partial so generated code only imports promptr.
type Provider ¶
Provider is the single seam between promptr and a language model. Implement it with net/http against any chat API — the core imports no vendor SDK. A deterministic fake lives in providers/fake for tests and the playground.
func Chain ¶ added in v0.5.0
func Chain(p Provider, mws ...Middleware) Provider
Chain applies middlewares to p, outermost first: Chain(p, a, b) yields a(b(p)), so a sees each call before b does.
func Fallback ¶ added in v0.2.0
Fallback wraps providers so Complete tries each in order, returning the first success; if all fail, the last error is returned. Use it to fail over from a primary model to a backup.
func Retry ¶ added in v0.2.0
Retry wraps p so that Complete is retried up to attempts times on error, sleeping backoff between tries (respecting context cancellation). attempts < 1 is treated as 1. This is reliability against transient failures, distinct from Extract's parse-repair loop (which re-asks on unparseable but successful replies).
func RoundRobin ¶ added in v0.2.0
RoundRobin wraps providers so each Complete call goes to the next provider in rotation — load-spreading across keys or endpoints. It is safe for concurrent use.
func WithHooks ¶ added in v0.7.0
WithHooks wraps p so every Complete, Stream and CompleteTools call fires the given hooks, and returns a provider that still satisfies StreamProvider and ToolProvider exactly when p does. This is the capability-preserving seam for observability: unlike Chain (which only wraps Complete), WithHooks keeps a streaming/tool provider usable for streaming and tool calls. With no hooks it returns p unchanged.
Example ¶
package main
import (
"context"
"fmt"
"github.com/zkrebbekx/promptr"
)
// spanHook is the shape an OpenTelemetry exporter would take: open a span in
// BeforeCall, close it in the returned AfterFunc. Here it just prints, to keep
// the example dependency-free and deterministic.
type spanHook struct{}
func (spanHook) BeforeCall(_ context.Context, info promptr.CallInfo) promptr.AfterFunc {
fmt.Printf("start %s\n", info.Kind)
return func(o promptr.Outcome) {
fmt.Printf("end %s err=%v\n", info.Kind, o.Err)
}
}
func main() {
// Any Provider; here a trivial inline one.
base := promptr.ProviderFunc(func(context.Context, []promptr.Message) (string, error) {
return "ok", nil
})
p := promptr.WithHooks(base, spanHook{})
_, _ = p.Complete(context.Background(), nil)
}
Output: start complete end complete err=<nil>
type ProviderFunc ¶ added in v0.7.0
ProviderFunc adapts a plain function to the Provider interface, for inline or test providers and for middleware that build wrappers without a named type.
type Registry ¶ added in v0.2.0
Registry maps the client names declared in a .promptr file to live Providers the caller has wired up. Generated client constructors resolve their provider references through a Registry, so the DSL stays free of credentials.
type Reply ¶ added in v0.6.0
Reply is one turn from a ToolProvider: either a final answer in Text, or one or more tool Calls the model wants run before it can answer. When Calls is non-empty the loop dispatches them and asks again; otherwise Text is final.
type Stats ¶ added in v0.5.0
type Stats struct {
Calls int
Errors int
PromptTokens int
ReplyTokens int
TotalLatency time.Duration
}
Stats is a rollup of a Collector's recorded calls.
func (Stats) AvgLatency ¶ added in v0.5.0
AvgLatency is the mean Complete duration, or zero when no calls were recorded.
func (Stats) TotalTokens ¶ added in v0.5.0
TotalTokens is the sum of prompt and reply tokens across all calls.
type StreamProvider ¶ added in v0.4.0
type StreamProvider interface {
Stream(ctx context.Context, messages []Message) (<-chan string, error)
}
StreamProvider is an optional capability a Provider may implement to deliver a reply incrementally. Stream returns a channel of text chunks (e.g. server-sent token deltas); the channel closes when the model is done. A Provider that does not implement this is still usable for streaming extraction — ExtractStream falls back to a single Complete call.
type TemplateError ¶ added in v0.2.0
type TemplateError struct{ Msg string }
TemplateError reports a structurally invalid template.
func (*TemplateError) Error ¶ added in v0.2.0
func (e *TemplateError) Error() string
type Tool ¶ added in v0.6.0
type Tool struct {
Def ToolDef
Invoke func(ctx context.Context, argsJSON string) (resultJSON string, err error)
}
Tool binds a ToolDef to its Go implementation. Invoke receives the model's raw JSON arguments and returns the result as JSON to feed back into the conversation. Generated code builds these by wrapping a typed handler (decode args → call handler → marshal result); you can also construct them by hand.
type ToolCall ¶ added in v0.6.0
ToolCall is the model's request to invoke a tool: an opaque ID the provider uses to correlate the result, the tool Name, and the raw JSON Arguments. The agent loop decodes Arguments tolerantly via the coerce kernel.
type ToolDef ¶ added in v0.6.0
type ToolDef struct {
Name string
Description string
Params string // schema description of the JSON argument object
}
ToolDef describes a tool offered to the model: its name, a one-line description, and a human-readable schema of its argument object (built the same way output schemas are, so the model sees one consistent style). A ToolProvider marshals these into whatever its backend's tool/function-calling API expects.
type ToolProvider ¶ added in v0.6.0
type ToolProvider interface {
CompleteTools(ctx context.Context, messages []Message, tools []ToolDef) (Reply, error)
}
ToolProvider is an optional capability a Provider may implement to support tool/function-calling. CompleteTools sends the conversation along with the available tool definitions and returns either the model's final text or the tool calls it wants executed. A Provider that does not implement this cannot run tool functions — RunTools returns a clear error.
type Union ¶ added in v0.3.0
Union is a resolver over several candidate variant types — the building block generated code uses for a function that returns a union. It is an alias for coerce.Union so generated code only needs to import promptr.
type UsageReporter ¶ added in v0.5.0
type UsageReporter interface {
LastUsage() (prompt, reply int)
}
UsageReporter is an optional interface a Provider may implement to expose the exact token counts of its most recent Complete call. The Collector uses it when present and falls back to a chars/4 estimate otherwise. It is consulted immediately after Complete returns, so an implementation should report the usage of that call.
Source Files
¶
Directories
¶
| Path | Synopsis |
|---|---|
|
cmd
|
|
|
promptr
command
Command promptr compiles .promptr schema files into Go.
|
Command promptr compiles .promptr schema files into Go. |
|
promptr-lsp
command
Command promptr-lsp is a minimal Language Server for .promptr files.
|
Command promptr-lsp is a minimal Language Server for .promptr files. |
|
Package codegen turns a parsed .promptr File into idiomatic Go source.
|
Package codegen turns a parsed .promptr File into idiomatic Go source. |
|
Package coerce is a schema-aligned parser: it turns the loose, near-JSON text that language models actually emit into typed Go values.
|
Package coerce is a schema-aligned parser: it turns the loose, near-JSON text that language models actually emit into typed Go values. |
|
Package dsl lexes and parses the .promptr schema language into an AST.
|
Package dsl lexes and parses the .promptr schema language into an AST. |
|
examples
|
|
|
agent
Package agent is a promptr v0.6 example: a tool-using function that hands the model two Go-backed tools and runs the model→tool→model agent loop, returning a typed Itinerary.
|
Package agent is a promptr v0.6 example: a tool-using function that hands the model two Go-backed tools and runs the model→tool→model agent loop, returning a typed Itinerary. |
|
route
Package route is a promptr v0.3 example: a union return type (Search | Escalate), @description/@alias field attributes, and a map field, compiled to Go (route.promptr.go) and exercised against the fake provider in route_test.go.
|
Package route is a promptr v0.3 example: a union return type (Search | Escalate), @description/@alias field attributes, and a map field, compiled to Go (route.promptr.go) and exercised against the fake provider in route_test.go. |
|
stream
Package stream is a promptr v0.4 example: a streaming function (-> stream T) that yields progressively-completed partial values, plus a multimodal function taking an image input.
|
Package stream is a promptr v0.4 example: a streaming function (-> stream T) that yields progressively-completed partial values, plus a multimodal function taking an image input. |
|
ticket
Package ticket is an end-to-end promptr example: a .promptr schema compiled to Go (ticket.promptr.go) and exercised against the fake provider in ticket_test.go.
|
Package ticket is an end-to-end promptr example: a .promptr schema compiled to Go (ticket.promptr.go) and exercised against the fake provider in ticket_test.go. |
|
providers
|
|
|
anthropic
Package anthropic is a promptr.Provider backed by the Anthropic Messages API, built on net/http alone — no vendor SDK, in keeping with promptr's zero-SDK core.
|
Package anthropic is a promptr.Provider backed by the Anthropic Messages API, built on net/http alone — no vendor SDK, in keeping with promptr's zero-SDK core. |
|
fake
Package fake is a deterministic promptr.Provider for tests, examples and the playground — no network, no API key.
|
Package fake is a deterministic promptr.Provider for tests, examples and the playground — no network, no API key. |
|
gemini
Package gemini is a promptr.Provider for Google's Generative Language API (Gemini), built on net/http alone — no vendor SDK.
|
Package gemini is a promptr.Provider for Google's Generative Language API (Gemini), built on net/http alone — no vendor SDK. |
|
ollama
Package ollama is a promptr.Provider for a local Ollama server, built on net/http alone — no vendor SDK.
|
Package ollama is a promptr.Provider for a local Ollama server, built on net/http alone — no vendor SDK. |
|
openai
Package openai is a promptr.Provider for any OpenAI Chat Completions-compatible API, built on net/http alone — no vendor SDK.
|
Package openai is a promptr.Provider for any OpenAI Chat Completions-compatible API, built on net/http alone — no vendor SDK. |
|
recorded
Package recorded is a promptr.Provider that replays hand-authored JSON cassettes instead of calling a real model — a VCR for deterministic tests and offline demos.
|
Package recorded is a promptr.Provider that replays hand-authored JSON cassettes instead of calling a real model — a VCR for deterministic tests and offline demos. |