omp-plugin-dynamic-system-prompt
v0.5.0
Published
Append a per-model tuning block to the omp system prompt, scaled by model family, version, and tier.
Maintainers
Readme
omp-plugin-dynamic-system-prompt
Scales system-prompt guidance to the model that will read it — less instruction for frontier models, more for the ones that need it.
An oh-my-pi plugin. It appends one ## Model tuning section to the system prompt on every turn, choosing the directives from the model actually in use.
Why
A prompt written for a weak model wastes tokens and credibility on a strong one. "Don't narrate your chain of thought" is worth saying to a model that does; on a frontier reasoner it is noise the harness already controls through its effort setting.
So the guidance is inverted from the obvious direction: capability buys silence. A frontier flagship gets a light prompt. An older or small-tier model in the same family gets more. An unrecognized model gets the heaviest budget, on the assumption that it needs the most help.
gpt-5.6 -> light (449 chars of 700)
claude-opus-4.5 -> medium (157 chars of 1600)
claude-haiku-4.5 -> heavy (157 chars of 3000)
glm-4.5-air -> heavy (509 chars of 3000)The block never names the model, its version, or the resolved profile. Nothing tells the model it is being profiled.
Install
omp plugin install omp-plugin-dynamic-system-promptIt works with no configuration. Every setting below has a default.
How it works
Three stages, one per module.
model id -> resolve axis -> select rules -> render block
(model-axis) (render) (render)1. Resolve the axis. The model id is normalized (bareModelId, lowercased, with suffixes like :thinking, :free, -high stripped) and classified into three independent dimensions:
| Dimension | Values |
| --------- | --------------------------------------------------------------------------------------- |
| family | openai anthropic gemini glm kimi deepseek grok qwen minimax unknown |
| version | numeric series — gpt-5.6 → 5.6, kimi-k2.7 → 2.7; null when unparseable |
| tier | flagship mid small |
Parsing is delegated to @oh-my-pi/pi-catalog/identity rather than reimplemented, so new model ids are recognized as the host catalog learns them.
2. Derive the weight. weight falls out of how far the version sits behind the family's frontier, plus a penalty for small-tier models. unknown always lands heavy.
| Distance from frontier | flagship / mid | small |
| ---------------------- | -------------- | -------- |
| current generation | light | medium |
| one generation back | medium | heavy |
| older, or unparseable | heavy | heavy |
3. Select and render. Rules whose applies(axis) holds and whose minWeight fits are sorted by priority, reduced to one rule per concern, then trimmed from the lowest priority end until the rendered block fits CHAR_BUDGET[weight] — 700, 1600, or 3000 characters.
When no rule applies, nothing is appended and the prompt is returned untouched. This is the normal outcome for an unrecognized model: it resolves heavy, but every rule gates on a known family, so the block is empty.
Worked examples
| model id | profile | weight | rules | block / budget |
| --------------------- | ---------------------- | ------ | ----- | -------------- |
| gpt-5.6 | openai-5.6-flagship | light | 2 | 449 / 700 |
| claude-opus-5 | anthropic-5-flagship | light | 1 | 267 / 700 |
| claude-opus-4.5 | anthropic-4.5-flagship | medium | 1 | 157 / 1600 |
| claude-haiku-4.5 | anthropic-4.5-small | heavy | 1 | 157 / 3000 |
| gemini-3-pro | gemini-3-flagship | medium | 1 | 222 / 1600 |
| deepseek-v4-pro | deepseek-4-flagship | light | 1 | 239 / 700 |
| glm-4.5-air | glm-4.5-mid | heavy | 2 | 509 / 3000 |
| kimi-k2 | kimi-2-flagship | heavy | 2 | 449 / 3000 |
| grok-3-mini | grok-3-small | heavy | 1 | 260 / 3000 |
| some-unlisted-model | unknown-flagship | heavy | 0 | 0 / 3000 |
What glm-4.5-air actually receives:
## Model tuning
Commit to one reading and act on it: once you settle an interpretation of the
request or an instruction, reopen it only when a tool result contradicts it.
"Wait, let me reconsider" loops over the same evidence add no information —
decide, verify with a cheap tool call, and move on.
When the answer is already in context, return it. Skip filler verification
language ("let me confirm again", "to be sure", "just to double-check") and do
not re-derive facts you established earlier this turn.Settings
Configurable in omp's settings UI, in ~/.omp/plugins/omp-plugins.lock.json, or by environment variable. The lockfile wins over the environment.
| Setting | Env | Values | Default | Effect |
| ------------ | ------------------------- | ------------------------------------- | ------- | ------------------------------------------------------------- |
| enabled | OMP_MODEL_TUNING | boolean | true | Append the block at all. |
| weight | OMP_MODEL_TUNING_WEIGHT | auto light medium heavy off | auto | Override the derived weight. off disables the plugin. |
| showStatus | OMP_MODEL_TUNING_STATUS | boolean | true | Show the tuning weight and injection state in the omp footer. |
With showStatus on, omp's footer carries one line: ● tuning heavy when the block was appended this turn, ○ tuning heavy when the resolved model matched no rule and nothing was appended. omp strips colour from extension status text, so the symbol is the whole signal. Switching models with /model updates it on the next turn, not immediately — omp emits no model_select event, so the axis is resolved when the turn starts.
Rules
Eleven rules across seven concerns. Only one rule per concern ever ships, so priority decides which of several competing directives a given model sees.
| id | concern | priority | min weight |
| ------------------------ | -------------- | -------- | ---------- |
| verify-once | verification | 8 | light |
| stop-when-green | completion | 9 | light |
| settled-reading | deliberation | 10 | light |
| reasoning-aim | deliberation | 11 | light |
| effort-calibration | deliberation | 12 | medium |
| no-inline-cot | deliberation | 13 | medium |
| no-filler-verification | verification | 14 | medium |
| reflect-then-ask | ambiguity | 20 | light |
| scope-literalism | scope | 30 | medium |
| compaction-persistence | context-budget | 40 | medium |
| house-style-counter | aesthetics | 50 | medium |
The table is deliberately sparse. A rule earns its place only on evidence that a family needs the correction and that omp's base prompt does not already make it — gemini, qwen, and minimax are resolved on the axis but carry no rules of their own yet.
Development
bun install
bun test ./test # 64 tests across 6 files
bun x tsc --noEmit -p tsconfig.json # typecheckNo runtime dependencies. Every import is a Node builtin or an omp package resolved from the host.
Two invariants worth knowing before changing anything:
- Append-only. The
before_agent_starthandler never replaces omp's system prompt, and it checks forTUNING_HEADERbefore appending so the block cannot accumulate across turns. - The block is anonymous. No model name, version, or profile string may appear in the rendered output.
AGENTS.md carries the full contract.
License
MIT
