Skip to content

Commit da662fd

Browse files
Improved examples and added context history size and compaction
1 parent d7b20c3 commit da662fd

22 files changed

Lines changed: 1089 additions & 28 deletions

README.md

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,8 @@ reasoning:
9090
sampling:
9191
temperature: 0.7
9292
context:
93+
history:
94+
max_items: 10
9395
inputs:
9496
- name: user_message
9597
non_empty:
@@ -125,6 +127,12 @@ const result = await kit.renderPrompt({
125127
user_message: 'How do I reset my password?',
126128
app_context: 'Account settings page',
127129
},
130+
history: [
131+
{ role: 'user', content: 'I am on the account settings page.' },
132+
{ role: 'assistant', content: 'I can help with account access.' },
133+
],
134+
onHistoryCompaction: ({ overflow }) =>
135+
`Earlier conversation summary: ${summarizeConversationUsingLLM(overflow)}`,
128136
});
129137

130138
if (result.returnMessage) {
@@ -168,6 +176,7 @@ Supported values for `warnings.contextSize` are `auto`, `off`, `result-only`, `c
168176
- **Context hardening** — copyable `/pattern/i` regex literals, structured regexes with `return_message`, and built-in `non_empty` / `reject_secrets` validators
169177
- **Optional short-circuit messages** — validators can return a structured `returnMessage` instead of throwing when configured
170178
- **Context size guardrails** — optional per-input `max_size` metadata with non-blocking render-time warnings
179+
- **History preservation** — optional `context.history.max_items` compacts older conversation turns into one preserved history item, with a runtime `onHistoryCompaction` hook for custom summaries
171180
- **Warning controls** — top-level config can suppress or emit context size warnings differently in dev and prod
172181
- **Caching** — LRU cache with mtime-based invalidation
173182
- **CLI** — init, validate, compile, render, inspect, skill
@@ -585,9 +594,10 @@ Renders a prompt for a specific provider. Returns `{ resolved, request?, returnM
585594
| `provider` | `string` | `'openai'`, `'openai-responses'`, `'anthropic'`, `'gemini'`, `'openrouter'` |
586595
| `variables` | `Record<string, string>` | Template variables |
587596
| `onContextOverflow` | `(info) => string` | Optional callback to transform oversized context values before rendering |
597+
| `onHistoryCompaction` | `(info) => string \| { role, content }` | Optional callback to compact overflow history when `context.history.max_items` is exceeded |
588598
| `environment` | `string` | Environment override name |
589599
| `tier` | `string` | Tier override name |
590-
| `history` | `Array<{ role, content }>` | Conversation history |
600+
| `history` | `Array<{ role, content }>` | Conversation history. If the prompt declares `context.history.max_items`, older turns are compacted into one preserved history item before provider rendering. |
591601
| `toolRegistry` | `Record<string, unknown>` | Tool definitions for resolving string tool references |
592602
| `strict` | `boolean` | Fail on missing variables |
593603
| `openaiResponses` | `object` | Optional Responses API extras (`previous_response_id`, `conversation`, `instructions`, `parallel_tool_calls`, `max_tool_calls`, `store`, `metadata`, `include`, `background`) |
@@ -621,7 +631,7 @@ Prompt files use YAML front matter with these fields:
621631
| `provider_options` | `object` | Provider-specific non-portable options (`anthropic`, `gemini`, `openrouter`) |
622632
| `raw` | `object` | Provider-scoped request-body passthrough (`openai`, `openai-responses`, `anthropic`, `gemini`/`google`, `openrouter`) |
623633
| `mcp` | `object` | MCP server references |
624-
| `context` | `object` | `{ inputs, history }` — declare expected variables, with optional per-input `max_size`, `trim`, structured or literal `allow_regex`/`deny_regex`, and built-in `non_empty` / `reject_secrets` validators |
634+
| `context` | `object` | `{ inputs, history }` — declare expected variables, with optional per-input `max_size`, `trim`, structured or literal `allow_regex`/`deny_regex`, built-in `non_empty` / `reject_secrets` validators, and `history.max_items` compaction |
625635
| `includes` | `string[]` | Paths to included prompt files |
626636
| `environments` | `object` | Named environment overrides |
627637
| `tiers` | `object` | Named tier overrides |

SKILL.md

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,7 @@ the fields required by that specific file:
7070
| `raw` | object | no | Provider-scoped request-body passthrough for unmodeled vendor fields |
7171
| `mcp` | object | no | `{ servers: [string | { name, config }] }` |
7272
| `context.inputs` | `Array<string | { name, max_size?, trim?, allow_regex?, deny_regex?, non_empty?, reject_secrets? }>` | no | Declared variable names used in templates, with optional size budgets and runtime hardening controls |
73-
| `context.history` | object | no | `{ max_items: number }` |
73+
| `context.history` | object | no | `{ max_items: number }`; caps rendered history by compacting older turns into one preserved message |
7474
| `includes` | string[] | no | Relative paths to other prompt files to include |
7575
| `environments` | object | no | Per-environment overrides (see Overrides) |
7676
| `tiers` | object | no | Per-tier overrides (see Overrides) |
@@ -131,6 +131,32 @@ If a validator declares `return_message`, `renderPrompt()` returns that message
131131

132132
Malformed `allow_regex` and `deny_regex` values fail during `validate` and `compile`, not just at render time. When regex compilation fails, the error includes the prompt id, variable name, field name, and raw configured value. Double-quoted YAML regex strings with raw backslashes fail as `POK013`; use `/pattern/i`, single-quoted `pattern: '...'`, or doubled backslashes.
133133
134+
### Conversation history limits
135+
136+
Use `context.history.max_items` when a chat-style prompt should bound rendered conversation history:
137+
138+
```yaml
139+
context:
140+
history:
141+
max_items: 10
142+
```
143+
144+
When runtime `history` exceeds `max_items`, PromptOpsKit preserves history by compacting older turns into one synthetic history message and keeping the most recent turns. Callers can provide `onHistoryCompaction` to create a custom summary:
145+
146+
```typescript
147+
const result = await kit.renderPrompt({
148+
path: 'support/reply',
149+
provider: 'openai',
150+
history,
151+
onHistoryCompaction: ({ overflow }) => ({
152+
role: 'user',
153+
content: `Earlier conversation summary: ${summarizeConversationUsingLLM(overflow)}`,
154+
}),
155+
});
156+
```
157+
158+
If no callback is supplied, PromptOpsKit creates a plain text compacted history message. Do not describe `max_items` as dropping history; it preserves overflow through compaction.
159+
134160
Example: this is the minimal valid shape for a prompt that references
135161
`{{ pull_request }}` even when provider/model are inherited from defaults:
136162

docs/api-reference.md

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,8 @@ const result = await kit.renderPrompt({
5555
{ role: 'user', content: 'Hello' },
5656
{ role: 'assistant', content: 'Hi!' },
5757
],
58+
onHistoryCompaction: ({ overflow }) =>
59+
`Earlier conversation summary: ${summarizeConversationUsingLLM(overflow)}`,
5860
strict: false,
5961
});
6062
```
@@ -66,9 +68,10 @@ const result = await kit.renderPrompt({
6668
| `provider` | `string` | `'openai'`, `'openai-responses'`, `'anthropic'`, `'gemini'`, `'openrouter'` (required) |
6769
| `variables` | `Record<string, string>` | Template variables |
6870
| `onContextOverflow` | `(info) => string` | Optional callback to transform an oversized context value before rendering |
71+
| `onHistoryCompaction` | `(info) => string \| { role, content }` | Optional callback used when `context.history.max_items` compacts overflow history |
6972
| `environment` | `string` | Environment override name |
7073
| `tier` | `string` | Tier override name |
71-
| `history` | `Array<{ role, content }>` | Conversation history |
74+
| `history` | `Array<{ role, content }>` | Conversation history. If the prompt declares `context.history.max_items`, overflow history is compacted into one preserved history item before provider rendering. |
7275
| `toolRegistry` | `Record<string, unknown>` | Tool definitions for resolving string tool references |
7376
| `strict` | `boolean` | Fail on missing variables (default `false`) |
7477
| `openaiResponses` | `object` | Optional Responses API extras (`previous_response_id`, `conversation`, `instructions`, `parallel_tool_calls`, `max_tool_calls`, `store`, `metadata`, `include`, `background`) |
@@ -247,7 +250,7 @@ const request = adapter.render(resolvedAsset, {
247250
});
248251
```
249252

250-
`RuntimeRenderOptions` for direct adapter rendering supports `environment`, `tier`, `runtime`, `variables`, `onContextOverflow`, `history`, `toolRegistry`, `strict`, and `openaiResponses`.
253+
`RuntimeRenderOptions` for direct adapter rendering supports `environment`, `tier`, `runtime`, `variables`, `onContextOverflow`, `history`, `onHistoryCompaction`, `toolRegistry`, `strict`, and `openaiResponses`.
251254

252255
Runtime overrides can include the same overridable front matter fields as `environments` and `tiers`, including `raw` provider passthrough blocks. Raw blocks are merged into provider request bodies after normalized fields and provider-specific options.
253256

docs/getting-started.md

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,8 @@ reasoning:
4242
sampling:
4343
temperature: 0.7
4444
context:
45+
history:
46+
max_items: 10
4547
inputs:
4648
- name: user_message
4749
non_empty: true
@@ -77,6 +79,12 @@ const result = await kit.renderPrompt({
7779
user_message: 'How do I reset my password?',
7880
app_context: 'Billing settings page',
7981
},
82+
history: [
83+
{ role: 'user', content: 'I am looking at my account page.' },
84+
{ role: 'assistant', content: 'I can help with account access.' },
85+
],
86+
onHistoryCompaction: ({ overflow }) =>
87+
`Earlier conversation summary: ${summarizeConversationUsingLLM(overflow)}`,
8088
});
8189

