prompty

package module
v0.6.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Mar 19, 2026 License: MIT Imports: 23 Imported by: 1

README

prompty

Go Reference

TL;DR — prompty is a library for prompt management, templating, and unified interaction with LLMs in Go. It supports loading prompts from files, Git, or HTTP and works with multiple backends (OpenAI, Anthropic, Gemini, Ollama) without locking you to a single vendor.

Modules and installation

The project is split into multiple Go modules. Install only what you need:

Layer Package Install
Core prompty (templates, registries in-tree) go get github.com/skosovsky/prompty
Adapters OpenAI go get github.com/skosovsky/prompty/adapter/openai
Gemini go get github.com/skosovsky/prompty/adapter/gemini
Anthropic go get github.com/skosovsky/prompty/adapter/anthropic
Ollama go get github.com/skosovsky/prompty/adapter/ollama
Registries Git (remote) go get github.com/skosovsky/prompty/remoteregistry/git

fileregistry and embedregistry are part of the core module (github.com/skosovsky/prompty).

Quick Start

Minimal example: create an LLM client from an adapter, format a prompt, call Generate, and read the response text. Requires OPENAI_API_KEY in the environment.

package main

import (
	"context"
	"fmt"
	"log"
	"os"

	"github.com/openai/openai-go/v3"
	"github.com/openai/openai-go/v3/option"
	"github.com/skosovsky/prompty"
	"github.com/skosovsky/prompty/adapter"
	openaiadapter "github.com/skosovsky/prompty/adapter/openai"
)

func main() {
	adp := openaiadapter.New(openaiadapter.WithClient(
		openai.NewClient(option.WithAPIKey(os.Getenv("OPENAI_API_KEY")))),
	)
	client := adapter.NewClient(adp)

	exec := prompty.SimpleChat("You are a helpful assistant.", "What is 2+2?")
	resp, err := client.Generate(context.Background(), exec)
	if err != nil {
		log.Fatal(err)
	}
	fmt.Println(resp.Text())
}

Main abstractions

  • Registry — supplies ChatPromptTemplate by id (from files, embed, or remote). Interface: GetTemplate(ctx, id) (*ChatPromptTemplate, error).
  • Adapter — maps PromptExecution to a provider request and parses the response. Recommended: adapter.NewClient(providerAdapter)client.Generate(ctx, exec)resp.Text(). Low-level: TranslateExecuteParseResponse. For streaming use GenerateStream; adapters implement StreamerAdapter.ExecuteStream for native streaming.
  • TemplatingChatPromptTemplate is built from message templates and optional tools; you pass a typed payload (struct with prompt tags) to FormatStruct(ctx, payload) to get a PromptExecution. Registries can load manifests (JSON or YAML) and support WithPartials for shared {{ template "name" }} partials. Template functions (funcmaps) include truncate_chars, truncate_tokens, render_tools_as_xml, render_tools_as_json, escapeXML, and randomHex.

Pipeline: RegistryTemplate + payload → PromptExecutionAdapter → provider API. HTTP/transport is the caller’s responsibility.

