Prompt provenance reference¶
Topics: moderation, requests
A waiting request stores two prompts: the one the client sent and the one workers receive. They differ whenever a style is applied or the replacement filter rewrites the text.
Code map¶
| Concept | File | Symbol |
|---|---|---|
| Effective worker input | horde/classes/base/waiting_prompt.py |
WaitingPrompt.prompt |
| Original submission | horde/classes/base/waiting_prompt.py |
WaitingPrompt.submitted_prompt |
| Named consumers of the original | horde/classes/base/waiting_prompt.py |
SubmittedPromptPurpose |
| Privileged read | horde/classes/base/waiting_prompt.py |
WaitingPrompt.read_privileged_submitted_prompt |
| Requester serialization | horde/classes/base/waiting_prompt.py |
WaitingPrompt.get_submitted_request |
| Capture point | horde/apis/v2/base.py |
GenerateTemplate._post_inner |
| Ownership check | horde/apis/v2/base.py |
assert_submitting_key |
| Image retrieval endpoint | horde/apis/v2/stable.py |
ImageAsyncRequest.get |
| Text retrieval endpoint | horde/apis/v2/kobold.py |
TextAsyncRequest.get |
Prompt meanings¶
| Value | Meaning | Intended consumers |
|---|---|---|
WaitingPrompt.prompt |
Effective generation input, including styles and moderation | Generation, worker, and scheduling code |
WaitingPrompt.submitted_prompt |
Parsed submission string, before styles or moderation | Authenticated request owner, evidence capture |
Event moderation_prompt |
Input to moderation after style expansion | Moderator evidence review |
Event effective_prompt |
Prompt a worker received; null for a rejection |
Moderator evidence review |
submitted_prompt is captured in GenerateTemplate._post_inner before the subclass validate() runs, because that
is where styles and moderation are applied. Each constructor path receives the captured string explicitly: the waiting
prompt constructor commits internally, so setting provenance afterwards would leave a committed row whose provenance is
temporarily missing.
Access contract¶
The column is nullable, deferred, and configured with raiseload=True. Ordinary ORM queries, polymorphic loads, joins,
and relationship loads omit it, and an accidental lazy attribute access raises instead of fetching the text. Reads go
through read_privileged_submitted_prompt(purpose=...), which names one of the two approved consumers in
SubmittedPromptPurpose. The scalar read leaves the deferred attribute unloaded on the ORM instance.
The purpose declares caller intent. It records which reviewed path reads the column, so a future consumer has to
identify itself. Authorization is a separate step, the retrieval endpoints call assert_submitting_key
first and serialize only once it returns.
tests/unit/test_submitted_request.py covers this contract, including that ORM queries and eager loads omit the
column, that the explicit read leaves it unloaded, and that the original is absent from every existing export.
tests/integration/test_request_parameters.py covers the retrieval endpoints: the original is returned after a
filter rewrite, a row without provenance omits the prompt, and the response is not cacheable.
Retrieval responses¶
GET /api/v2/generate/request/<id> and GET /api/v2/generate/text/request/<id> return the original prompt alongside
normalized parameters, under Cache-Control: private, no-store. The response uses the generation input shape. It is
an approximation: parameters carry defaults and any style's overrides, while the prompt is the pre-style submission. A
client reproducing a styled request needs the style it originally specified.
Sharp edges¶
- A missing original stays missing. Rows created before the column existed have
NULL, and the response omitspromptentirely. Never writesubmitted_prompt or prompt, infer an original from a replacement, or backfill fromprompt. The effective prompt reveals what the filters rewrote, which is the leak this column exists to close. - Deferred loading is an ORM rule. The database does not enforce anything:
vars(),__dict__serialization, enumerating ORM columns for export,select(WaitingPrompt.__table__),SELECT *,undefer, and newly constructed instances can all reach the original prompt text. Public payloads must be built from explicit field lists. - The read needs a live session. A detached instance raises. The retrieval endpoints materialize the response dictionary before removing the session.
- Worker payloads and shared-image metadata keep the effective
prompt. Shared metadata already exposes the effective prompt. This contract leaves that open and must never add the original. - Raw-submission logging is unchanged.
CorruptPromptlog lines and the object-storage rejection upload still carry the rejected prompt, and database backups still contain originals. - New copy or retry paths must capture their own submission. Directly constructed waiting prompts without provenance remain valid legacy rows; never copy one request's original onto another request's effective prompt.