Prompts¶
The imbi.common.prompts package holds the prompt CMS code that
imbi-api and the prompt consumers share. A consumer is a service that
reads its system prompt from the CMS, such as imbi-assistant and
imbi-slackbot. For the operator view of the CMS, see
Prompts.
| Module | What it does |
|---|---|
system |
Gives a consumer its system prompt, with a packaged fallback |
resolve |
Reads prompts and versions from the graph and resolves references |
rendering |
Renders templates in a Jinja2 sandbox with resource limits |
pool |
Keeps warm worker processes that render templates |
worker |
The worker process (python -m imbi.common.prompts.worker) |
References¶
A reference names a prompt version:
| Form | Selects |
|---|---|
namespace/slug |
The version that has the prompt's default label |
namespace/slug@label |
The version that has the label. If the prompt does not have the label, the default label |
namespace/slug@n |
Version n |
resolve.resolve(db, ref) returns the prompt, the version, and the
label. It raises InvalidRef for a bad reference and PromptNotFound
when the prompt, the version or a usable label does not exist. Both are
subclasses of PromptError.
Load a system prompt¶
system.load_system_prompt() returns a SystemPrompt. It tries three
sources in this order:
override: a template that the operator supplies, for example in an environment setting.- The CMS version that
refnames. - The
DefaultPromptthat is packaged with Imbi.
A failure in the CMS path, such as a missing prompt or a render error,
is logged. The function then uses the packaged prompt. A broken prompt
therefore never stops a conversation. SystemPrompt.source tells you
which source was used.
from imbi.common.prompts import system
prompt = await system.load_system_prompt(
db,
'imbi-assistant/system@stable',
{'display_name': 'Pat', 'email': 'pat@example.com', ...},
system.ASSISTANT,
)
The function passes only the variables that the version declares. An editor can therefore remove a variable from a version without a render error.
When the CMS version names an enabled AI model that an anthropic
provider serves, SystemPrompt.model_id holds its model ID. In all
other cases it is None, and the consumer uses its configured model.
Render limits¶
rendering.render() renders the system template and each message of a
version. It raises RenderError when the variables are not valid, the
template fails, or the template breaks a limit. The sandbox blocks
private attributes and Python internals. A template sees only the
variables and the registered providers.
| Limit | Value |
|---|---|
| Rendered output | 262144 characters (MAX_OUTPUT) |
| Work units for one render | 4194304 (MAX_WORK) |
| Work units for one loop iteration | 256 (ITERATION_COST) |
range() length |
1000 (MAX_RANGE) |
| Loop nesting | 2 (MAX_LOOP_DEPTH) |
| Wall clock, including provider calls | 2 seconds (RENDER_TIMEOUT) |
Macros and recursive loops are not allowed. Only principals with
prompt:update can save a template.
Render workers¶
Renders run in separate worker processes. A worker has no database access and no credentials. When a template calls a provider, the worker asks the parent process to run it. The parent kills a worker that runs too long, crashes, or sends too much output, and starts a new one.
Add pool.pool_lifespan to the service lifespan to start the workers at
startup and stop them at shutdown. imbi-api, imbi-assistant,
imbi-slackbot and imbi-workflows do this. If the workers cannot
start, the service still starts, and a render starts a worker when it
needs one.
| Variable | Default | Description |
|---|---|---|
IMBI_PROMPT_WORKERS |
2 |
Workers for each service process |
IMBI_PROMPT_WORKER_MAX_RENDERS |
500 |
Renders before the pool replaces a worker |
IMBI_PROMPT_WORKER_MEMORY_MB |
256 |
Address space that a worker can add after it starts. Linux only |
IMBI_PROMPT_RENDER_INPROCESS |
false |
Render in the service process. For debugging only: it removes the isolation |
API Reference¶
System prompts¶
load_system_prompt
async
¶
load_system_prompt(
db: Graph | None,
ref: str,
variables: Mapping[str, str],
default: DefaultPrompt,
*,
override: str | None = None,
) -> SystemPrompt
Return the consumer's system prompt.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
db
|
Graph | None
|
The graph, or |
required |
ref
|
str
|
The CMS reference, such as |
required |
variables
|
Mapping[str, str]
|
The consumer's template variables. Only the variables the version declares are passed to it, so an editor may drop one without breaking the render. |
required |
default
|
DefaultPrompt
|
The packaged prompt used when the CMS cannot answer. |
required |
override
|
str | None
|
An operator-supplied template that replaces both. |
None
|
Source code in libraries/common/src/imbi/common/prompts/system.py
load_model_id
async
¶
Return the model id of ref's version, when a consumer can call it.
None when the prompt is missing, names no model, or names one
that is disabled or served by an unsupported driver. Errors are
logged, never raised.
Source code in libraries/common/src/imbi/common/prompts/system.py
SystemPrompt
dataclass
¶
SystemPrompt(
text: str,
source: Source,
ref: str | None = None,
model_id: str | None = None,
max_tokens: int | None = None,
temperature: float | None = None,
)
A rendered system prompt and the settings that came with it.
DefaultPrompt
dataclass
¶
DefaultPrompt(
namespace: str,
slug: str,
name: str,
description: str,
template: str,
variables: tuple[str, ...],
type: str = 'core_system',
)
A prompt Imbi ships, used as the fallback and as seed data.
References¶
resolve
async
¶
Find the version a reference names.
Returns:
| Type | Description |
|---|---|
Prompt
|
The prompt, the version, and the label used ( |
PromptVersion
|
reference named a version number). |
Raises:
| Type | Description |
|---|---|
InvalidRef
|
The reference is not valid. |
PromptNotFound
|
The prompt, version, or a usable label does not exist. |
Source code in libraries/common/src/imbi/common/prompts/resolve.py
parse_ref ¶
Split namespace/slug@label into its parts.
Raises:
| Type | Description |
|---|---|
InvalidRef
|
The reference is not valid. |
Source code in libraries/common/src/imbi/common/prompts/resolve.py
PromptError ¶
Bases: Exception
A reference cannot be resolved.
InvalidRef ¶
Bases: PromptError
The reference is not namespace/slug[@label|@n].
PromptNotFound ¶
Bases: PromptError
The prompt, version, or a usable label does not exist.
Rendering¶
render
async
¶
render(
version: PromptVersion,
variables: Mapping[str, object],
providers: Mapping[str, Provider],
) -> RenderedPrompt
Render the system prompt and each message of version.
Raises:
| Type | Description |
|---|---|
RenderError
|
The variables are not valid, the template fails or breaks a limit, the render takes too long, or the output is too large. |
Source code in libraries/common/src/imbi/common/prompts/rendering.py
render_decision
async
¶
render_decision(
version: PromptVersion,
variables: Mapping[str, object],
providers: Mapping[str, Provider],
) -> RenderedDecision
Render the state and every question template of version.
Raises:
| Type | Description |
|---|---|
RenderError
|
As :func: |
Source code in libraries/common/src/imbi/common/prompts/rendering.py
check_syntax ¶
Parse each template so a broken version cannot be saved.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
version_fields
|
Mapping[str, str]
|
Field label ( |
required |
Raises:
| Type | Description |
|---|---|
RenderError
|
A template does not parse, uses a macro, or nests loops too deep. |
Source code in libraries/common/src/imbi/common/prompts/rendering.py
validate_variables ¶
validate_variables(
schema: Mapping[str, PromptVariable],
variables: Mapping[str, object],
) -> None
Check the caller's variables against the version's schema.
Raises:
| Type | Description |
|---|---|
RenderError
|
A variable is unknown, missing, or the wrong type. |
Source code in libraries/common/src/imbi/common/prompts/rendering.py
RenderError ¶
Bases: ValueError
The template or the variables cannot produce a prompt.
Worker pool¶
pool_lifespan
async
¶
Start warm render workers with a service, and stop them after.
Source code in libraries/common/src/imbi/common/prompts/pool.py
get_pool ¶
Return the pool for the running event loop.
Workers are tied to the loop that started them. A pool left by a
loop that has ended (tests, TestClient portals) is killed and
replaced.
Source code in libraries/common/src/imbi/common/prompts/pool.py
RenderPool ¶
Warm render workers bound to one event loop.
Source code in libraries/common/src/imbi/common/prompts/pool.py
abandon ¶
Kill every worker without the pool's event loop.
close
async
¶
Stop every worker.
Source code in libraries/common/src/imbi/common/prompts/pool.py
render_sources
async
¶
render_sources(
schema: Mapping[str, PromptVariable],
variables: Mapping[str, object],
providers: Mapping[str, Provider],
sources: Mapping[str, str],
) -> dict[str, str]
Render sources in a worker; see :func:.rendering.render.
Raises:
| Type | Description |
|---|---|
RenderError
|
The render failed, timed out, or the worker stopped. |
Source code in libraries/common/src/imbi/common/prompts/pool.py
start
async
¶
Start the workers now instead of on first use.