Retrieved Chunk Deduplicator
knowledge.deduplicate_chunks
Overview
Retrieval from several sources returns the same passage more than once, and paying to embed or prompt with it twice is waste. Send the chunks you retrieved and we hand back the survivors in their original order plus a removal report saying which chunk was a duplicate of which, and how close they were. Three modes: byte-identical text, text identical after normalising case, whitespace, punctuation and Unicode form, or near-duplicate by word-shingle Jaccard overlap against a threshold you choose. You also choose which member of a duplicate group survives — the first, the highest scoring, or the longest. Near-duplicate detection is a threshold judgement rather than a fact, which is why the mode is yours to pick and the similarity comes back with every removal. Reach for data.detect_duplicates instead when you are matching short record fields, where edit distance is the right measure.
Retrieval across several sources returns the same passage repeatedly, and every copy costs tokens in the prompt that follows. Removing them needs a decision about how close counts as the same and which copy to keep, and both belong to you — so both are settings, and every removal comes back with the evidence behind it.
Reliability
BEST_EFFORT, and the reason is worth understanding: byte-identical and normalised matching are facts, but a Jaccard overlap of 0.62 is only a duplicate because you said 0.5 was the line. Every removal therefore carries its similarity so you can audit the threshold you chose. Runs are deterministic — the same chunks, mode and settings always produce the same survivors, ties included, and nothing here reads a clock or a random number. Clustering is a single forward pass: with A similar to B and B similar to C but A not similar to C, A survives, B is removed as a duplicate of A, and C survives, because the only survivor C was measured against was A. Failures, none of which get better on their own: DEDUPE_CHUNKS_INVALID means chunks was empty, an entry was not an object, an id or text was missing or mistyped, a score was not finite, or an entry carried a property we do not accept — fix the entry named in the error and retry; DEDUPE_ID_DUPLICATE means two entries share an id, and the error says which position repeated it; DEDUPE_MODE_UNSUPPORTED means mode, keep, blockBy or normalization.unicodeForm named something that does not exist, and the accepted values come back with the error; DEDUPE_THRESHOLD_REQUIRED means you asked for SHINGLE_JACCARD without a threshold; DEDUPE_THRESHOLD_INVALID means the threshold was outside 0.5 to 1.0, or you sent one under a mode that does not use it; DEDUPE_LIMIT_EXCEEDED means more than 10000 chunks or a chunk text over 200000 characters — deduplicate the set in parts; DEDUPE_COMPARISON_LIMIT_EXCEEDED means the comparison would take more than 2000000 shingle-pair operations, and the error carries the number needed — set blockBy to LENGTH_BUCKET, raise shingleSize, or split the set; DEDUPE_OUTPUT_TOO_LARGE means the survivors could carry more than 1000000 characters of text between them, which we refuse rather than shorten.
Context footprint
Survivors carry their text, so the response is at most the size of the set you sent, capped at 1000000 characters across all of them. The removal report carries ids and numbers only. Both together are bounded by the 10000-chunk ceiling. A successful call costs 1 execution credit.
When to use it
After retrieval and before packing or embedding, when the same passage can reach you more than once.
When you are matching short record fields, when you need the chunks ranked, or when duplicates should be merged rather than dropped.
Powered by Squillify's own implementation of the published Jaccard index over word shingles, with SHA-256 content grouping for the exact modes. No third-party similarity engine and no model is involved.
Scope
Non-goals
- Does not use edit distance — chunk similarity is a set problem, not a field problem
- Does not rank, re-order or score the survivors
- Does not merge two near-duplicates into one chunk
- Does not chain matches transitively across a whole cluster
- Does not fetch, store or index anything
Properties
Capabilities
Cost & billing
Flat rate per invocation
File handling
How we handle your data
What this means
This skill may receive sensitive content and may return submitted, transformed, extracted, or reformatted content.
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 may contain submitted or transformed 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 chunks {id, text} plus optional score and ordinal. |
| mode | string | No | How duplicates are judged; omit it for EXACT. |
| normalization | object | No | Closed flattening applied before NORMALIZED and SHINGLE_JACCARD comparison; omit it for NFKC, collapseWhitespace true, caseFold true and stripPunctuation false. |
| threshold | number | No | Required unless mode is not SHINGLE_JACCARD; Jaccard overlap from 0.5 through 1.0 with no default. |
| shingleSize | integer | No | Words per shingle from 2 through 8; omit it for 5. |
| keep | string | No | Which member of a duplicate group survives; omit it for FIRST. |
| blockBy | string | No | How SHINGLE_JACCARD pairs are blocked; omit it for NONE. |
Output schema
| Field | Type | Required | Description |
|---|---|---|---|
| chunks | array | Yes | Survivors in the relative order you sent them, each a closed {id, text} object with optional score; this is the filtered set, not the candidate list. |
| removed | array | Yes | One closed removal per dropped chunk in arrival order, with ids, mode, optional similarity and reasonCode. |
| summary | object | Yes | Closed counts and settings receipt with inputCount, keptCount, removedCount, mode, optional threshold, shingleSize, keep, blockBy, normalization, comparisons and truncated. |
API contract
POST /v1/executecurl -sS -X POST "$BASE/v1/execute" -H "Authorization: Bearer $KEY" -H 'Content-Type: application/json' -d '{"skillKey":"knowledge.deduplicate_chunks","input":{"chunks":[{"id":"c1","text":"Refunds within 30 days.","score":0.9},{"id":"c2","text":"refunds within 30 days","score":0.4}],"mode":"NORMALIZED","keep":"HIGHEST_SCORE"}}'import { Squillify } from "@squillify/sdk";
const sq = new Squillify({ apiKey: process.env.SQUILLIFY_API_KEY });
const { output } = await sq.execute("knowledge.deduplicate_chunks", {"chunks":[{"text":"We refund an unused subscription within 30 days.","score":0.91,"id":"a"},{"text":"A refund needs the original order id.","score":0.74,"id":"b"},{"text":"We refund an unused subscription within 30 days.","score":0.55,"id":"c"}],"mode":"EXACT"});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. |
| DEDUPE_CHUNKS_INVALID | 400 | No | The request payload failed this skill's input validation. |
| DEDUPE_ID_DUPLICATE | 400 | No | The request payload failed this skill's input validation. |
| DEDUPE_MODE_UNSUPPORTED | 400 | No | The skill does not support the requested variant. |
| DEDUPE_THRESHOLD_REQUIRED | 400 | No | The request payload failed this skill's input validation. |
| DEDUPE_THRESHOLD_INVALID | 400 | No | The request payload failed this skill's input validation. |
| DEDUPE_LIMIT_EXCEEDED | 429 | No | A size, quota, rate, or time limit was exceeded. |
| DEDUPE_COMPARISON_LIMIT_EXCEEDED | 429 | No | A size, quota, rate, or time limit was exceeded. |
| DEDUPE_OUTPUT_TOO_LARGE | 429 | No | A size, quota, rate, or time limit was exceeded. |
Other limits
| Limit | Value |
|---|---|
| Recommended inline bytes | 32 KiB |
Limitations
- Does not use edit distance — chunk similarity is a set problem, not a field problem
- Does not rank, re-order or score the survivors
- Does not merge two near-duplicates into one chunk
- Does not chain matches transitively across a whole cluster
- Does not fetch, store or index anything
- Matching customer records or short field values, where data.detect_duplicates fits
- Deciding which chunks are most relevant, which knowledge.rank_bm25 answers
- Merging duplicates into a single combined chunk
How to call
POST /v1/execute (requires authentication)
{
"skillKey": "knowledge.deduplicate_chunks",
"input": {
"chunks": [
{
"text": "We refund an unused subscription within 30 days.",
"score": 0.91,
"id": "a"
},
{
"text": "A refund needs the original order id.",
"score": 0.74,
"id": "b"
},
{
"text": "We refund an unused subscription within 30 days.",
"score": 0.55,
"id": "c"
}
],
"mode": "EXACT"
}
}Examples
Two byte-identical chunks, the earlier one kept
{
"chunks": [
{
"text": "We refund an unused subscription within 30 days.",
"score": 0.91,
"id": "a"
},
{
"text": "A refund needs the original order id.",
"score": 0.74,
"id": "b"
},
{
"text": "We refund an unused subscription within 30 days.",
"score": 0.55,
"id": "c"
}
],
"mode": "EXACT"
}{
"removed": [
{
"id": "c",
"duplicateOfId": "a",
"mode": "EXACT",
"reasonCode": "EXACT_MATCH"
}
],
"summary": {
"inputCount": 3,
"keptCount": 2,
"removedCount": 1,
"mode": "EXACT",
"shingleSize": 5,
"keep": "FIRST",
"blockBy": "NONE",
"normalization": {
"caseFold": true,
"collapseWhitespace": true,
"stripPunctuation": false,
"unicodeForm": "NFKC"
},
"comparisons": 0,
"truncated": false
},
"chunks": [
{
"id": "a",
"text": "We refund an unused subscription within 30 days.",
"score": 0.91
},
{
"id": "b",
"text": "A refund needs the original order id.",
"score": 0.74
}
]
}Near-duplicates found by word-shingle overlap
{
"chunks": [
{
"id": "first",
"text": "We refund an unused subscription within 30 days of the charge."
},
{
"id": "second",
"text": "We refund an unused subscription within 30 days of the payment."
},
{
"id": "third",
"text": "Partial months are never refunded under any circumstances."
}
],
"threshold": 0.5,
"mode": "SHINGLE_JACCARD",
"keep": "FIRST",
"shingleSize": 3
}{
"removed": [
{
"id": "second",
"duplicateOfId": "first",
"mode": "SHINGLE_JACCARD",
"similarity": 0.8,
"reasonCode": "SIMILARITY_ABOVE_THRESHOLD"
}
],
"summary": {
"inputCount": 3,
"keptCount": 2,
"removedCount": 1,
"mode": "SHINGLE_JACCARD",
"threshold": 0.5,
"shingleSize": 3,
"keep": "FIRST",
"blockBy": "NONE",
"normalization": {
"caseFold": true,
"collapseWhitespace": true,
"stripPunctuation": false,
"unicodeForm": "NFKC"
},
"comparisons": 15,
"truncated": false
},
"chunks": [
{
"id": "first",
"text": "We refund an unused subscription within 30 days of the charge."
},
{
"id": "third",
"text": "Partial months are never refunded under any circumstances."
}
]
}Recommended use cases
- Collapse the same passage returned by two retrievers before packing a prompt
- Drop re-formatted copies of a document that differ only in whitespace and case
- Find near-duplicate chunks in a knowledge base before an embedding run
Avoid for
- Matching customer records or short field values, where data.detect_duplicates fits
- Deciding which chunks are most relevant, which knowledge.rank_bm25 answers
- Merging duplicates into a single combined chunk
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.