first commit
Security: Sync from Public / sync-from-public (push) Has been cancelled
Test: Benchmark Nightly / build (push) Has been cancelled
Test: Benchmark Nightly / Notify Cats on failure (push) Has been cancelled
CI: Python / Checks (push) Has been cancelled
Test: Evals Python / Workflow Comparison Python (push) Has been cancelled
Util: Check Docs URLs / check-docs-urls (push) Has been cancelled
Test: Visual Storybook / Cloudflare Pages (push) Has been cancelled
Test: E2E Performance / build-and-test-performance (push) Has been cancelled
Test: Workflows Nightly / Run Workflow Tests (push) Has been cancelled
Util: Cleanup CI Docker Images / Delete stale CI images (push) Has been cancelled
Test: Benchmark Destroy Env / build (push) Has been cancelled
Util: Update Node Popularity / update-popularity (push) Has been cancelled
Test: E2E Coverage Weekly / Coverage Tests (push) Has been cancelled
Security: Sync from Public / sync-from-public (push) Has been cancelled
Test: Benchmark Nightly / build (push) Has been cancelled
Test: Benchmark Nightly / Notify Cats on failure (push) Has been cancelled
CI: Python / Checks (push) Has been cancelled
Test: Evals Python / Workflow Comparison Python (push) Has been cancelled
Util: Check Docs URLs / check-docs-urls (push) Has been cancelled
Test: Visual Storybook / Cloudflare Pages (push) Has been cancelled
Test: E2E Performance / build-and-test-performance (push) Has been cancelled
Test: Workflows Nightly / Run Workflow Tests (push) Has been cancelled
Util: Cleanup CI Docker Images / Delete stale CI images (push) Has been cancelled
Test: Benchmark Destroy Env / build (push) Has been cancelled
Util: Update Node Popularity / update-popularity (push) Has been cancelled
Test: E2E Coverage Weekly / Coverage Tests (push) Has been cancelled
This commit is contained in:
+92
@@ -0,0 +1,92 @@
|
||||
import type { TokenTextSplitterParams } from '@langchain/textsplitters';
|
||||
import { TextSplitter } from '@langchain/textsplitters';
|
||||
import {
|
||||
hasLongSequentialRepeat,
|
||||
getEncoding,
|
||||
estimateTextSplitsByTokens,
|
||||
} from '@n8n/ai-utilities';
|
||||
import type * as tiktoken from 'js-tiktoken';
|
||||
|
||||
/**
|
||||
* Implementation of splitter which looks at tokens.
|
||||
* This is override of the LangChain TokenTextSplitter
|
||||
* to use the n8n tokenizer utility which uses local JSON encodings
|
||||
*/
|
||||
export class TokenTextSplitter extends TextSplitter implements TokenTextSplitterParams {
|
||||
static lc_name() {
|
||||
return 'TokenTextSplitter';
|
||||
}
|
||||
|
||||
encodingName: tiktoken.TiktokenEncoding;
|
||||
|
||||
allowedSpecial: 'all' | string[];
|
||||
|
||||
disallowedSpecial: 'all' | string[];
|
||||
|
||||
private tokenizer: tiktoken.Tiktoken | undefined;
|
||||
|
||||
constructor(fields?: Partial<TokenTextSplitterParams>) {
|
||||
super(fields);
|
||||
|
||||
this.encodingName = fields?.encodingName ?? 'cl100k_base';
|
||||
this.allowedSpecial = fields?.allowedSpecial ?? [];
|
||||
this.disallowedSpecial = fields?.disallowedSpecial ?? 'all';
|
||||
}
|
||||
|
||||
async splitText(text: string): Promise<string[]> {
|
||||
try {
|
||||
// Validate input
|
||||
if (!text || typeof text !== 'string') {
|
||||
return [];
|
||||
}
|
||||
|
||||
// Check for repetitive content
|
||||
if (hasLongSequentialRepeat(text)) {
|
||||
const splits = estimateTextSplitsByTokens(
|
||||
text,
|
||||
this.chunkSize,
|
||||
this.chunkOverlap,
|
||||
this.encodingName,
|
||||
);
|
||||
return splits;
|
||||
}
|
||||
|
||||
// Use tiktoken for normal text
|
||||
try {
|
||||
this.tokenizer ??= await getEncoding(this.encodingName);
|
||||
|
||||
const splits: string[] = [];
|
||||
const input_ids = this.tokenizer.encode(text, this.allowedSpecial, this.disallowedSpecial);
|
||||
|
||||
let start_idx = 0;
|
||||
let chunkCount = 0;
|
||||
|
||||
while (start_idx < input_ids.length) {
|
||||
if (start_idx > 0) {
|
||||
start_idx = Math.max(0, start_idx - this.chunkOverlap);
|
||||
}
|
||||
const end_idx = Math.min(start_idx + this.chunkSize, input_ids.length);
|
||||
const chunk_ids = input_ids.slice(start_idx, end_idx);
|
||||
|
||||
splits.push(this.tokenizer.decode(chunk_ids));
|
||||
|
||||
chunkCount++;
|
||||
start_idx = end_idx;
|
||||
}
|
||||
|
||||
return splits;
|
||||
} catch (tiktokenError) {
|
||||
// Fall back to character-based splitting if tiktoken fails
|
||||
return estimateTextSplitsByTokens(
|
||||
text,
|
||||
this.chunkSize,
|
||||
this.chunkOverlap,
|
||||
this.encodingName,
|
||||
);
|
||||
}
|
||||
} catch (error) {
|
||||
// Return empty array on complete failure
|
||||
return [];
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user