Class Prompts

java.lang.Object
ai.redouble.nucleo.prompt.Prompts

public final class Prompts extends Object
Governor-style facade for the Prompt registry. Only public entry point for obtaining Prompt instances.

Resolution order in produce(String):

  1. CACHE (for keys served by StaticPromptSource) - returned immediately with fingerprint-aware guardrail re-validation if new guardrails were registered since cache insert.
  2. OVERRIDES.get(key) - per-key override set via replace(String, PromptSource).
  3. GLOBAL_BACKEND - wholesale substitute set via setGlobalBackend(PromptSource).
  4. DEFAULTS.get(key) - code-declared default populated by the @StaticPrompt / @DynamicPrompt scanner.
A miss in every tier throws PromptNotFoundException.

Invalidation ordering is reverse of intuitive put/remove so concurrent readers never observe the race "new override, stale cache":

The produce side holds up the same guarantee from its end: after publishing a static source's prompt to the cache, it re-resolves the key and evicts its own entry when a substitution landed mid-produce, so a stale prompt can never shadow a fresh source.

Guardrails attach per-key via addGuardrail(String, Supplier). They are NOT attached to the source, so source substitution cannot bypass validation. Baseline guardrails (addBaselineGuardrail(Supplier)) apply to every Prompt produced regardless of key, including inline of(String) calls; nothing is registered by default - a deployment wires the shipped ones (size cap, exfiltration marker) itself.

Since:
0.1 (2026-04-20)
Author:
Andrey Santrosyan
  • Method Details

    • produce

      public static Prompt produce(String key) throws PromptNotFoundException, GuardrailException
      Resolve the registered source for this key, produce its content, validate via per-key and baseline guardrails, return a Prompt. If the source is a StaticPromptSource, subsequent calls return a cached Prompt.
      Throws:
      PromptNotFoundException - if no source resolves for the key
      GuardrailException - if any guardrail rejects the produced Prompt
    • of

      public static Prompt of(String text) throws GuardrailException
      Build a one-shot Prompt under a synthetic key derived from a content hash. Not registered. Not substitutable. For inline throwaway content; registered @StaticPrompt / @DynamicPrompt declarations are the preferred path for anything reusable. Baseline guardrails still run.
      Throws:
      GuardrailException
    • of

      public static Prompt of(String key, String text) throws PromptNotFoundException, GuardrailException
      Register an inline text prompt as a code-level default under an explicit key and return it. If the key already has a default, the prior registration is replaced (this is the ergonomic entry point for "declare a prompt in code without the annotation scanner," so last-write-wins is the documented semantic). Does not populate the override layer - replace(String, PromptSource) and resetOverride(String) still function normally on top.
      Throws:
      PromptNotFoundException
      GuardrailException
    • bindStaticDefault

      public static Prompt bindStaticDefault(String key, String text) throws PromptNotFoundException, GuardrailException
      Lazy bind: if no default is registered for this key, install one wrapping text in a StaticTextSource. Subsequent calls reuse the existing registration and preserve the cached Prompt - no put, no cache invalidate. The intended caller is the runtime hook on SingleObjectiveThinker that resolves a @StaticPrompt instance-method declaration on first construction; the hook needs idempotent registration semantics to avoid invalidating the StaticPromptSource cache on every thinker construction.
      Throws:
      PromptNotFoundException
      GuardrailException
    • replace

      public static void replace(String key, PromptSource source)
      Per-key override. Invalidates the cache entry for this key before publishing the new source.
    • resetOverride

      public static void resetOverride(String key)
      Clear a per-key override, returning produce() to the global-backend / default layer.
    • setGlobalBackend

      public static void setGlobalBackend(PromptSource backend)
      Substitute a single source for every key. Clears the cache so every key re-produces.
    • clearGlobalBackend

      public static void clearGlobalBackend()
    • addGuardrail

      public static void addGuardrail(String key, Supplier<Guardrail<Prompt>> factory)
      Attach a guardrail factory to a specific key. Survives source replacement. Each produce(key) call that needs validation invokes the factory to get a fresh guardrail Job instance and dispatches it via JobDispatcher - Jobs are one-shot, so factories (not instances) are what survive in the registry. Invalidates the cached validation result.
    • addBaselineGuardrail

      public static void addBaselineGuardrail(Supplier<Guardrail<Prompt>> factory)
      Attach a framework-baseline guardrail factory that runs for every Prompt produced. Factory is invoked fresh per validation (see addGuardrail(String, Supplier)). Order of registration determines execution order.
    • removeBaselineGuardrail

      public static void removeBaselineGuardrail(Supplier<Guardrail<Prompt>> factory)
      Remove a previously-registered baseline guardrail factory. No-op if not present.
    • scanPackage

      public static void scanPackage(String pkg)
      Eagerly scan a package for @StaticPrompt / @DynamicPrompt declarations and register their default sources. Also disables the first-produce auto-scan since the caller has explicitly directed discovery. One scan per process: if any scan has already fired (this method or the auto-scan), the call is a silent no-op.
    • registerAnnotated

      public static void registerAnnotated(AnnotatedElement element)
      Registers one @StaticPrompt/@DynamicPrompt element (a type, static field or static method) discovered at build time, and marks discovery done so the first-produce auto-scan never fires. A build that enumerates the annotated elements itself - the Quarkus extension does, from its class index - calls this per element instead of the classpath scan that a native image cannot run. The per-element validation and registration are PromptScanner's, so build-time and scan discovery share one contract.
    • setDefaultScanPackage

      public static void setDefaultScanPackage(String pkg)
      Change the package auto-scanned on first produce(String) or of(String) call. Must be called before any produce/of, otherwise has no effect because the auto-scan has already fired.
    • warmCache

      public static void warmCache() throws PromptNotFoundException, GuardrailException
      Eagerly produce every registered StaticPromptSource default, running its guardrails and populating the cache. Call at bootstrap (outside any resource-holding region) so subsequent thinker-side produce(String) calls hit the cache.
      Throws:
      PromptNotFoundException
      GuardrailException
    • computeContextFor

      public static PromptContext computeContextFor(String key, com.fasterxml.jackson.databind.JsonNode content)
      Return the PromptContext lineage for a given (key, content) pair. Called by TextPrompt.context(). Memoized on the Prompt identity via a weak map so repeated context() calls on the same Prompt don't rehash.