Features

  • Domain model: ContentPart (text, image, tool call/result), ChatMessage, ToolDefinition, PromptExecution with metadata; open-ended roles in manifests (validation in adapters). Message-level: prompt caching uses ChatMessage.CachePoint (or cache: true per message in YAML); other provider options via ChatMessage.Metadata (e.g. gemini_search_grounding for Gemini).
  • Media: exec.ResolveMedia(ctx, fetcher) fills MediaPart.Data using a Fetcher (e.g. mediafetch.DefaultFetcher{}); use before Translate for adapters that require inline data (Anthropic, Ollama); OpenAI and Gemini accept URL natively.
  • Templating: text/template with fail-fast validation, PartialVariables, optional messages, chat history splicing. DRY: registries support WithPartials(pattern) so manifests can use {{ template "name" }} with shared partials (e.g. _partials/*.tmpl).
  • Template functions: truncate_chars, truncate_tokens, render_tools_as_xml / render_tools_as_json for tool injection.
  • Registries: load manifests from filesystem (fileregistry), embed (embedregistry), or remote HTTP/Git (remoteregistry) with TTL cache.
  • Adapters: map PromptExecution to provider request types (OpenAI, Anthropic, Gemini, Ollama); parse responses back to []ContentPart. Tool result is multimodal: ToolResultPart.Content is []ContentPart (text and/or images). Adapters that do not support media in tool results return ErrUnsupportedContentType when MediaPart is present in ToolResultPart.Content.
  • Observability: PromptMetadata (ID, version, description, tags, environment) on every execution.

Registries

Package Description
github.com/skosovsky/prompty/fileregistry Load manifests (JSON or YAML via WithParser) from a directory; lazy load with cache; Reload() to clear cache; WithPartials(relativePattern) for {{ template "name" }}
github.com/skosovsky/prompty/embedregistry Load from embed.FS at build time; eager load; no mutex; WithPartials(pattern) for shared partials
github.com/skosovsky/prompty/remoteregistry Fetch via Fetcher (HTTP or Git); TTL cache; Evict/EvictAll; Close() for resource cleanup

All three registries also implement optional prompty.Lister (List(ctx)) and prompty.Statter (Stat(ctx, id)). When you have a variable of type prompty.Registry and need to list IDs or get template metadata, use a type assertion: if l, ok := reg.(prompty.Lister); ok { ids, err := l.List(ctx); ... }.

Template name and environment resolve to {name}.{env}.json, {name}.{env}.yaml (or .yml), with fallback to {name}.json, {name}.yaml. Name must not contain ':'.

Adapters

Package Translate result Notes
github.com/skosovsky/prompty/adapter/openai *openai.ChatCompletionNewParams Tools, images (URL/base64), tool calls
github.com/skosovsky/prompty/adapter/anthropic *anthropic.MessageNewParams Images as base64 only
github.com/skosovsky/prompty/adapter/gemini *gemini.Request Model set at call site
github.com/skosovsky/prompty/adapter/ollama *api.ChatRequest Native Ollama tools

Each adapter implements Translate(ctx, exec) (Req, error) where Req is the provider request type; ParseResponse(ctx, raw) returns *prompty.Response; use resp.Text() for plain text. Use prompty.TextFromParts and adapter.ExtractModelConfig for helpers. Tool result: ToolResultPart.Content is []ContentPart (multimodal). Adapters that do not support media in tool results return adapter.ErrUnsupportedContentType when MediaPart is present. Media: OpenAI and Gemini accept image URL in MediaPart natively. For Anthropic and Ollama (base64 only), call exec.ResolveMedia(ctx, fetcher) before Translate when using image URLs; pass a Fetcher (e.g. mediafetch.DefaultFetcher{} or a custom implementation). Otherwise the adapter returns adapter.ErrMediaNotResolved. The core has no HTTP dependency; the default implementation lives in mediafetch.

Architecture

flowchart LR
    Registry[Registry]
    Template[ChatPromptTemplate]
    Format[FormatStruct]
    Exec[PromptExecution]
    Adapter[ProviderAdapter]
    API[LLM API]

    Registry -->|GetTemplate| Template
    Template -->|FormatStruct + payload| Format
    Format --> Exec
    Exec -->|Translate| Adapter
    Adapter -->|request| API
    API -->|raw response| Adapter
    Adapter -->|ParseResponse| Response["*Response"]

Pipeline: RegistryTemplate + typed payload → Fail-fast validationRendering (with tool injection) → PromptExecutionAdapter → LLM API. HTTP/transport is the caller’s responsibility.

Template functions

  • truncate_chars .text 4000 — trim by rune count
  • truncate_tokens .text 2000 — trim by token count (uses TokenCounter from template options; default CharFallbackCounter)
  • render_tools_as_xml .Tools / render_tools_as_json .Tools — inject tool definitions into the prompt (e.g. for local Llama)
  • escapeXML — escape <, >, &, ", ' so user input does not break XML structure (see Prompt Security)
  • randomHex N — cryptographically random hex string of N bytes (2N chars); for randomized delimiters

Prompt Security (Data Isolation)

To prevent prompt injection (e.g. user input closing a <patient_input> tag), escape user content and use randomized delimiters so the model cannot guess them. Example in a message template:

{{ $delim := randomHex 8 }}
<data_{{ $delim }}>
{{ .UserInput | escapeXML }}
</data_{{ $delim }}>
  • escapeXML — uses html.EscapeString; keeps user text from being interpreted as markup.
  • randomHex — e.g. randomHex 8 yields a 16-character hex string; use in opening and closing tags so the delimiter is unpredictable.

See examples/secure_prompt for a runnable example.

Development

This repo uses Go Workspaces (go.work). The root and all adapter/registry submodules must be listed there so that changes to the core prompty package and adapters compile together in one PR without publishing intermediate versions.

Build and test (from repo root):

go work sync
go build ./...
go test ./...
cd adapter/openai && go build . && go test . && cd ../..
cd adapter/anthropic && go build . && go test . && cd ../..
cd adapter/gemini && go build . && go test . && cd ../..
cd adapter/ollama && go build . && go test . && cd ../..
cd remoteregistry/git && go build . && go test . && cd ../..

Ensure go.work includes: ., ./adapter/openai, ./adapter/anthropic, ./adapter/gemini, ./adapter/ollama, ./remoteregistry/git.

Benchmarks: the library is optimized for zero-allocation rendering (sync.Pool). To check allocs/op and B/op (and ensure PRs do not regress them), run:

go test -bench=BenchmarkFormatStruct -benchmem ./...

Running examples locally: go.work includes ./examples/basic_chat, ./examples/git_prompts, ./examples/funcmap_tools, and ./examples/secure_prompt. From the repo root run go run ./examples/basic_chat (or go run ./examples/secure_prompt for the data-isolation example). Or cd into an example dir and go run .. The secure_prompt example embeds its manifest and works from any working directory; it demonstrates both escapeXML and randomHex (randomized delimiters). Each example’s go.mod uses replace for local development; remove those when using a published module.

License

MIT. See LICENSE.

Documentation

Overview

Package prompty provides prompt template management for LLM applications. It supports multimodal content, tool definitions, fail-fast validation, and provider-agnostic rendering via text/template.

Example
package main

import (
	"context"
	"fmt"

	"github.com/skosovsky/prompty"
)

func main() {
	tpl, err := prompty.NewChatPromptTemplate(
		[]prompty.MessageTemplate{
			{Role: prompty.RoleSystem, Content: prompty.TextContent("You are {{ .bot_name }}.")},
			{Role: prompty.RoleUser, Content: prompty.TextContent("{{ .query }}")},
		},
		prompty.WithPartialVariables(map[string]any{"bot_name": "HelperBot"}),
	)
	if err != nil {
		panic(err)
	}
	type Payload struct {
		Query string `prompt:"query"`
	}
	ctx := context.Background()
	exec, err := tpl.FormatStruct(ctx, &Payload{Query: "What is 2+2?"})
	if err != nil {
		panic(err)
	}
	fmt.Println(exec.Messages[0].Content[0].(prompty.TextPart).Text)
	fmt.Println(exec.Messages[1].Content[0].(prompty.TextPart).Text)
}
Output:
You are HelperBot.
What is 2+2?

Index

Examples

Constants

This section is empty.

Variables

View Source
var (
	ErrMissingVariable  = errors.New("prompty: required template variable not provided")
	ErrTemplateRender   = errors.New("prompty: template rendering failed")
	ErrTemplateParse    = errors.New("prompty: template parsing failed")
	ErrInvalidPayload   = errors.New("prompty: payload struct is invalid or missing prompt tags")
	ErrTemplateNotFound = errors.New("prompty: template not found in registry")
	ErrInvalidManifest  = errors.New("prompty: manifest file is malformed")
	ErrReservedVariable = errors.New("prompty: reserved variable name in payload (use a different prompt tag than Tools)")
	// ErrInvalidName indicates template name or env contains invalid characters (e.g. ':', path separators).
	ErrInvalidName = errors.New("prompty: invalid template name")
	// ErrNoParser indicates that a registry was created without a manifest parser (use WithParser when creating the registry).
	ErrNoParser = errors.New("prompty: parser is required but not provided")
	// ErrConflictingDirectives indicates conflicting execution directives (e.g. Tools and ResponseFormat together).
	ErrConflictingDirectives = errors.New("prompty: conflicting directives (e.g. Tools and ResponseFormat cannot be used together)")
)

Sentinel errors for template and registry operations. All use prefix "prompty:" for identification. Callers should use errors.Is/errors.As.

Functions

func ExecuteWithStructuredOutput added in v0.5.0

func ExecuteWithStructuredOutput[T any](
	ctx context.Context,
	invoker Invoker,
	exec *PromptExecution,
) (*T, error)

ExecuteWithStructuredOutput performs a single request to the LLM and parses the response as JSON into type T.

func ExtractSchema added in v0.6.0

func ExtractSchema(v any) map[string]any

ExtractSchema returns a JSON Schema for v using SchemaProvider when available, otherwise falling back to the built-in reflect generator.

On unsupported input, it returns nil.

func GenerateStructured added in v0.6.0

func GenerateStructured[T any](ctx context.Context, invoker Invoker, prompt string, opts ...GenerateOption) (T, error)

GenerateStructured builds a one-message PromptExecution and returns typed structured output.

Example
package main

import (
	"context"
	"fmt"
	"iter"

	"github.com/skosovsky/prompty"
)

type exampleStructuredInvoker struct{}

func (exampleStructuredInvoker) Generate(context.Context, *prompty.PromptExecution) (*prompty.Response, error) {
	return prompty.NewResponse([]prompty.ContentPart{
		prompty.TextPart{Text: `{"name":"Alice"}`},
	}), nil
}

func (exampleStructuredInvoker) GenerateStream(ctx context.Context, exec *prompty.PromptExecution) iter.Seq2[*prompty.ResponseChunk, error] {
	return func(yield func(*prompty.ResponseChunk, error) bool) {
		resp, err := exampleStructuredInvoker{}.Generate(ctx, exec)
		if err != nil {
			yield(nil, err)
			return
		}
		yield(&prompty.ResponseChunk{Content: resp.Content, IsFinished: true}, nil)
	}
}

func main() {
	type Result struct {
		Name string `json:"name"`
	}

	result, err := prompty.GenerateStructured[Result](context.Background(), exampleStructuredInvoker{}, "Extract name")
	if err != nil {
		panic(err)
	}
	fmt.Println(result.Name)
}
Output:
Alice

func GenerateText added in v0.5.6

func GenerateText(ctx context.Context, invoker Invoker, prompt string, opts ...Option) (string, error)

GenerateText is intended for prototyping, scripts, and simple tasks. For production, use prompty-gen and typed facade executions to maintain observability.

func StreamStructuredOutput added in v0.5.6

func StreamStructuredOutput[T any](ctx context.Context, invoker Invoker, exec *PromptExecution) iter.Seq2[T, error]

StreamStructuredOutput streams structured JSON objects from GenerateStream.

func TextFromParts added in v0.5.0

func TextFromParts(parts []ContentPart) string

TextFromParts concatenates all text parts into a single string, ignoring non-text parts.

func ValidateID added in v0.3.0

func ValidateID(id string) error

ValidateID checks that id is a valid io/fs-style path (slash-separated, no extensions). Canonical ID format: slash-only (e.g. "internal/router"). Rejects dotted IDs (Clean Break). Rejects ids containing ':', '\', ".", ".." for cross-platform safety. Use fs.ValidPath for path safety; rejects IDs with file extensions.

func ValidateName

func ValidateName(name, env string) error

ValidateName checks that name and env are safe for use in paths and cache keys. Rejects empty name and names containing '/', '\\', "..", or ':'. Call before registry GetTemplate or path resolution.

func WithRetry added in v0.6.0

func WithRetry[T any](
	ctx context.Context,
	exec *PromptExecution,
	maxRetries int,
	executor func(context.Context, *PromptExecution) (T, error),
) (T, error)

WithRetry replays executor with scratchpad feedback for retryable typed errors.

Types

type CharFallbackCounter

type CharFallbackCounter struct {
	CharsPerToken int
}

CharFallbackCounter estimates tokens as runes/CharsPerToken. Zero value uses 4 chars per token (English average).

func (*CharFallbackCounter) Count

func (c *CharFallbackCounter) Count(text string) (int, error)

Count returns estimated token count: ceil(rune_count / CharsPerToken). If CharsPerToken <= 0, uses 4.

type ChatMessage

type ChatMessage struct {
	Role       Role
	Content    []ContentPart
	CachePoint bool           // When true, adapters may set cache_control / use context caching per provider
	Metadata   map[string]any // Provider-specific flags; adapters read known keys (e.g. gemini_search_grounding)
}

ChatMessage is a single message with role and content parts (supports multimodal). CachePoint hints providers to cache this message (e.g. Anthropic ephemeral). Other provider-specific options go in Metadata.

func NewAssistantMessage added in v0.5.0

func NewAssistantMessage(text string) ChatMessage

NewAssistantMessage creates a single assistant message with text content.

func NewSystemMessage added in v0.5.0

func NewSystemMessage(text string) ChatMessage

NewSystemMessage creates a single system message with text content.

func NewUserMessage added in v0.5.0

func NewUserMessage(text string) ChatMessage

NewUserMessage creates a single user message with text content.

type ChatPromptTemplate

type ChatPromptTemplate struct {
	Messages         []MessageTemplate
	PartialVariables map[string]any
	Tools            []ToolDefinition
	ModelConfig      map[string]any
	Metadata         PromptMetadata
	ResponseFormat   *SchemaDefinition // JSON Schema for structured output (passed to PromptExecution)
	InputSchema      *SchemaDefinition // JSON Schema for template input (prompty-gen, required/partial derivation)
	RequiredVars     []string          // explicit required vars from manifest; merged with template-derived in FormatStruct
	// contains filtered or unexported fields
}

ChatPromptTemplate holds message templates and options for rendering. Use NewChatPromptTemplate to construct; options are applied via ChatTemplateOption. Fields must not be mutated after construction to ensure goroutine safety.

func CloneTemplate

func CloneTemplate(c *ChatPromptTemplate) *ChatPromptTemplate

CloneTemplate returns a copy of the template with cloned slice and map fields. Registries use this so callers cannot mutate the cached template.

func NewChatPromptTemplate

func NewChatPromptTemplate(messages []MessageTemplate, opts ...ChatTemplateOption) (*ChatPromptTemplate, error)

NewChatPromptTemplate builds a template with defensive copies and applies options. Returns ErrTemplateParse if any message content fails to parse.

Example
package main

import (
	"fmt"

	"github.com/skosovsky/prompty"
)

func main() {
	msgs := []prompty.MessageTemplate{
		{Role: prompty.RoleSystem, Content: prompty.TextContent("You are a helpful assistant.")},
		{Role: prompty.RoleUser, Content: prompty.TextContent("Hello, {{ .user_name }}!")},
	}
	tpl, err := prompty.NewChatPromptTemplate(msgs)
	if err != nil {
		panic(err)
	}
	fmt.Println(len(tpl.Messages))
}
Output:
2

func (*ChatPromptTemplate) Format added in v0.5.0

func (c *ChatPromptTemplate) Format(ctx context.Context, vars map[string]any) (*PromptExecution, error)

Format renders the template using the given input map (reflection-free). Same merge and validation as FormatStruct. History is not supported.

func (*ChatPromptTemplate) FormatStruct

func (c *ChatPromptTemplate) FormatStruct(ctx context.Context, payload any) (*PromptExecution, error)

FormatStruct renders the template using payload struct (prompt tags), merges input fields, validates, splices history.

Example
package main

import (
	"context"
	"fmt"

	"github.com/skosovsky/prompty"
)

func main() {
	tpl, _ := prompty.NewChatPromptTemplate([]prompty.MessageTemplate{
		{Role: prompty.RoleSystem, Content: prompty.TextContent("Hello, {{ .name }}!")},
	})
	type Payload struct {
		Name string `prompt:"name"`
	}
	ctx := context.Background()
	exec, err := tpl.FormatStruct(ctx, &Payload{Name: "Alice"})
	if err != nil {
		panic(err)
	}
	text := exec.Messages[0].Content[0].(prompty.TextPart).Text
	fmt.Println(text)
}
Output:
Hello, Alice!

func (*ChatPromptTemplate) ValidateVariables added in v0.3.0

func (c *ChatPromptTemplate) ValidateVariables(data map[string]any) error

ValidateVariables runs a dry-run execute with the given data (same merge as FormatStruct: PartialVariables + data + Tools). Returns an error with role/message index context if any template references a missing or invalid input field.

type ChatTemplateOption

type ChatTemplateOption func(*ChatPromptTemplate)

ChatTemplateOption configures ChatPromptTemplate (functional options pattern).

func WithConfig

func WithConfig(config map[string]any) ChatTemplateOption

WithConfig sets model config (e.g. temperature, max_tokens).

func WithInputSchema added in v0.5.0

func WithInputSchema(schema *SchemaDefinition) ChatTemplateOption

WithInputSchema sets the JSON Schema for template input (used by prompty-gen, FormatStruct required/partial).

func WithMetadata

func WithMetadata(meta PromptMetadata) ChatTemplateOption

WithMetadata sets prompt metadata for observability.

func WithPartialVariables

func WithPartialVariables(vars map[string]any) ChatTemplateOption

WithPartialVariables sets default input values merged with payload (payload overrides).

func WithPartialsFS added in v0.3.0

func WithPartialsFS(fsys fs.FS, pattern string) ChatTemplateOption

WithPartialsFS sets an fs.FS and pattern for partials (e.g. embed FS and "partials/*.tmpl"); enables {{ template "name" }}.

func WithPartialsGlob added in v0.3.0

func WithPartialsGlob(glob string) ChatTemplateOption

WithPartialsGlob sets a glob pattern (e.g. "_partials/*.tmpl") to parse before message templates; enables {{ template "name" }}.

func WithRequiredVars

func WithRequiredVars(vars []string) ChatTemplateOption

WithRequiredVars sets explicit required input field names (e.g. from manifest input_schema.required). Merged with input fields inferred from template content in FormatStruct.

func WithResponseFormat added in v0.2.0

func WithResponseFormat(schema *SchemaDefinition) ChatTemplateOption

WithResponseFormat sets the JSON Schema for structured response format (used by OpenAI, Gemini).

func WithTokenCounter

func WithTokenCounter(tc TokenCounter) ChatTemplateOption

WithTokenCounter sets the token counter for truncate_tokens in templates.

func WithTools

func WithTools(tools []ToolDefinition) ChatTemplateOption

WithTools sets tool definitions available in templates as .Tools.

Example
package main

import (
	"fmt"

	"github.com/skosovsky/prompty"
)

func main() {
	tpl, _ := prompty.NewChatPromptTemplate(
		[]prompty.MessageTemplate{
			{Role: prompty.RoleSystem, Content: prompty.TextContent("Tools: {{ render_tools_as_json .Tools }}")},
		},
		prompty.WithTools([]prompty.ToolDefinition{
			{Name: "get_weather", Description: "Get weather", Parameters: nil},
		}),
	)
	fmt.Println(len(tpl.Tools))
}
Output:
1

type ContentPart

type ContentPart interface {
	// contains filtered or unexported methods
}

ContentPart is a sealed interface for message parts. Only package types implement it via isContentPart().

Contract: All ProviderAdapter implementations of ParseResponse MUST return a []ContentPart slice containing only value types (e.g. TextPart, not *TextPart). Consumers can rely on this and need no defensive checks for pointer vs value.

type Fetcher added in v0.3.0

type Fetcher interface {
	Fetch(ctx context.Context, url string) (data []byte, mimeType string, err error)
}

Fetcher defines how media URLs are resolved into raw bytes. Callers can use mediafetch.DefaultFetcher or provide a custom implementation (e.g. S3, local files).

type GenerateOption added in v0.6.0

type GenerateOption func(*generateStructuredConfig)

GenerateOption configures GenerateStructured.

func WithRetries added in v0.6.0

func WithRetries(n int) GenerateOption

WithRetries enables retry orchestration for GenerateStructured.

type Invoker added in v0.5.0

type Invoker interface {
	Generate(ctx context.Context, exec *PromptExecution) (*Response, error)
	GenerateStream(ctx context.Context, exec *PromptExecution) iter.Seq2[*ResponseChunk, error]
}

Invoker defines the minimal contract for an LLM client (sync + stream).

func Chain added in v0.5.0

func Chain(base Invoker, middlewares ...Middleware) Invoker

Chain combines multiple middlewares around the base Invoker. The leftmost middleware in the chain executes first.

type LLMClient added in v0.5.0

type LLMClient = Invoker

LLMClient is the public client interface. At the code level it is equivalent to Invoker but separated by meaning.

type Lister added in v0.4.0

type Lister interface {
	List(ctx context.Context) ([]string, error)
}

Lister is optional. When implemented by a registry, List returns available template ids.

type MediaPart added in v0.2.0

type MediaPart struct {
	MediaType string // "image", "audio", "video", "document"
	MIMEType  string // e.g. "application/pdf", "image/jpeg"
	URL       string // Optional: link (adapters may fetch and convert to inline)
	Data      []byte // Optional: raw bytes (base64 is decoded by adapters as needed)
}

MediaPart holds universal media (image, audio, video, document). URL or Data may be set. Adapters that do not accept URL natively may download the URL in Translate(ctx) and send inline data.

type MessageTemplate

type MessageTemplate struct {
	Role       Role           // RoleSystem, RoleUser, RoleAssistant (and others; see Role* constants)
	Content    []TemplatePart // Parts to render (text and/or image_url); each part is a Go text/template
	Optional   bool           // true → skip if all referenced variables are zero-value
	CachePoint bool           // When true, request caching for this message where supported
	Metadata   map[string]any `yaml:"metadata,omitempty"`
}

MessageTemplate is the raw template for one message before rendering. After FormatStruct it becomes a ChatMessage with substituted values. Optional: true skips the message if all referenced variables are zero-value. CachePoint maps from YAML cache: true; adapters use it for prompt caching (e.g. Anthropic ephemeral).

type Middleware added in v0.5.0

type Middleware func(next Invoker) Invoker

Middleware wraps Invoker (LLMClient), allowing cross-cutting behavior for Generate and GenerateStream (logging, metrics, tracing, etc.).

type Option added in v0.5.6

type Option func(*PromptExecution)

Option configures a PromptExecution for convenience helpers.

type PromptExecution

type PromptExecution struct {
	Messages       []ChatMessage
	Tools          []ToolDefinition
	ModelConfig    map[string]any
	Metadata       PromptMetadata
	ResponseFormat *SchemaDefinition `json:"response_format,omitempty" yaml:"response_format,omitempty"`
}

PromptExecution is the result of formatting a template; immutable after creation.

func ExecuteWithToolValidation added in v0.5.6

func ExecuteWithToolValidation(
	ctx context.Context,
	invoker Invoker,
	exec *PromptExecution,
	validator ToolValidator,
) (*PromptExecution, error)

ExecuteWithToolValidation performs one model call and validates tool call arguments.

func NewExecution added in v0.5.0

func NewExecution(messages []ChatMessage) *PromptExecution

NewExecution creates a new prompt execution from a set of messages.

func SimpleChat added in v0.5.0

func SimpleChat(system, user string) *PromptExecution

SimpleChat builds a typical execution with one system instruction and one user message.

func SimplePrompt added in v0.5.0

func SimplePrompt(user string) *PromptExecution

SimplePrompt builds an execution with only one user message.

func (*PromptExecution) AddMessage added in v0.3.0

func (e *PromptExecution) AddMessage(msg ChatMessage) *PromptExecution

AddMessage appends one message. Clones Messages before append; returns e for chaining.

func (*PromptExecution) Clone added in v0.5.6

func (e *PromptExecution) Clone() *PromptExecution

Clone returns a deep copy of the execution and all mutable nested structures.

func (*PromptExecution) Normalize added in v0.4.5

func (e *PromptExecution) Normalize() *PromptExecution

Normalize returns a new PromptExecution with consecutive system/developer messages merged into one. Content is merged: TextPart texts are concatenated with "\n\n"; other parts (e.g. MediaPart) are preserved in order. Call explicitly when history may have produced adjacent system messages (e.g. to avoid provider 400).

func (*PromptExecution) ResolveMedia added in v0.2.1

func (e *PromptExecution) ResolveMedia(ctx context.Context, fetcher Fetcher) error

ResolveMedia fills Data and MIMEType for all MediaParts that have a URL but no Data, using the provided Fetcher. Only "image" media type is supported; other types with URL and empty Data return an error (fail-fast).

func (*PromptExecution) Truncate added in v0.5.6

func (e *PromptExecution) Truncate(maxTokens int, counter TokenCounter) error

Truncate trims the execution history in place until it fits the requested token budget.

func (*PromptExecution) WithHistory added in v0.3.0

func (e *PromptExecution) WithHistory(history []ChatMessage) *PromptExecution

WithHistory appends history messages after system/developer block. Clones Messages before append; returns e for chaining.

type PromptMetadata

type PromptMetadata struct {
	ID          string         `json:"id"`
	Version     string         `json:"version,omitempty"`
	Description string         `json:"description,omitempty"`
	Tags        []string       `json:"tags,omitempty"`
	Environment string         `json:"environment,omitempty"`
	Extras      map[string]any `json:"extras,omitempty"`
}

PromptMetadata holds observability metadata (v2.0 DTO). Known fields: ID, Version, Description, Tags, Environment. Extras holds arbitrary keys from manifest metadata block for tracing/custom middleware.

type PromptRegistry

type PromptRegistry = Registry

PromptRegistry is an alias for Registry for backward compatibility; prefer Registry.

type ReasoningPart added in v0.2.0

type ReasoningPart struct {
	Text string
}

ReasoningPart is the hidden reasoning chain returned by some models (e.g. DeepSeek R1, OpenAI o-series).

type Registry added in v0.3.0

type Registry interface {
	GetTemplate(ctx context.Context, id string) (*ChatPromptTemplate, error)
}

Registry returns a chat prompt template by id. id is a single identifier (e.g. "doctor", "doctor.prod"); environments are expressed via file layout.

type Response added in v0.5.0

type Response struct {
	Content      []ContentPart
	Usage        Usage
	FinishReason string // provider stop reason (e.g. "stop", "length") for telemetry
}

Response is the canonical full model response for sync calls.

func NewResponse added in v0.5.0

func NewResponse(parts []ContentPart) *Response

NewResponse creates a Response from content parts. Usage remains zero.

func (*Response) Text added in v0.5.0

func (r *Response) Text() string

Text concatenates all text parts of the response into a single string. Convenient for simple cases when multimodality is not needed.

type ResponseChunk added in v0.5.0

type ResponseChunk struct {
	Content      []ContentPart
	Usage        Usage
	IsFinished   bool
	FinishReason string // provider stop reason (e.g. "stop", "length") for telemetry
}

ResponseChunk is one chunk of the stream. In streaming providers Usage and FinishReason are typically populated only in the final chunk.

type Role

type Role string

Role is the message role in a chat (system, developer, user, assistant, tool).

const (
	RoleSystem    Role = "system"
	RoleDeveloper Role = "developer" // Replaces system for OpenAI o1/o3-style models
	RoleUser      Role = "user"
	RoleAssistant Role = "assistant"
	RoleTool      Role = "tool"
)

Chat message roles.

type SchemaDefinition added in v0.2.0

type SchemaDefinition struct {
	Name        string         `json:"name,omitempty" yaml:"name,omitempty"`
	Description string         `json:"description,omitempty" yaml:"description,omitempty"`
	Schema      map[string]any `json:"schema" yaml:"schema"` // JSON Schema
}

SchemaDefinition describes a structured output (JSON Schema) for response format.

type SchemaProvider added in v0.5.6

type SchemaProvider interface {
	JSONSchema() map[string]any
}

SchemaProvider allows caller-owned types to provide a JSON Schema without reflection.

type Statter added in v0.4.0

type Statter interface {
	Stat(ctx context.Context, id string) (TemplateInfo, error)
}

Statter is optional. When implemented by a registry, Stat returns template metadata without parsing the body.

type TemplateInfo added in v0.3.0

type TemplateInfo struct {
	ID        string
	Version   string
	UpdatedAt time.Time
}

TemplateInfo holds metadata about a template without parsing its body.

type TemplatePart added in v0.4.5

type TemplatePart struct {
	Type string // "text" or "image_url"
	Text string // Go text/template for type "text"
	URL  string // Go text/template for type "image_url"
}

TemplatePart is one part of a message template (text or image_url). Type determines which field (Text or URL) is the template source.

func TextContent added in v0.4.5

func TextContent(text string) []TemplatePart

TextContent returns a single text TemplatePart slice for convenience.

type TextPart

type TextPart struct {
	Text string
}

TextPart holds plain text content.

type TokenCounter

type TokenCounter interface {
	Count(text string) (int, error)
}

TokenCounter estimates token count for a string. Callers can plug in an exact tokenizer (e.g. tiktoken); default is CharFallbackCounter.

type ToolCallError added in v0.6.0

type ToolCallError struct {
	RawAssistantMessage *ChatMessage
	ToolResults         []ContentPart
	Err                 error
}

ToolCallError is returned when model-generated tool arguments fail validation.

func (*ToolCallError) Error added in v0.6.0

func (e *ToolCallError) Error() string

Error implements error.

func (*ToolCallError) Unwrap added in v0.6.0

func (e *ToolCallError) Unwrap() error

Unwrap returns the wrapped error for errors.Is/errors.As.

type ToolCallPart

type ToolCallPart struct {
	ID        string // Empty for models that do not support ID (e.g. base Gemini)
	Name      string
	Args      string // Full JSON string of arguments (non-stream response)
	ArgsChunk string // Chunk of JSON arguments (streaming); client glues chunks
}

ToolCallPart represents an AI request to call a function (in assistant message). In streaming: ArgsChunk holds incremental JSON; Args is set in non-stream ParseResponse.

type ToolDefinition

type ToolDefinition struct {
	Name        string         `json:"name"`
	Description string         `json:"description"`
	Parameters  map[string]any `json:"parameters,omitempty"` // JSON Schema for parameters
}

ToolDefinition is the universal tool schema. JSON tags are required for template functions (e.g. render_tools_as_json) that marshal tools.

type ToolResultPart

type ToolResultPart struct {
	ToolCallID string
	Name       string
	Content    []ContentPart
	IsError    bool
}

ToolResultPart is the result of a tool call (in message with Role "tool"). Content is a slice of multimodal parts (text, images, etc.).

type ToolValidator added in v0.5.6

type ToolValidator interface {
	ValidateToolCall(name string, argsJSON string) error
}

ToolValidator validates a tool call without coupling prompty to a concrete tool registry.

type Usage added in v0.5.0

type Usage struct {
	PromptTokens     int
	CompletionTokens int
	TotalTokens      int
}

Usage contains token statistics for the model response.

type Validatable added in v0.5.6

type Validatable interface {
	Validate() error
}

Validatable allows caller-owned types to enforce post-unmarshal business rules.

type ValidationError added in v0.6.0

type ValidationError struct {
	RawAssistantMessage *ChatMessage
	FeedbackPrompt      string
	Err                 error
}

ValidationError is returned when model output is invalid JSON or violates semantic validation.

func (*ValidationError) Error added in v0.6.0

func (e *ValidationError) Error() string

Error implements error.

func (*ValidationError) Unwrap added in v0.6.0

func (e *ValidationError) Unwrap() error

Unwrap returns the wrapped error for errors.Is/errors.As.

type VariableError

type VariableError struct {
	Variable string
	Template string
	Err      error
}

VariableError wraps a sentinel error with variable and template context. Use errors.Is(err, ErrMissingVariable) and errors.As(err, &variableErr) to inspect.

func (*VariableError) Error

func (e *VariableError) Error() string

Error implements error.

func (*VariableError) Unwrap

func (e *VariableError) Unwrap() error

Unwrap returns the wrapped error for errors.Is/errors.As.

Directories

Path Synopsis
Package adapter defines the ProviderAdapter interface for mapping prompty's canonical PromptExecution to provider-specific request/response types (e.g.
Package adapter defines the ProviderAdapter interface for mapping prompty's canonical PromptExecution to provider-specific request/response types (e.g.
gemini module
Package embedregistry provides an embed.FS-based prompt registry that loads all YAML manifests at construction (eager).
Package embedregistry provides an embed.FS-based prompt registry that loads all YAML manifests at construction (eager).
Package fileregistry provides a filesystem-based prompt registry that loads YAML manifests on demand (lazy) and caches them.
Package fileregistry provides a filesystem-based prompt registry that loads YAML manifests on demand (lazy) and caches them.
internal
cast
Package cast provides type conversion helpers for map[string]any and similar generic data.
Package cast provides type conversion helpers for map[string]any and similar generic data.
Package manifest parses YAML/JSON prompt manifests into prompty.ChatPromptTemplate.
Package manifest parses YAML/JSON prompt manifests into prompty.ChatPromptTemplate.
Package mediafetch provides safe URL download for adapters that require inline media (e.g.
Package mediafetch provides safe URL download for adapters that require inline media (e.g.
middleware
contextx
Package contextx provides context window management middleware for prompty.
Package contextx provides context window management middleware for prompty.
parser
yaml
Package yaml implements manifest.Unmarshaler for YAML manifests.
Package yaml implements manifest.Unmarshaler for YAML manifests.
Package remoteregistry provides a remote prompt registry that loads YAML manifests via a Fetcher (HTTP or Git).
Package remoteregistry provides a remote prompt registry that loads YAML manifests via a Fetcher (HTTP or Git).

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL