Skip to content
GitHub

Architecture

Internal design of the lexigram-ai-prompt package.


flowchart BT
    Prompt[lexigram-ai-prompt<br/>Template · Registry · Renderer<br/>Optimization · Assembly · Sanitizer]
    LLM[lexigram-ai-llm] & RAG[lexigram-ai-rag] & Agents[lexigram-ai-agents]
    Contracts[lexigram-contracts<br/>PromptTemplateProtocol · PromptAssemblerProtocol<br/>PromptRegistryProtocol · PromptRendererProtocol]

    Prompt -->|implements| Contracts
    LLM & RAG & Agents -->|consumes| Prompt

The prompt management layer sits between contracts (interfaces in lexigram-contracts) and LLM/RAG/Agent consumers. It provides template authoring, variable validation, multi-format rendering, versioned storage, automated optimization, and provider-aware assembly with cache annotation.


Five implementations at lexigram/ai/prompt/template/, all extending AbstractPromptTemplate:

TemplateOutputUse Case
StringPromptTemplatestrSingle-message prompts with typed variable validation
ChatPromptTemplatelist[dict]Multi-turn system/user/assistant message slots
FewShotPromptTemplatestrPrefix-examples-suffix with pluggable ExampleSelectorProtocol
PartialPromptTemplatestr | list[dict]Wraps any template with pre-filled defaults
ConditionalPromptstr | list[dict]Predicate-based branch dispatch

PromptVariable(name, type, required, default, max_length, allowed_values) declares typed constraints. resolve_variables() checks type, length, allowed values, and required-ness, merging with caller-supplied values. Undeclared extras pass through.

PromptRenderer dispatches to one of four RenderFormat engines: F_STRING (default, str.format_map), JINJA2 (optional jinja2 dep), DOLLAR (string.Template), or SIMPLE (literal).

InputSanitizer scans variable values for injection patterns (instruction override, role hijack, system prompt leak) with optional strict mode.

@prompt_template(name, version, tags) registers a class as a named, versioned template — for code-first definitions alongside file-based loading from YAML/JSON.


PromptRegistry maps names to templates (register, get, list, unregister). VersionedPromptStore maintains per-name version history with push(), rollback(steps), and configurable max_versions eviction.

PromptService is the primary runtime facade injected via DI. It resolves name+version, merges variables, applies provider-specific escaping, substitutes content, and notifies an optional PromptObserverProtocol for telemetry or audit.

# Template loading — combined at construction:
DirectoryPromptLoader("/path/to/templates/").load()
DictPromptLoader([{"name": "...", "version": "v1", "content": "..."}]).load()

PromptOptimizer automatically improves prompts on labelled datasets (DSPy-inspired):

StrategyApproach
BOOTSTRAP_FEW_SHOTSearch example pool for best few-shot combination
TEMPLATE_REFINEMENTLLM rewrites template based on failure analysis
ENSEMBLEEvaluate multiple candidate templates, return highest-scoring

DynamicFewShotSelector uses embedding cosine similarity (EmbeddingClientProtocol) for semantic example selection.


sequenceDiagram
    participant P as PromptProvider
    participant C as Container
    participant A as CacheAwarePromptAssembler

    P->>C: register()
    C->>C: singletons: PromptConfig, PromptRegistry, PromptService, Assembler
    P->>C: boot()
    C->>C: resolve(TokenCounterProtocol)
    alt Available
        C->>A: set_token_counter(counter)
    end

PromptProvider at di/provider.py, priority DOMAIN:

  1. register() — loads templates from configured sources, binds all singletons. Early-returns if enabled=False.
  2. boot() — optionally injects TokenCounterProtocol into the assembler for cache-size validation. Graceful fallback.
  3. shutdown() — no-op (in-process, no external backends).

CacheAwarePromptAssembler enforces static-before-dynamic 7-layer ordering for maximum KV-cache reuse:

Layer 1: System instructions ─┐
Layer 2: Tool definitions │ STATIC (cached)
Layer 3: Reference documents │
Layer 4: Few-shot examples ─┘
────────────────────────────────── ← CACHE BOUNDARY
Layer 5: Chat history ─┐
Layer 6: Current query │ DYNAMIC
Layer 7: Dynamic metadata ─┘

Provider-specific strategies dispatch through ProviderCacheStrategyRegistry:

ProviderStrategy
anthropiccache_control: ephemeral on ≤ 4 blocks ≥ 1024 tokens
openai / azureWarn if static prefix < 1024 tokens (auto-cached)
deepseekPad static blocks to nearest 64-token boundary
geminiFlag blocks ≥ 32k tokens for Context Caching API
mistralPass-through

Contractlexigram-contracts LocationPurpose
PromptTemplateProtocolai/llm.pyTemplate interface — name, render(), get_variables()
PromptRegistryProtocolai/llm.pyNamed template registry
PromptAssemblerProtocolai/llm.pyStatic-to-dynamic assembly with cache annotations
PromptRendererProtocolai/llm.pyTemplate string substitution
PromptOptimizerProtocolai/llm.pyAutomatic prompt improvement
TokenCounterProtocolai/llm.pyModel-aware token counting
ChatMessageai/llm.pyShared message value type
EmbeddingClientProtocolai/llm.pyDynamic few-shot selection
DomainEventdomain/events.pyEvent base class
ProviderPrioritycore/provider.pyDI provider ordering

All extend PromptError(AIError) at exceptions.py:

ExceptionWhen Raised
PromptRenderErrorMissing variable or substitution failure
PromptValidationErrorVariable type/length/allowed-value violation
PromptNotFoundErrorTemplate not in registry
PromptVersionErrorVersion conflict or invalid rollback
PromptConfigErrorInvalid configuration
OptimizationErrorOptimization fails
  • PromptRenderedEvent(DomainEvent) — fired after successful render
  • PromptTemplateResolvedHook, PromptRenderedHook, PromptInputSanitizedHook — hook payloads

PointMechanism
Custom templateSubclass AbstractPromptTemplate, implement render()
Custom rendering formatAdd RenderFormat value + branch in PromptRenderer.render()
Custom variable validationAdd patterns to InputSanitizer or custom PromptVariable
Custom example selectorImplement ExampleSelectorProtocol, pass to FewShotPromptTemplate
Custom cache strategyImplement CacheStrategy protocol, register in ProviderCacheStrategyRegistry
Custom optimizer strategyAdd OptimizationStrategy value, implement method on PromptOptimizer
Custom template loaderImplement PromptLoaderProtocol, supply to PromptService
Observability hookImplement PromptObserverProtocol, pass to PromptProvider(observer=...)
Provider escaping rulesExtend _apply_provider_escaping() with new LLMProvider values
Versioned storageConfigure max_versions on VersionedPromptStore