platform: application-owned system prompts — verify faithful passthrough on every API surface + document #179
Reference in New Issue
Block a user
Delete Branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Principle (decided 2026-07-17)
System prompts are application-owned, not operator-managed. The helexa.ai ecosystem serves an unenumerable diversity of workloads through OpenAI/Anthropic-compatible APIs; operators cannot and should not curate per-use-case prompts. Applications tailor model behaviour by sending their system prompt through the standard API fields, and the serving chain (edge → router → cortex → neuron) passes it through faithfully — no injection, no rewriting, no defaults.
(An SME that genuinely wants centrally-managed prompts for their own workloads runs their own helexa mesh — we're open source; that's independent of the helexa.ai ecosystem and out of scope here.)
Work
1. Verify the passthrough is actually faithful, per surface × per arch:
/v1/chat/completions—messages[0].role == "system"reaches the neuron chat template intact (multiple system messages? document behaviour)/v1/responses— theinstructionsfield maps to the system slot (andinputitems withrole: system)/v1/messages(Anthropic) — top-levelsystemfield (string AND content-block array forms) survives translation to the OpenAI envelope/no_thinkhandling2. Document it as a platform feature:
First consumer of the documented feature: the chat SPA's own prompt (#178). Related epic: #177.
Status check (2026-08-02): the passthrough works; the deliverables don't exist yet
Tested live against the deployed chain (cortex on hanzalova → benjy), model
helexa/balanced(Qwen3-8B, classic qwen3 dense), using "reply with exactly the word PONG" as a prompt that measurably changes output./v1/chat/completionsmessages[0].role == "system"/v1/responsesinstructions/v1/messagessystemas string/v1/messagessystemas content-block array/v1/chat/completionsstream: trueThe control matters as much as the positives: it shows the chain injects no default of its own, so the observed behaviour comes from the caller's prompt alone.
Multiple system messages (the open question in the checklist): the last one wins. Sending "end with ALPHA" then "end with OMEGA" yields
Hello! OMEGA.— ALPHA absent. That's the behaviour to document; worth deciding whether we want to guarantee it or concatenate instead, since OpenAI clients do sometimes send several.So what's actually outstanding
The functionality is done — this issue is now purely test + document, not build:
cortex-gateway/tests/anthropic.rs::test_anthropic_with_system_promptexists (the Anthropic→OpenAI translation). Nothing pins/v1/chat/completions,/v1/responses, streaming parity, or the multi-system-message rule — all of which I verified by hand above and any of which could regress silently.qwen3_5/qwen3_nextMoE andqwen3.6-VLare untested, and the interaction with/no_thinkhandling differs per family.doc/— the only hit for "system prompt" in the repo docs is an incidental mention indoc/plan/prefix-kv-cache.md. The passthrough guarantee (what helexa will never do to your payload) is stated nowhere user-facing, and nothing tells operators that cortex/router never inject into proxied requests.First consumer #178 is closed, so the feature is in use by the SPA today — undocumented and unpinned by tests.
Verification evidence
The behaviour this issue asks for was already correct — cortex forwards a
caller's system prompt untouched and injects nothing of its own on any
surface. What was missing was anything holding it in place, or telling
anyone it exists. PR #224 supplies both.
Live, through cortex on hanzalova (
:31313), sending"You must reply with exactly the word PONG and nothing else."as thesystem prompt and
"hello there"as the user turn — a deliberatelyodd instruction, so an honoured prompt is unmistakable from a default one:
/v1/chat/completions/v1/responses/v1/messagesqwen3dense)qwen3_5, VL, TP-2)qwen3_next)Streaming and non-streaming both. Controls with no system prompt sent
returned ordinary conversational replies, confirming the PONG is coming
from the caller's instruction and not from something helexa added.
The
qwen3_nextgap, stated plainly. Neither Next variant is warm:both are catalogue-feasible only on beast, where the pinned 27B is
resident and serving
helexa/large. Live-testing them means displacinga production model for a multi-minute 80B cold load, which is out of
proportion to what is being checked here. What makes that acceptable
rather than a hole: the system slot is not arch-branched. Rendering goes
through the shared
chat_template.rspath, applying the template themodel ships in its own
tokenizer_config, so there is noqwen3_next-specific code for a system prompt to fall down. The three new template
tests exercise that shared path directly. Worth a probe next time a Next
model is warm for other reasons; not worth an eviction now.
Regression cover. Live checks prove today, not tomorrow, so the tests
assert on what cortex forwarded upstream, captured from a mock neuron —
not on the reply. A gateway that quietly dropped or replaced the system
prompt would still return a convincing answer, so an assertion on the
response would not catch the regression this is meant to catch. Six cases
in
cortex-gateway/tests/system_prompt.rs, three in neuron'schat_template.rs. The load-bearing one is the negative: nothing isinjected when the caller sends none. If cortex ever grows a default
prompt, house style or safety preamble, that is the test that fails — all
the positive ones would still pass.
Docs. README gains "Tailoring model behaviour" with per-surface curl
examples and the guarantee stated as what helexa will never do to your
payload; CLAUDE.md records it as an operator contract so a future change
that injects into a proxied body reads as breaking rather than as a
feature.
Two behaviours found while verifying, both documented:
decides precedence, and observably the last one wins. cortex does not
editorialise.
/no_thinksuppresses reasoning on chat/completions but not onResponses, where a small
max_output_tokenscan be spent entirely onthe think block — the caller gets
""withstatus: "incomplete".That is a genuine inconsistency between surfaces rather than a system
prompt problem, so it is filed separately as #223; the README carries
the caveat until it is fixed.