os_helper.prompts_utils module
Model-Facing Prompt Templates, in Several Languages
The third localization shape the suite needs, and the one that is not
translation. Catalog serves interface copy keyed on its
English source; Locales serves any text keyed
on meaning; Prompts serves the text a model reads, which looks like
the second but is consumed like neither.
Why prompts are not gettext
A prompt’s language versions are independently authored, not translated. Measured in standpoint on 2026-10-22, one prompt ran 1345 characters in English, 1532 in French and 1479 in Spanish: nobody translated that: somebody wrote the instruction three times, because what makes a model behave in French is not the French words of the English sentence.
Two consequences follow, and they are the whole reason this module exists rather than a second catalogue:
The key is the stable thing. Prompt engineering reworks the wording constantly. Key on the English text, as gettext does, and every tweak to the English orphans the other languages — which then fall back to English silently, exactly where a wrong language is hardest to notice. Key on
axis_promptand the wording is free to move.A 1300-character msgid is not a catalogue entry. It is unreadable in a
.po, unusable in a translation tool, and meaningless to a translator who is not a prompt engineer.
So prompts stay keyed on meaning, in YAML, read through Locales —
which already parses, validates and falls back. What this class adds is the
four things every caller was otherwise writing by hand:
the whole-language view (a caller wants every prompt for one language, not one prompt at a time), cached;
the field shape
key.system/key.user, for prompts that come in parts;language resolution for a requested tag, so
fr-CAfindsfr;a placeholder check across languages — the failure specific to this shape, where a translated prompt drops a
{field}the code fills and the render raises in production rather than in a test.
Layout
prompts:
axis_prompt: # simple: key -> language -> text
en: You are naming the four poles…
fr: Vous nommez les quatre pôles…
eyeball_critique: # fielded: key -> field -> language -> text
system:
en: You are a strict visual-quality reviewer…
user:
en: Examine the image and write…
Usage example
>>> # p = Prompts.from_path("standpoint/locales/i18n.yaml", section="bundle")
>>> # p.text("axis_prompt", "fr")
>>> # p.render("title_template", "fr", domain="LLM")
>>> # p.bundle("fr")["axis_prompt"]
- class os_helper.prompts_utils.Prompts(locales, section)[source]
Bases:
objectOne section of a locale file, read as model-facing templates.
Build one with
from_path(); the constructor takes an already-parsedLocalesfor callers that have one.- locales
The parsed file. Reused rather than re-read, so a package that also has a
guisection pays for one parse.- Type:
- section
Which section holds the prompts. Named by the caller because os-helper has no business knowing that prompts are a thing — it is the member that decides, and members differ (
promptshere,bundlethere).- Type:
- bundle(language=None)[source]
Every prompt for one language at once.
This is how prompts are actually consumed: a caller resolves the language once, then reads several templates. Asking key by key means resolving the language again for each, which is both slower and an invitation to resolve it inconsistently.
- Parameters:
language (str or None) – Wanted language, resolved through
resolve().- Returns:
{key: text}for simple prompts,{key: {field: text}}for fielded ones. Values come back as stored, so a key holding a structure for that language (a nested sub-tree, not a template) survives intact. A key with nothing for this language and nothing for the default is omitted rather than raising: a bundle is a view, and a caller that needs one key should ask for it by name and get the error.- Return type:
- classmethod from_path(path, section='prompts')[source]
Read a locale file and take one section of it as prompts.
- Parameters:
path (str or pathlib.Path) – The
locales/i18n.yamlto read. Must live inside the importable package directory, or it is not package data and never reaches the wheel.section (str) – The section holding the templates.
- Returns:
A handle. The file is parsed and validated now, by Locales.
- Return type:
- property languages: tuple[str, ...]
Every language any prompt in this section is written in, sorted.
Taken from the prompts themselves rather than from
meta: a package whose interface is bilingual may well have prompts in three languages, or in one, and the file should not have to lie about either.
- problems()[source]
Everything wrong with this section, as sentences.
Checks, in order:
a prompt missing a language that the others have — the reader gets a different language than the one they asked for, which for a model means a different answer;
a prompt whose text is empty;
a language whose template does not carry the same placeholders as the default — the failure specific to this shape. A French prompt that drops the
{criteria}the English one has will raise at render time, in production, on whichever request first asked for French. An extra placeholder is just as bad: it reachesformat()with no value and raises there instead.
- render(key, language=None, *, field=None, **values)[source]
One prompt with its
{placeholder}fields filled in.Templates are data, never code: this formats, it never evaluates. The placeholder names are checked first, so a caller learns what it forgot instead of sending a half-filled instruction to a model — which would not fail, it would just answer the wrong question.
- Parameters:
- Returns:
The formatted prompt.
- Return type:
- Raises:
KeyError – If the template needs a placeholder no value was given for.
- resolve(language)[source]
The language that will actually be served for a request.
- Parameters:
language (str or None) – A language code, possibly regional (
fr-CA) or absent.- Returns:
language when prompts exist in it, its base language when only that has them, otherwise
default_language.- Return type:
- text(key, language=None, *, field=None)[source]
One prompt, in the best available language.
- Parameters:
key (str) – The semantic key, e.g.
axis_prompt.language (str or None) – Wanted language; resolved through
resolve()first, so a regional tag or an unknown one never reaches the file.field (str, optional) – For prompts that come in parts, which part — typically
"system"or"user". Omit for a single-text prompt.
- Returns:
The template, unformatted. Use
render()to fill it.- Return type:
- Raises:
KeyError – If the key (or the field) does not exist, or carries neither the wanted language nor the default one. Raising beats returning the key: a prompt that is silently the string
axis_promptwould reach a model and produce confident nonsense.