Context Pack Planner
knowledge.context_pack
Overview
Choosing which retrieved chunks earn a place in a prompt is a policy decision, so here you name the policy: by priority then recency, by score, by recency, by priority then score, by rotating through groups so one source cannot monopolise the window, or simply in the order you sent. We order the candidates accordingly, admit them while the whole assembled rendering still fits your budget — counted with the same engine context.pack_chunks uses, so the two can never disagree about what fits — and hand back the plan: which chunks made it, in what position, how many tokens each carries, and for every chunk left out, which rule dropped it. The plan carries no chunk text, because you already hold every chunk you sent. You can also declare chunks that must be admitted, and if they cannot all fit the call fails rather than quietly dropping one.
The three context primitives all refuse to choose: one allocates a budget, one checks a fit, one packs an order you already decided. Deciding which chunks deserve the window is the missing step, and it needs to be a named policy rather than a guess — so the strategy is required, the ordering rules are published, and every chunk left out says which rule left it out.
Reliability
Admission is delegated to the same packing engine context.pack_chunks runs, so the two skills always agree about whether a selection fits a budget. The full rendering is counted at every step rather than summing per-chunk counts, because byte-pair counts are not additive across a join — which is also why a chunk's own tokenCount will not add up to packedTokenCount. Selection is deterministic for identical input on the same tokenizer version, ties included, and nothing here reads a clock or a random number; a tokenizer upgrade can change which chunks fit, which is why tokenizer_version comes back. Failures, none of which get better on their own: PACK_CHUNKS_INVALID means chunks was empty, an entry was not an object, an id or text was missing, a numeric field was out of range, a timestamp was not an ISO-8601 instant, or an entry carried a property we do not accept; PACK_CHUNK_ID_DUPLICATE means two candidates share an id, and the error says which position repeated it; PACK_BUDGET_INVALID means tokenBudget, reservedTokens or maxChunks was outside its range, or reservedTokens was not below tokenBudget; PACK_STRATEGY_UNSUPPORTED means the strategy named does not exist, and the accepted values come back with the error; PACK_STRATEGY_FIELD_MISSING means a candidate does not carry the field the strategy orders by, and the error says which position and which field; PACK_REQUIRED_ID_UNKNOWN means requireIds names an id that is not among your candidates; PACK_REQUIRED_ID_UNFITTABLE means the required chunks cannot all be admitted together — raise tokenBudget, lower reservedTokens or require fewer; PACK_FRAMING_TOO_LARGE means the prefix and suffix alone exceed the available budget, so nothing could be admitted; PACK_LIMIT_EXCEEDED means more than 10000 candidates, a chunk over 200000 characters, an affix over its limit, more than 100 required ids, a projected rendering over 2000000 characters, or — the one that is about cost rather than size — a request whose packing would cost more than 32000000 bytes of token counting. That last one exists because a token count is not additive, so admitting a candidate means counting the whole rendering again and the work is the number of candidates multiplied by how much is already packed. Ten thousand candidates of two hundred characters sits inside every other limit here and still asks for billions of characters of counting; the error carries the projected work, the ceiling, the candidate count and the rendered size, and halving either the count or the size roughly quarters the work. A large candidate set against a small tokenBudget is cheap and stays allowed, because nothing admitted can exceed the budget; PACK_TOKENIZER_UNSUPPORTED means the encoding you named is not one we have, and the supported list comes back with the error.
Context footprint
The response is ids, positions, counts and reasons — never chunk text, whatever the candidates carried. At most 10000 included and excluded entries come back between them, and an affix over 1000 characters is reported by token count instead of being repeated. A successful call costs 1 execution credit.
When to use it
When you have more candidate chunks than budget and want a policy, not a coin flip, to decide what goes in.
When you want the assembled text, when the chunks still need ranking, or when you are partitioning a whole corpus rather than filling one window.
Powered by JTokkit, an open-source tokenizer reproducing OpenAI tiktoken encodings, with Squillify's own selection policies on top of the shared packing engine that context.pack_chunks also uses.
Scope
Non-goals
- Does not return the assembled context or any chunk text
- Does not truncate, summarise or rewrite a chunk to make it fit
- Does not re-rank by relevance — bring your own priority, score or recency
- Does not model provider message framing you have not supplied
- Does not partition a corpus across many requests, which the batch planner does
Properties
Capabilities
Cost & billing
Flat rate per invocation
File handling
How we handle your data
What this means
This skill may receive sensitive content, but it does not target sensitive data and returns only a structured non-sensitive result.
Squillify guarantees
- Runs inside Squillify
- This skill runs inside Squillify.
- Payloads are not sent to an external service by this skill.
- Squillify does not permanently store submitted payloads.
- Telemetry records metadata only, not submitted content.
- Submitted payloads are not used for training.
- Submitted payloads are not reviewed by humans.
- This skill does not target sensitive data.
- The response does not contain submitted content.
Your responsibility
Squillify does not determine whether submitted content is sensitive. Your application is responsible for deciding what data it is allowed to submit. Your application is responsible for securing the response.
Input schema
| Field | Type | Required | Description |
|---|---|---|---|
| chunks | array | Yes | From 1 through 10000 closed candidates {id, text} plus the optional ordering fields the strategy requires. |
| tokenBudget | integer | Yes | Tokens available for packed content including framing, from 1 through 2000000. |
| strategy | string | Yes | Required packing policy with no default. |
| reservedTokens | integer | No | Tokens withheld from the budget for the model's answer; omit it for 0. |
| tokenizer | string | No | Encoding used to count tokens; omit it for cl100k_base. |
| assembly | object | No | Closed framing counted inside the budget: optional prefix, separator and suffix; omit the whole object to add nothing. |
| maxChunks | integer | No | Second ceiling independent of tokens; omit it for 10000 so only the token budget decides. |
| requireIds | array | No | From 0 through 100 chunk ids that must be admitted first; omit it when none are required. |
Output schema
| Field | Type | Required | Description |
|---|---|---|---|
| strategyApplied | string | Yes | Strategy that produced this order, in lower case. |
| included | array | Yes | Admitted chunks in assembly order, each a closed {id, position, tokenCount, reason} object with no chunk text. |
| excluded | array | Yes | Every candidate left out, each a closed {id, reason} object. |
| packedTokenCount | integer | Yes | Token count of the full admitted rendering including framing. |
| remainingTokens | integer | Yes | tokenBudget minus reservedTokens minus packedTokenCount. |
| assembly | object | Yes | Closed framing actually measured: separator always, prefix and suffix text only when not withheld, plus token counts and withheld flags. |
| summary | object | Yes | Closed counts and budget receipt with candidateCount, includedCount, excludedCount, tokenBudget, reservedTokens, availableTokens, maxChunks, tokenizer, tokenizer_version and framingTokens. |
API contract
POST /v1/executecurl -sS -X POST "$BASE/v1/execute" -H "Authorization: Bearer $KEY" -H 'Content-Type: application/json' -d '{"skillKey":"knowledge.context_pack","input":{"chunks":[{"id":"c1","text":"First chunk.","score":0.9},{"id":"c2","text":"Second chunk.","score":0.4}],"tokenBudget":8000,"reservedTokens":1000,"strategy":"SCORE_DESC","assembly":{"separator":"\n\n"}}}'import { Squillify } from "@squillify/sdk";
const sq = new Squillify({ apiKey: process.env.SQUILLIFY_API_KEY });
const { output } = await sq.execute("knowledge.context_pack", {"chunks":[{"priority":1,"text":"Refunds are issued within 30 days of the original charge.","recencyRank":3,"id":"chunk-overview"},{"priority":1,"text":"A refund requires the original order id and cannot cover partial months.","recencyRank":1,"id":"chunk-detail"},{"priority":5,"text":"Shipping estimates are unrelated to the refund window.","recencyRank":2,"id":"chunk-aside"}],"strategy":"PRIORITY_THEN_RECENCY","assembly":{"prefix":"Context:\n","separator":"\n\n"},"reservedTokens":16,"tokenBudget":64});Errors
| Code | HTTP | Retryable | Description |
|---|---|---|---|
| VALIDATION_ERROR | 400 | No | The request payload failed schema validation. |
| UNAUTHORIZED | 401 | No | Missing or invalid API key. |
| SKILL_NOT_ACCESSIBLE | 403 | No | The API key is not granted access to this skill. |
| SKILL_NOT_AVAILABLE_FOR_PLAN | 403 | No | This skill is not included in your current plan. |
| PLAN_ELIGIBILITY_UNAVAILABLE | 503 | Yes | Plan eligibility could not be decided. Retry after backoff. |
| SKILL_NOT_FOUND | 404 | No | The requested skill or version does not exist. |
| TENANT_RATE_LIMITED | 429 | Yes | Request rate limit exceeded. Retry after backoff. |
| QUOTA_EXCEEDED | 429 | Yes | Plan quota exhausted. Retryable once the quota period resets. |
| EXECUTION_TIMEOUT | 504 | Yes | Execution exceeded the allowed time budget. |
| PACK_CHUNKS_INVALID | 400 | No | The request payload failed this skill's input validation. |
| PACK_CHUNK_ID_DUPLICATE | 400 | No | The request payload failed this skill's input validation. |
| PACK_BUDGET_INVALID | 400 | No | The request payload failed this skill's input validation. |
| PACK_STRATEGY_UNSUPPORTED | 400 | No | The skill does not support the requested variant. |
| PACK_STRATEGY_FIELD_MISSING | 412 | No | The input is valid but cannot be processed as-is. |
| PACK_REQUIRED_ID_UNKNOWN | 400 | No | The request payload failed this skill's input validation. |
| PACK_REQUIRED_ID_UNFITTABLE | 412 | No | The input is valid but cannot be processed as-is. |
| PACK_FRAMING_TOO_LARGE | 429 | No | A size, quota, rate, or time limit was exceeded. |
| PACK_LIMIT_EXCEEDED | 429 | No | A size, quota, rate, or time limit was exceeded. |
| PACK_TOKENIZER_UNSUPPORTED | 400 | No | The skill does not support the requested variant. |
Other limits
| Limit | Value |
|---|---|
| Recommended inline bytes | 32 KiB |
Limitations
- Does not return the assembled context or any chunk text
- Does not truncate, summarise or rewrite a chunk to make it fit
- Does not re-rank by relevance — bring your own priority, score or recency
- Does not model provider message framing you have not supplied
- Does not partition a corpus across many requests, which the batch planner does
- Getting the assembled prompt text back — you join your own chunks
- Ranking chunks in the first place, which knowledge.rank_bm25 does
- Splitting a chunk that is too large to admit
How to call
POST /v1/execute (requires authentication)
{
"skillKey": "knowledge.context_pack",
"input": {
"chunks": [
{
"priority": 1,
"text": "Refunds are issued within 30 days of the original charge.",
"recencyRank": 3,
"id": "chunk-overview"
},
{
"priority": 1,
"text": "A refund requires the original order id and cannot cover partial months.",
"recencyRank": 1,
"id": "chunk-detail"
},
{
"priority": 5,
"text": "Shipping estimates are unrelated to the refund window.",
"recencyRank": 2,
"id": "chunk-aside"
}
],
"strategy": "PRIORITY_THEN_RECENCY",
"assembly": {
"prefix": "Context:\n",
"separator": "\n\n"
},
"reservedTokens": 16,
"tokenBudget": 64
}
}Examples
Priority first, ties broken by recency, with a reserved answer budget
{
"chunks": [
{
"priority": 1,
"text": "Refunds are issued within 30 days of the original charge.",
"recencyRank": 3,
"id": "chunk-overview"
},
{
"priority": 1,
"text": "A refund requires the original order id and cannot cover partial months.",
"recencyRank": 1,
"id": "chunk-detail"
},
{
"priority": 5,
"text": "Shipping estimates are unrelated to the refund window.",
"recencyRank": 2,
"id": "chunk-aside"
}
],
"strategy": "PRIORITY_THEN_RECENCY",
"assembly": {
"prefix": "Context:\n",
"separator": "\n\n"
},
"reservedTokens": 16,
"tokenBudget": 64
}{
"assembly": {
"prefix": "Context:\n",
"prefixTokens": 2,
"prefixWithheld": false,
"separator": "\n\n",
"separatorTokens": 1,
"suffix": "",
"suffixTokens": 0,
"suffixWithheld": false
},
"packedTokenCount": 37,
"excluded": [],
"remainingTokens": 11,
"strategyApplied": "priority_then_recency",
"summary": {
"candidateCount": 3,
"includedCount": 3,
"excludedCount": 0,
"tokenBudget": 64,
"reservedTokens": 16,
"availableTokens": 48,
"maxChunks": 10000,
"tokenizer": "cl100k_base",
"tokenizer_version": "jtokkit-1.1.0",
"framingTokens": 2
},
"included": [
{
"id": "chunk-detail",
"position": 0,
"tokenCount": 13,
"reason": "highest_priority"
},
{
"id": "chunk-overview",
"position": 1,
"tokenCount": 13,
"reason": "highest_priority"
},
{
"id": "chunk-aside",
"position": 2,
"tokenCount": 9,
"reason": "highest_priority"
}
]
}Groups rotated so one source cannot fill the window, with a count ceiling
{
"chunks": [
{
"text": "Refund window is 30 days.",
"group": "wiki",
"id": "wiki-1"
},
{
"text": "Refunds need an order id.",
"group": "wiki",
"id": "wiki-2"
},
{
"text": "Partial months are excluded.",
"group": "wiki",
"id": "wiki-3"
},
{
"text": "A customer asked about a late refund.",
"group": "tickets",
"id": "tickets-1"
}
],
"tokenBudget": 40,
"maxChunks": 3,
"strategy": "ROUND_ROBIN_BY_GROUP"
}{
"assembly": {
"prefix": "",
"prefixTokens": 0,
"prefixWithheld": false,
"separator": "",
"separatorTokens": 0,
"suffix": "",
"suffixTokens": 0,
"suffixWithheld": false
},
"packedTokenCount": 21,
"excluded": [
{
"id": "wiki-3",
"reason": "max_chunks_reached"
}
],
"remainingTokens": 19,
"strategyApplied": "round_robin_by_group",
"summary": {
"candidateCount": 4,
"includedCount": 3,
"excludedCount": 1,
"tokenBudget": 40,
"reservedTokens": 0,
"availableTokens": 40,
"maxChunks": 3,
"tokenizer": "cl100k_base",
"tokenizer_version": "jtokkit-1.1.0",
"framingTokens": 0
},
"included": [
{
"id": "wiki-1",
"position": 0,
"tokenCount": 8,
"reason": "group_rotation"
},
{
"id": "tickets-1",
"position": 1,
"tokenCount": 8,
"reason": "group_rotation"
},
{
"id": "wiki-2",
"position": 2,
"tokenCount": 7,
"reason": "group_rotation"
}
]
}Recommended use cases
- Fill a prompt from ranked retrieval results while reserving room for the answer
- Keep several retrievers represented in one window instead of letting one dominate
- Explain to a reviewer why a particular chunk did not make it into a prompt
Avoid for
- Getting the assembled prompt text back — you join your own chunks
- Ranking chunks in the first place, which knowledge.rank_bm25 does
- Splitting a chunk that is too large to admit
Related skills
Composable with
Safety & privacy
- Runs without calling any external service.
- Your payloads are never stored permanently.
- Telemetry captures metadata only — never your payload contents.
- We never train models on your payloads.
- No human reviews your payloads.