8290
if (!result.request) {
@@ -115,6 +123,8 @@ const kit = createPromptOpsKit({
115123

116124
Your application owns the HTTP call — PromptOpsKit produces the request body only.
117125

126+
When `context.history.max_items` is set and runtime history exceeds that count, PromptOpsKit compacts older turns into one preserved history item and keeps the most recent turns. Use `onHistoryCompaction` when your app wants to provide its own summary.
127+
118128
## Validate prompts
119129

120130
```bash

docs/prompt-format.md

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -247,6 +247,34 @@ At render time, PromptOpsKit also emits a non-blocking `POK030` warning when a p
247247

248248
Malformed `allow_regex` and `deny_regex` values fail during `validate` and `compile` with `POK013`, so bad patterns are caught before runtime. Double-quoted YAML regex strings with raw backslashes are also reported as `POK013`; use unquoted `/pattern/i`, single-quoted `pattern: '...'`, or doubled backslashes in double quotes.
249249
250+
### Conversation history
251+
252+
Declare `context.history.max_items` when a prompt should bound rendered conversation history:
253+
254+
```yaml
255+
context:
256+
history:
257+
max_items: 8
258+
```
259+
260+
When runtime `history` has more than `max_items` messages, PromptOpsKit preserves all history by compacting older turns into one synthetic history message, then keeping the most recent turns. The final provider request receives at most `max_items` history items before the current prompt template is added.
261+
262+
Callers can customize the compacted message with `onHistoryCompaction`:
263+
264+
```typescript
265+
const result = await kit.renderPrompt({
266+
path: 'support/reply',
267+
provider: 'openai',
268+
history,
269+
onHistoryCompaction: ({ overflow }) => ({
270+
role: 'user',
271+
content: `Earlier conversation summary: ${summarizeConversationUsingLLM(overflow)}`,
272+
}),
273+
});
274+
```
275+
276+
If no callback is supplied, PromptOpsKit creates a plain text compacted history message. This behavior is provider-agnostic: OpenAI/OpenRouter use `messages`, OpenAI Responses uses `input`, Anthropic uses `messages`, and Gemini maps assistant history to `model`.
277+
250278
Example hardened input definition:
251279

252280
```yaml

docs/providers.md

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -471,6 +471,28 @@ const { request } = result;
471471

472472
History messages are inserted between system instructions and the prompt template in the messages array. For Gemini, role `assistant` is mapped to `model`.
473473

474+
If the prompt declares `context.history.max_items`, provider rendering compacts overflow history before shaping the request. Older turns become one preserved history item, and the most recent turns are kept as-is:
475+
476+
```yaml
477+
context:
478+
history:
479+
max_items: 4
480+
```
481+
482+
```typescript
483+
const result = await kit.renderPrompt({
484+
path: 'chat',
485+
provider: 'openai',
486+
history,
487+
onHistoryCompaction: ({ overflow }) => ({
488+
role: 'user',
489+
content: `Earlier conversation summary: ${summarizeConversationUsingLLM(overflow)}`,
490+
}),
491+
});
492+
```
493+
494+
If no `onHistoryCompaction` callback is supplied, PromptOpsKit creates a plain text compacted history message. The behavior is shared by OpenAI, OpenAI Responses, Anthropic, Gemini, and OpenRouter.
495+
474496
## Tools
475497

476498
Tools defined in front matter are included in the request body. They can be string references or inline definitions:

docs/schema.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -278,7 +278,9 @@ context:
278278
|-------|------|-------------|
279279
| `inputs` | `Array<string | { name, max_size?, trim?, allow_regex?, deny_regex?, non_empty?, reject_secrets? }>` | Expected variable names, optionally with size and runtime sanitization constraints |
280280
| `history` | `object` | History settings |
281-
| `history.max_items` | `number` | Maximum history items |
281+
| `history.max_items` | `number` | Maximum rendered history items. Overflow history is compacted into one preserved message rather than dropped. |
282+
283+
When runtime `history` exceeds `history.max_items`, PromptOpsKit creates one compacted history item for the older overflow and keeps the most recent turns. Applications can pass `onHistoryCompaction` to `renderPrompt()` or direct adapter rendering to produce their own summary message.
282284

283285
String-form inputs remain valid:
284286

0 commit comments

Comments
 (0)