Loading prompts
A PromptLoader is a pluggable source of raw prompt text looked up by a logical key. Its single
operation load(key) returns the raw text of a prompt definition — never a parsed Prompt. Loading
and construction are always separate, composable steps:
raw = loader.load("greet") ← I/O leaf (caller-invoked)prompt = Prompt.from_yaml(raw) ← construction (I/O-free)The engine kernel stays I/O-free at all times; only the loader the caller passes in performs I/O.
Built-in loaders
Section titled “Built-in loaders”MemoryLoader
Section titled “MemoryLoader”MemoryLoader holds a key → text mapping in memory. Its primary use case is
dependency injection in tests: production code uses a FileSystemLoader or a custom loader;
tests substitute a MemoryLoader with hard-coded prompt text. No filesystem access is performed.
// This Source Code Form is subject to the terms of the Mozilla Public// License, v. 2.0. If a copy of the MPL was not distributed with this// file, You can obtain one at https://mozilla.org/MPL/2.0/.
//! Loader guide — MemoryLoader: load raw text by key, then construct a Prompt.//! The kernel stays I/O-free; the loader is a separate, caller-invoked I/O leaf.//! Standalone — `cargo run --example guides_loader_memory`.
use std::collections::HashMap;
use prompting_press::loader::{MemoryLoader, PromptLoader};use prompting_press::Prompt;
fn main() -> Result<(), Box<dyn std::error::Error>> { let mut map = HashMap::new(); map.insert( "greet".to_string(), r#"name: greetrole: userbody: "Hello {{ name }}"variables: name: { type: string, trusted: true }"# .to_string(), ); let loader = MemoryLoader::new(map);
// load() returns raw text — parsing is a separate step. let raw = loader.load("greet")?; let prompt = Prompt::from_yaml(&raw)?; assert_eq!(prompt.name(), "greet"); Ok(())}# This Source Code Form is subject to the terms of the Mozilla Public# License, v. 2.0. If a copy of the MPL was not distributed with this# file, You can obtain one at https://mozilla.org/MPL/2.0/.
"""Loader guide — MemoryLoader: load raw text by key, then construct a Prompt.
The kernel stays I/O-free; the loader is a separate, caller-invoked I/O leaf."""
from prompting_press import Promptfrom prompting_press.loader import MemoryLoader
GREET_YAML = """\name: greetrole: userbody: "Hello {{ name }}"variables: name: { type: string, trusted: true }"""
def main() -> None: loader = MemoryLoader({"greet": GREET_YAML})
# load() returns raw text — parsing is a separate step. raw = loader.load("greet") prompt = Prompt.from_yaml(raw) assert prompt.name == "greet"
if __name__ == "__main__": main()// This Source Code Form is subject to the terms of the Mozilla Public// License, v. 2.0. If a copy of the MPL was not distributed with this// file, You can obtain one at https://mozilla.org/MPL/2.0/.
/** * Loader guide — MemoryLoader: load raw text by key, then construct a Prompt. * * The kernel stays I/O-free; the loader is a separate, caller-invoked I/O leaf. */
import assert from "node:assert/strict";import { test } from "node:test";import { MemoryLoader, Prompt } from "prompting-press";
const GREET_YAML = `\name: greetrole: userbody: "Hello {{ name }}"variables: name: { type: string, trusted: true }`;
test("MemoryLoader: load raw text then construct", async () => { const loader = new MemoryLoader({ greet: GREET_YAML });
// load() returns raw text — parsing is a separate step. const raw = await loader.load("greet"); const prompt = Prompt.fromYaml(raw); assert.equal(prompt.name, "greet");});FileSystemLoader
Section titled “FileSystemLoader”FileSystemLoader maps a logical key to {base}/{key}{suffix} on disk. The default suffix is
.yaml; pass a custom suffix or max_bytes cap via the full constructor. The base directory is
canonicalized at construction time (Rust/Python) or at each load() call (TypeScript).
// This Source Code Form is subject to the terms of the Mozilla Public// License, v. 2.0. If a copy of the MPL was not distributed with this// file, You can obtain one at https://mozilla.org/MPL/2.0/.
//! Loader guide — FileSystemLoader: map a key to a file in a base directory.//! Uses the `assistant.yaml` fixture that lives next to this program.//! Standalone — `cargo run --example guides_loader_filesystem`.
use prompting_press::loader::{FileSystemLoader, PromptLoader};use prompting_press::Prompt;
fn main() -> Result<(), Box<dyn std::error::Error>> { let dir = concat!(env!("CARGO_MANIFEST_DIR"), "/examples");
// Construct from an existing directory (canonicalized at construction time). let loader = FileSystemLoader::with_base(dir)?;
// "assistant" maps to {dir}/assistant.yaml (default suffix ".yaml"). let raw = loader.load("assistant")?; let prompt = Prompt::from_yaml(&raw)?; assert_eq!(prompt.name(), "assistant"); Ok(())}# This Source Code Form is subject to the terms of the Mozilla Public# License, v. 2.0. If a copy of the MPL was not distributed with this# file, You can obtain one at https://mozilla.org/MPL/2.0/.
"""Loader guide — FileSystemLoader: map a key to a file in a base directory.
Uses the ``assistant.yaml`` fixture that lives next to this program."""
from pathlib import Path
from prompting_press import Promptfrom prompting_press.loader import FileSystemLoader
_HERE = Path(__file__).parent
def main() -> None: # Construct from an existing directory (canonicalized at construction time). loader = FileSystemLoader(_HERE)
# "assistant" maps to {dir}/assistant.yaml (default suffix ".yaml"). raw = loader.load("assistant") prompt = Prompt.from_yaml(raw) assert prompt.name == "assistant"
if __name__ == "__main__": main()// This Source Code Form is subject to the terms of the Mozilla Public// License, v. 2.0. If a copy of the MPL was not distributed with this// file, You can obtain one at https://mozilla.org/MPL/2.0/.
/** * Loader guide — FileSystemLoader: map a key to a file in a base directory. * * Uses the `assistant.yaml` fixture that lives next to this program. */
import assert from "node:assert/strict";import nodepath from "node:path";import { test } from "node:test";import { fileURLToPath } from "node:url";import { FileSystemLoader, Prompt } from "prompting-press";
const dir = nodepath.dirname(fileURLToPath(import.meta.url));
test("FileSystemLoader: map key to file and construct", async () => { // Construct from an existing directory. const loader = new FileSystemLoader(dir);
// "assistant" maps to {dir}/assistant.yaml (default suffix ".yaml"). const raw = await loader.load("assistant"); const prompt = Prompt.fromYaml(raw); assert.equal(prompt.name, "assistant");});Loading JSON, TOML, or any format
Section titled “Loading JSON, TOML, or any format”A loader is format-agnostic: it returns raw text and never parses. You choose the format at the
parse step — from_yaml / from_json / from_toml (Rust & Python) or fromYaml / fromJson /
fromToml (TypeScript) — independently of how the loader found the text. So JSON and TOML work
exactly like YAML; only two things change: the FileSystemLoader suffix and which from_* you call.
MemoryLoader needs no suffix at all (it is a direct key→text map); the format is decided solely by
which from_* you parse with.
// This Source Code Form is subject to the terms of the Mozilla Public// License, v. 2.0. If a copy of the MPL was not distributed with this// file, You can obtain one at https://mozilla.org/MPL/2.0/.
//! Loader guide — loading JSON and TOML: the loader is format-agnostic (returns raw//! text), so only the FileSystemLoader suffix and the `from_*` parser change.//! Uses the `assistant.json` / `assistant.toml` fixtures next to this program.//! Standalone — `cargo run --example guides_loader_formats`.
use prompting_press::loader::{FileSystemLoader, PromptLoader};use prompting_press::Prompt;
fn main() -> Result<(), Box<dyn std::error::Error>> { let dir = concat!(env!("CARGO_MANIFEST_DIR"), "/examples");
// JSON: suffix ".json" → loads {dir}/assistant.json, parsed with from_json. let json_loader = FileSystemLoader::new(dir, ".json", FileSystemLoader::DEFAULT_MAX_BYTES)?; let json_raw = json_loader.load("assistant")?; let from_json = Prompt::from_json(&json_raw)?; assert_eq!(from_json.name(), "assistant");
// TOML: suffix ".toml" → loads {dir}/assistant.toml, parsed with from_toml. let toml_loader = FileSystemLoader::new(dir, ".toml", FileSystemLoader::DEFAULT_MAX_BYTES)?; let toml_raw = toml_loader.load("assistant")?; let from_toml = Prompt::from_toml(&toml_raw)?; assert_eq!(from_toml.name(), "assistant");
// Empty suffix → the extension lives in the key instead (same file either way). let bare_loader = FileSystemLoader::new(dir, "", FileSystemLoader::DEFAULT_MAX_BYTES)?; let bare_raw = bare_loader.load("assistant.json")?; assert_eq!(Prompt::from_json(&bare_raw)?.name(), "assistant");
Ok(())}# This Source Code Form is subject to the terms of the Mozilla Public# License, v. 2.0. If a copy of the MPL was not distributed with this# file, You can obtain one at https://mozilla.org/MPL/2.0/.
"""Loader guide — loading JSON and TOML.
The loader is format-agnostic (returns raw text), so only the FileSystemLoader``suffix`` and the ``from_*`` parser change. Uses the ``assistant.json`` /``assistant.toml`` fixtures next to this program."""
from pathlib import Path
from prompting_press import Promptfrom prompting_press.loader import FileSystemLoader
_HERE = Path(__file__).parent
def main() -> None: # JSON: suffix ".json" -> loads {dir}/assistant.json, parsed with from_json. json_loader = FileSystemLoader(_HERE, suffix=".json") from_json = Prompt.from_json(json_loader.load("assistant")) assert from_json.name == "assistant"
# TOML: suffix ".toml" -> loads {dir}/assistant.toml, parsed with from_toml. toml_loader = FileSystemLoader(_HERE, suffix=".toml") from_toml = Prompt.from_toml(toml_loader.load("assistant")) assert from_toml.name == "assistant"
# Empty suffix -> the extension lives in the key instead (same file either way). bare_loader = FileSystemLoader(_HERE, suffix="") assert Prompt.from_json(bare_loader.load("assistant.json")).name == "assistant"
def test_loader_formats() -> None: main()
if __name__ == "__main__": main()// This Source Code Form is subject to the terms of the Mozilla Public// License, v. 2.0. If a copy of the MPL was not distributed with this// file, You can obtain one at https://mozilla.org/MPL/2.0/.
/** * Loader guide — loading JSON and TOML. * * The loader is format-agnostic (returns raw text), so only the FileSystemLoader * suffix and the `from*` parser change. Uses the `assistant.json` / `assistant.toml` * fixtures next to this program. */
import assert from "node:assert/strict";import nodepath from "node:path";import { test } from "node:test";import { fileURLToPath } from "node:url";import { FileSystemLoader, Prompt } from "prompting-press";
const dir = nodepath.dirname(fileURLToPath(import.meta.url));
test("FileSystemLoader: JSON and TOML are format-agnostic", async () => { // JSON: suffix ".json" -> loads {dir}/assistant.json, parsed with fromJson. const jsonLoader = new FileSystemLoader(dir, ".json"); const fromJson = Prompt.fromJson(await jsonLoader.load("assistant")); assert.equal(fromJson.name, "assistant");
// TOML: suffix ".toml" -> loads {dir}/assistant.toml, parsed with fromToml. const tomlLoader = new FileSystemLoader(dir, ".toml"); const fromToml = Prompt.fromToml(await tomlLoader.load("assistant")); assert.equal(fromToml.name, "assistant");
// Empty suffix -> the extension lives in the key instead (same file either way). const bareLoader = new FileSystemLoader(dir, ""); const bare = Prompt.fromJson(await bareLoader.load("assistant.json")); assert.equal(bare.name, "assistant");});Custom loaders
Section titled “Custom loaders”Any callable or struct that maps a key to raw text works as a loader. In Rust, a
Fn(&str) -> Result<String, PromptLoadError> closure is a loader directly, with no struct
required. In Python, any callable (key: str) -> str satisfies the protocol. In TypeScript,
implement the PromptLoader interface (load(key): Promise<string>).
// This Source Code Form is subject to the terms of the Mozilla Public// License, v. 2.0. If a copy of the MPL was not distributed with this// file, You can obtain one at https://mozilla.org/MPL/2.0/.
//! Loader guide — custom loaders: a closure or a struct implementing PromptLoader.//! No struct is required — any `Fn(&str) -> Result<String, PromptLoadError>` closure works.//! Standalone — `cargo run --example guides_loader_custom`.
use prompting_press::loader::PromptLoader;use prompting_press::{Prompt, PromptLoadError};
const GREET_YAML: &str = r#"name: greetrole: userbody: "Hello {{ name }}"variables: name: { type: string, trusted: true }"#;
fn main() -> Result<(), Box<dyn std::error::Error>> { // A closure is a loader — no struct needed. let loader = |key: &str| -> Result<String, PromptLoadError> { match key { "greet" => Ok(GREET_YAML.to_string()), _ => Err(PromptLoadError::NotFound { key: key.to_string(), }), } };
let raw = loader.load("greet")?; let prompt = Prompt::from_yaml(&raw)?; assert_eq!(prompt.name(), "greet");
// A missing key returns NotFound. assert!(loader.load("missing").is_err()); Ok(())}# This Source Code Form is subject to the terms of the Mozilla Public# License, v. 2.0. If a copy of the MPL was not distributed with this# file, You can obtain one at https://mozilla.org/MPL/2.0/.
"""Loader guide — custom loaders: a plain callable or a class implementing the PromptLoader protocol.
Any callable ``(key: str) -> str`` satisfies the loader contract — no class needed."""
from prompting_press import Prompt, PromptLoadError, make_prompt_load_errorfrom prompting_press.loader import LOAD_NOT_FOUND
GREET_YAML = """\name: greetrole: userbody: "Hello {{ name }}"variables: name: { type: string, trusted: true }"""
def _source_loader(key: str) -> str: """A plain function works as a loader — just return the raw text or raise.""" if key == "greet": return GREET_YAML raise make_prompt_load_error(LOAD_NOT_FOUND, f"key not found: `{key}`")
def main() -> None: # A plain callable — no struct or class required. raw = _source_loader("greet") prompt = Prompt.from_yaml(raw) assert prompt.name == "greet"
# A missing key raises PromptLoadError. try: _source_loader("missing") assert False, "should have raised" except PromptLoadError: pass
if __name__ == "__main__": main()// This Source Code Form is subject to the terms of the Mozilla Public// License, v. 2.0. If a copy of the MPL was not distributed with this// file, You can obtain one at https://mozilla.org/MPL/2.0/.
/** * Loader guide — custom loaders: implement the `PromptLoader` interface. * * A class with an async `load(key): Promise<string>` satisfies the contract. */
import assert from "node:assert/strict";import { test } from "node:test";import type { PromptLoader } from "prompting-press";import { LOAD_NOT_FOUND, Prompt, PromptLoadError } from "prompting-press";
const GREET_YAML = `\name: greetrole: userbody: "Hello {{ name }}"variables: name: { type: string, trusted: true }`;
class InlineLoader implements PromptLoader { readonly #map: Record<string, string>;
constructor(map: Record<string, string>) { this.#map = map; }
async load(key: string): Promise<string> { const text = this.#map[key]; if (text === undefined) { throw new PromptLoadError(`key not found: \`${key}\``, [ { field: "", code: LOAD_NOT_FOUND, message: `key not found: \`${key}\``, }, ]); } return text; }}
test("custom loader: class implementing PromptLoader", async () => { const loader = new InlineLoader({ greet: GREET_YAML });
const raw = await loader.load("greet"); const prompt = Prompt.fromYaml(raw); assert.equal(prompt.name, "greet");
// A missing key rejects with PromptLoadError. await assert.rejects(() => loader.load("missing"), PromptLoadError);});Error taxonomy
Section titled “Error taxonomy”PromptLoadError is distinct from LoadError (the parse/shape error). except PromptLoadError
/ catch (e instanceof PromptLoadError) does NOT catch a malformed-YAML LoadError. The two error
codes are:
| Code | When raised |
|---|---|
load_not_found | Key absent from the backing store; also used for traversal-rejected keys. |
load_io | I/O failure, or file exceeds max_bytes. |
// This Source Code Form is subject to the terms of the Mozilla Public// License, v. 2.0. If a copy of the MPL was not distributed with this// file, You can obtain one at https://mozilla.org/MPL/2.0/.
//! Loader guide — a missing key raises PromptLoadError (load_not_found),//! distinct from ConsumerError (the parse/shape error raised on malformed YAML).//! Standalone — `cargo run --example guides_loader_miss`.
use std::collections::HashMap;
use prompting_press::error::code;use prompting_press::loader::{MemoryLoader, PromptLoader};use prompting_press::{Prompt, PromptLoadError};
fn main() -> Result<(), Box<dyn std::error::Error>> { let loader = MemoryLoader::new(HashMap::new());
// A missing key returns PromptLoadError::NotFound — not a parse error. let err = loader.load("missing").unwrap_err(); match &err { PromptLoadError::NotFound { key } => assert_eq!(key, "missing"), other => panic!("unexpected variant: {other:?}"), } // The normalized error row carries code "load_not_found". assert_eq!(err.to_field_error().code, code::LOAD_NOT_FOUND);
// PromptLoadError is distinct from ConsumerError. // Parsing bad YAML raises ConsumerError::Load — a different type on a different path. let parse_result = Prompt::from_yaml("not: valid: yaml: ["); assert!(parse_result.is_err());
Ok(())}# This Source Code Form is subject to the terms of the Mozilla Public# License, v. 2.0. If a copy of the MPL was not distributed with this# file, You can obtain one at https://mozilla.org/MPL/2.0/.
"""Loader guide — a missing key raises PromptLoadError (load_not_found),distinct from LoadError (the parse/shape error raised on malformed YAML).
``except PromptLoadError`` does NOT catch a malformed-YAML ``LoadError``."""
from prompting_press import LoadError, Prompt, PromptLoadErrorfrom prompting_press.loader import LOAD_NOT_FOUND, MemoryLoader
def main() -> None: loader = MemoryLoader({})
# A missing key raises PromptLoadError — not a parse error. try: loader.load("missing") assert False, "should have raised" except PromptLoadError as exc: assert exc.errors[0].code == LOAD_NOT_FOUND
# PromptLoadError is distinct from LoadError. # Parsing bad YAML raises LoadError — a different type on a different path. try: Prompt.from_yaml("not: valid: yaml: [") assert False, "should have raised" except LoadError: pass
if __name__ == "__main__": main()// This Source Code Form is subject to the terms of the Mozilla Public// License, v. 2.0. If a copy of the MPL was not distributed with this// file, You can obtain one at https://mozilla.org/MPL/2.0/.
/** * Loader guide — a missing key rejects with PromptLoadError (load_not_found), * distinct from LoadError (the parse/shape error thrown on malformed YAML). * * `catch (e) { if (e instanceof PromptLoadError) }` does NOT catch a malformed-YAML `LoadError`. */
import assert from "node:assert/strict";import { test } from "node:test";import { LOAD_NOT_FOUND, LoadError, MemoryLoader, Prompt, PromptLoadError,} from "prompting-press";
test("missing key rejects with PromptLoadError (load_not_found)", async () => { const loader = new MemoryLoader({});
// A missing key rejects with PromptLoadError — not a parse error. await assert.rejects( () => loader.load("missing"), (err: unknown) => { assert.ok(err instanceof PromptLoadError); assert.equal(err.errors[0].code, LOAD_NOT_FOUND); return true; }, );
// PromptLoadError is distinct from LoadError. // Parsing bad YAML throws LoadError — a different type on a different path. assert.throws( () => Prompt.fromYaml("not: valid: yaml: ["), (err: unknown) => { assert.ok(err instanceof LoadError); return true; }, );});API summary
Section titled “API summary”| Rust | Python | TypeScript | |
|---|---|---|---|
| In-memory loader | MemoryLoader::new(HashMap) | MemoryLoader(dict) | new MemoryLoader(record|map) |
| Filesystem loader | FileSystemLoader::with_base(path)? | FileSystemLoader(base) | new FileSystemLoader(base) |
| Custom loader | closure |key| -> Result<_, PromptLoadError> | callable (key) -> str | class with async load(key): Promise<string> |
| Error type | PromptLoadError | PromptLoadError | PromptLoadError |
load() sync? | sync | sync | async (Promise<string>) |
docs current as of 0.5.0