@silmaril-security/sdk 0.1.3 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +271 -68
- package/dist/index.cjs +204 -81
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +21 -12
- package/dist/index.d.ts +21 -12
- package/dist/index.js +201 -78
- package/dist/index.js.map +1 -1
- package/package.json +5 -7
package/dist/index.js
CHANGED
|
@@ -9,6 +9,22 @@ var __export = (target, all) => {
|
|
|
9
9
|
};
|
|
10
10
|
|
|
11
11
|
// src/exceptions.ts
|
|
12
|
+
function parseErrorBody(body) {
|
|
13
|
+
try {
|
|
14
|
+
const parsed = JSON.parse(body);
|
|
15
|
+
if (!parsed || typeof parsed !== "object") {
|
|
16
|
+
return { details: void 0, error: void 0, apiMessage: void 0 };
|
|
17
|
+
}
|
|
18
|
+
const data = parsed;
|
|
19
|
+
return {
|
|
20
|
+
details: data.details && typeof data.details === "object" ? data.details : void 0,
|
|
21
|
+
error: typeof data.error === "string" ? data.error : void 0,
|
|
22
|
+
apiMessage: typeof data.message === "string" ? data.message : void 0
|
|
23
|
+
};
|
|
24
|
+
} catch {
|
|
25
|
+
return { details: void 0, error: void 0, apiMessage: void 0 };
|
|
26
|
+
}
|
|
27
|
+
}
|
|
12
28
|
var MAX_PROMPT_DISPLAY_LEN, PromptBlockedException, SilmarilApiError;
|
|
13
29
|
var init_exceptions = __esm({
|
|
14
30
|
"src/exceptions.ts"() {
|
|
@@ -37,12 +53,20 @@ var init_exceptions = __esm({
|
|
|
37
53
|
status;
|
|
38
54
|
statusText;
|
|
39
55
|
body;
|
|
56
|
+
details;
|
|
57
|
+
error;
|
|
58
|
+
apiMessage;
|
|
40
59
|
constructor(params) {
|
|
41
|
-
|
|
60
|
+
const statusText = params.statusText ? ` ${params.statusText}` : "";
|
|
61
|
+
super(`Silmaril API error ${params.status}${statusText}`);
|
|
42
62
|
this.name = "SilmarilApiError";
|
|
43
63
|
this.status = params.status;
|
|
44
64
|
this.statusText = params.statusText;
|
|
45
65
|
this.body = params.body;
|
|
66
|
+
const parsed = parseErrorBody(params.body);
|
|
67
|
+
this.details = params.details ?? parsed.details;
|
|
68
|
+
this.error = params.error ?? parsed.error;
|
|
69
|
+
this.apiMessage = params.apiMessage ?? parsed.apiMessage;
|
|
46
70
|
Object.setPrototypeOf(this, _SilmarilApiError.prototype);
|
|
47
71
|
}
|
|
48
72
|
};
|
|
@@ -78,7 +102,7 @@ function prependToolName(text, toolName) {
|
|
|
78
102
|
}
|
|
79
103
|
return `[TOOL:${toolName}] ${text}`;
|
|
80
104
|
}
|
|
81
|
-
var HookLabel, FirewallHook, DEFAULT_HOOKS, INPUT_HOOKS, OUTPUT_HOOKS, ALL_HOOKS, FIREWALL_HOOK_TO_LABEL,
|
|
105
|
+
var HookLabel, FirewallHook, DEFAULT_HOOKS, INPUT_HOOKS, OUTPUT_HOOKS, ALL_HOOKS, FIREWALL_HOOK_TO_LABEL, FIREWALL_HOOK_VALUES;
|
|
82
106
|
var init_hooks = __esm({
|
|
83
107
|
"src/hooks.ts"() {
|
|
84
108
|
"use strict";
|
|
@@ -127,14 +151,6 @@ var init_hooks = __esm({
|
|
|
127
151
|
[FirewallHook.RETRIEVER_END]: HookLabel.TOOL_RESPONSE,
|
|
128
152
|
[FirewallHook.LLM_END]: HookLabel.LLM_OUTPUT
|
|
129
153
|
};
|
|
130
|
-
DEFAULT_HOOK_THRESHOLDS = {
|
|
131
|
-
[HookLabel.USER_INPUT]: 0.5,
|
|
132
|
-
[HookLabel.SYSTEM_PROMPT]: 0.5,
|
|
133
|
-
[HookLabel.TOOL_CALL]: 0.5,
|
|
134
|
-
[HookLabel.TOOL_RESPONSE]: 0.5,
|
|
135
|
-
[HookLabel.LLM_OUTPUT]: 0.5,
|
|
136
|
-
[HookLabel.UNKNOWN]: 0.5
|
|
137
|
-
};
|
|
138
154
|
FIREWALL_HOOK_VALUES = new Set(Object.values(FirewallHook));
|
|
139
155
|
}
|
|
140
156
|
});
|
|
@@ -221,19 +237,8 @@ async function createLangChainHandler(firewall, options = {}) {
|
|
|
221
237
|
const logger = options.logger ?? ((message, error) => {
|
|
222
238
|
console.warn(`silmaril.firewall: ${message}`, error);
|
|
223
239
|
});
|
|
224
|
-
const overrideThreshold = options.threshold;
|
|
225
|
-
const overrideHookThresholds = options.hookThresholds ?? {};
|
|
226
240
|
const shadowMode = options.shadowMode ?? firewall.shadowMode;
|
|
227
241
|
const onClassify = options.onClassify;
|
|
228
|
-
const effectiveThreshold = (label) => {
|
|
229
|
-
if (overrideHookThresholds[label] !== void 0) {
|
|
230
|
-
return overrideHookThresholds[label];
|
|
231
|
-
}
|
|
232
|
-
if (firewall.hookThresholds[label] !== void 0) {
|
|
233
|
-
return firewall.hookThresholds[label];
|
|
234
|
-
}
|
|
235
|
-
return overrideThreshold ?? firewall.threshold;
|
|
236
|
-
};
|
|
237
242
|
const fireOnClassify = (hookLabel, text, result, blocked, toolName) => {
|
|
238
243
|
if (!onClassify) {
|
|
239
244
|
return;
|
|
@@ -265,7 +270,7 @@ async function createLangChainHandler(firewall, options = {}) {
|
|
|
265
270
|
logger("classification failed, allowing prompt through", err);
|
|
266
271
|
return;
|
|
267
272
|
}
|
|
268
|
-
const threshold =
|
|
273
|
+
const threshold = result.threshold;
|
|
269
274
|
const blocked = result.score >= threshold;
|
|
270
275
|
fireOnClassify(hookLabel, text, result, blocked, toolName);
|
|
271
276
|
if (blocked && !shadowMode) {
|
|
@@ -438,18 +443,7 @@ function findLastUserMessage(messages) {
|
|
|
438
443
|
function createMiddleware(firewall, options = {}) {
|
|
439
444
|
const scanInput = options.scanInput ?? true;
|
|
440
445
|
const scanOutput = options.scanOutput ?? false;
|
|
441
|
-
const overrideThreshold = options.threshold;
|
|
442
|
-
const overrideHookThresholds = options.hookThresholds ?? {};
|
|
443
446
|
const shadowMode = options.shadowMode ?? firewall.shadowMode;
|
|
444
|
-
const effectiveThreshold = (label) => {
|
|
445
|
-
if (overrideHookThresholds[label] !== void 0) {
|
|
446
|
-
return overrideHookThresholds[label];
|
|
447
|
-
}
|
|
448
|
-
if (firewall.hookThresholds[label] !== void 0) {
|
|
449
|
-
return firewall.hookThresholds[label];
|
|
450
|
-
}
|
|
451
|
-
return overrideThreshold ?? firewall.threshold;
|
|
452
|
-
};
|
|
453
447
|
const classifyOrBlock = async (text, hook, toolName) => {
|
|
454
448
|
if (!text.trim()) {
|
|
455
449
|
return;
|
|
@@ -458,7 +452,7 @@ function createMiddleware(firewall, options = {}) {
|
|
|
458
452
|
text,
|
|
459
453
|
toolName !== void 0 ? { hook, toolName } : { hook }
|
|
460
454
|
);
|
|
461
|
-
const threshold =
|
|
455
|
+
const threshold = result.threshold;
|
|
462
456
|
const blocked = result.score >= threshold;
|
|
463
457
|
options.onClassify?.({
|
|
464
458
|
hook,
|
|
@@ -581,7 +575,45 @@ var MAX_INPUT_CHARS = MAX_INPUT_TOKENS * CHARS_PER_TOKEN;
|
|
|
581
575
|
var CHUNK_WINDOW_CHARS = CHUNK_WINDOW * CHARS_PER_TOKEN;
|
|
582
576
|
var CHUNK_OVERLAP_CHARS = CHUNK_OVERLAP * CHARS_PER_TOKEN;
|
|
583
577
|
var CHUNK_STRIDE_CHARS = CHUNK_WINDOW_CHARS - CHUNK_OVERLAP_CHARS;
|
|
578
|
+
function isHighSurrogate(code) {
|
|
579
|
+
return code >= 55296 && code <= 56319;
|
|
580
|
+
}
|
|
581
|
+
function isLowSurrogate(code) {
|
|
582
|
+
return code >= 56320 && code <= 57343;
|
|
583
|
+
}
|
|
584
|
+
function sanitizeText(text) {
|
|
585
|
+
let out = "";
|
|
586
|
+
for (let i = 0; i < text.length; i++) {
|
|
587
|
+
const code = text.charCodeAt(i);
|
|
588
|
+
if (isHighSurrogate(code)) {
|
|
589
|
+
if (i + 1 < text.length && isLowSurrogate(text.charCodeAt(i + 1))) {
|
|
590
|
+
out += text[i];
|
|
591
|
+
out += text[i + 1];
|
|
592
|
+
i++;
|
|
593
|
+
}
|
|
594
|
+
continue;
|
|
595
|
+
}
|
|
596
|
+
if (isLowSurrogate(code)) {
|
|
597
|
+
continue;
|
|
598
|
+
}
|
|
599
|
+
out += text[i];
|
|
600
|
+
}
|
|
601
|
+
return out;
|
|
602
|
+
}
|
|
603
|
+
function safeChunkStart(text, start) {
|
|
604
|
+
if (start > 0 && start < text.length && isLowSurrogate(text.charCodeAt(start)) && isHighSurrogate(text.charCodeAt(start - 1))) {
|
|
605
|
+
return start + 1;
|
|
606
|
+
}
|
|
607
|
+
return start;
|
|
608
|
+
}
|
|
609
|
+
function safeChunkEnd(text, end) {
|
|
610
|
+
if (end > 0 && end < text.length && isHighSurrogate(text.charCodeAt(end - 1)) && isLowSurrogate(text.charCodeAt(end))) {
|
|
611
|
+
return end - 1;
|
|
612
|
+
}
|
|
613
|
+
return end;
|
|
614
|
+
}
|
|
584
615
|
function chunkText(text) {
|
|
616
|
+
text = sanitizeText(text);
|
|
585
617
|
const n = text.length;
|
|
586
618
|
if (n > MAX_INPUT_CHARS) {
|
|
587
619
|
throw new Error(
|
|
@@ -593,8 +625,12 @@ function chunkText(text) {
|
|
|
593
625
|
}
|
|
594
626
|
const chunks = [];
|
|
595
627
|
for (let start = 0; start < n; start += CHUNK_STRIDE_CHARS) {
|
|
596
|
-
const
|
|
597
|
-
|
|
628
|
+
const safeStart = safeChunkStart(text, start);
|
|
629
|
+
if (safeStart >= n) {
|
|
630
|
+
break;
|
|
631
|
+
}
|
|
632
|
+
const end = safeChunkEnd(text, safeStart + CHUNK_WINDOW_CHARS);
|
|
633
|
+
chunks.push(text.slice(safeStart, end));
|
|
598
634
|
if (end >= n) {
|
|
599
635
|
break;
|
|
600
636
|
}
|
|
@@ -604,14 +640,33 @@ function chunkText(text) {
|
|
|
604
640
|
|
|
605
641
|
// src/firewall.ts
|
|
606
642
|
init_exceptions();
|
|
607
|
-
var
|
|
643
|
+
var BASE_THRESHOLD = 0.5;
|
|
644
|
+
var TARGET_SEQUENCE_FPR = 0.01;
|
|
645
|
+
var MAX_ADAPTIVE_THRESHOLD = 0.9;
|
|
608
646
|
var DEFAULT_TIMEOUT_MS = 1e4;
|
|
647
|
+
var DEFAULT_CHUNK_CONCURRENCY = 8;
|
|
609
648
|
var DEFAULT_MAX_RETRIES = 5;
|
|
610
649
|
var MAX_BACKOFF_SECONDS = 30;
|
|
611
|
-
|
|
650
|
+
var MAX_ERROR_BODY_BYTES = 1 << 16;
|
|
651
|
+
function adaptiveThreshold(scoringOpportunityCount) {
|
|
652
|
+
if (!Number.isInteger(scoringOpportunityCount) || scoringOpportunityCount < 1) {
|
|
653
|
+
throw new Error(
|
|
654
|
+
`Firewall: scoringOpportunityCount must be an integer >= 1, got ${scoringOpportunityCount}`
|
|
655
|
+
);
|
|
656
|
+
}
|
|
657
|
+
if (scoringOpportunityCount === 1) {
|
|
658
|
+
return BASE_THRESHOLD;
|
|
659
|
+
}
|
|
660
|
+
const targetChunkFpr = 1 - Math.pow(1 - TARGET_SEQUENCE_FPR, 1 / scoringOpportunityCount);
|
|
661
|
+
const oddsRatio = TARGET_SEQUENCE_FPR / targetChunkFpr;
|
|
662
|
+
const rawThreshold = oddsRatio / (1 + oddsRatio);
|
|
663
|
+
return Math.min(rawThreshold, MAX_ADAPTIVE_THRESHOLD);
|
|
664
|
+
}
|
|
665
|
+
function blockResultFromResponse(data, threshold) {
|
|
612
666
|
const result = {
|
|
613
667
|
prediction: data.prediction,
|
|
614
|
-
score: Number(data.score)
|
|
668
|
+
score: Number(data.score),
|
|
669
|
+
threshold
|
|
615
670
|
};
|
|
616
671
|
if (data.primary_outcome !== void 0) {
|
|
617
672
|
result.primaryOutcome = data.primary_outcome;
|
|
@@ -633,12 +688,73 @@ function blockResultFromResponse(data) {
|
|
|
633
688
|
}
|
|
634
689
|
return Object.freeze(result);
|
|
635
690
|
}
|
|
691
|
+
async function readCappedErrorBody(response) {
|
|
692
|
+
if (!response.body) {
|
|
693
|
+
return response.text().then((body2) => body2.slice(0, MAX_ERROR_BODY_BYTES)).catch(() => "");
|
|
694
|
+
}
|
|
695
|
+
const reader = response.body.getReader();
|
|
696
|
+
const chunks = [];
|
|
697
|
+
let remaining = MAX_ERROR_BODY_BYTES;
|
|
698
|
+
try {
|
|
699
|
+
while (remaining > 0) {
|
|
700
|
+
const { done, value } = await reader.read();
|
|
701
|
+
if (done) {
|
|
702
|
+
break;
|
|
703
|
+
}
|
|
704
|
+
if (!value) {
|
|
705
|
+
continue;
|
|
706
|
+
}
|
|
707
|
+
const chunk = value.byteLength > remaining ? value.slice(0, remaining) : value;
|
|
708
|
+
chunks.push(chunk);
|
|
709
|
+
remaining -= chunk.byteLength;
|
|
710
|
+
if (chunk.byteLength < value.byteLength) {
|
|
711
|
+
break;
|
|
712
|
+
}
|
|
713
|
+
}
|
|
714
|
+
} catch {
|
|
715
|
+
return "";
|
|
716
|
+
} finally {
|
|
717
|
+
await reader.cancel().catch(() => void 0);
|
|
718
|
+
}
|
|
719
|
+
const total = chunks.reduce((sum, chunk) => sum + chunk.byteLength, 0);
|
|
720
|
+
const body = new Uint8Array(total);
|
|
721
|
+
let offset = 0;
|
|
722
|
+
for (const chunk of chunks) {
|
|
723
|
+
body.set(chunk, offset);
|
|
724
|
+
offset += chunk.byteLength;
|
|
725
|
+
}
|
|
726
|
+
return new TextDecoder().decode(body);
|
|
727
|
+
}
|
|
728
|
+
async function mapWithConcurrency(items, concurrency, task) {
|
|
729
|
+
const results = new Array(items.length);
|
|
730
|
+
let nextIndex = 0;
|
|
731
|
+
let firstError;
|
|
732
|
+
const workerCount = Math.min(concurrency, items.length);
|
|
733
|
+
async function worker() {
|
|
734
|
+
for (; ; ) {
|
|
735
|
+
const index = nextIndex;
|
|
736
|
+
nextIndex += 1;
|
|
737
|
+
if (index >= items.length) {
|
|
738
|
+
return;
|
|
739
|
+
}
|
|
740
|
+
try {
|
|
741
|
+
results[index] = await task(items[index], index);
|
|
742
|
+
} catch (err) {
|
|
743
|
+
firstError ??= err;
|
|
744
|
+
}
|
|
745
|
+
}
|
|
746
|
+
}
|
|
747
|
+
await Promise.all(Array.from({ length: workerCount }, () => worker()));
|
|
748
|
+
if (firstError !== void 0) {
|
|
749
|
+
throw firstError;
|
|
750
|
+
}
|
|
751
|
+
return results;
|
|
752
|
+
}
|
|
636
753
|
var Firewall = class {
|
|
637
754
|
apiKey;
|
|
638
755
|
apiUrl;
|
|
639
|
-
threshold;
|
|
640
756
|
timeoutMs;
|
|
641
|
-
|
|
757
|
+
chunkConcurrency;
|
|
642
758
|
shadowMode;
|
|
643
759
|
headers;
|
|
644
760
|
constructor(options) {
|
|
@@ -650,9 +766,16 @@ var Firewall = class {
|
|
|
650
766
|
}
|
|
651
767
|
this.apiKey = options.apiKey;
|
|
652
768
|
this.apiUrl = options.apiUrl;
|
|
653
|
-
this.threshold = options.threshold ?? DEFAULT_THRESHOLD;
|
|
654
769
|
this.timeoutMs = options.timeoutMs ?? DEFAULT_TIMEOUT_MS;
|
|
655
|
-
this.
|
|
770
|
+
if (typeof this.timeoutMs !== "number" || !Number.isFinite(this.timeoutMs) || this.timeoutMs < 0) {
|
|
771
|
+
throw new Error(`Firewall: timeoutMs must be a finite non-negative number, got ${this.timeoutMs}`);
|
|
772
|
+
}
|
|
773
|
+
this.chunkConcurrency = options.chunkConcurrency ?? DEFAULT_CHUNK_CONCURRENCY;
|
|
774
|
+
if (!Number.isInteger(this.chunkConcurrency) || this.chunkConcurrency < 1) {
|
|
775
|
+
throw new Error(
|
|
776
|
+
`Firewall: chunkConcurrency must be an integer >= 1, got ${this.chunkConcurrency}`
|
|
777
|
+
);
|
|
778
|
+
}
|
|
656
779
|
this.shadowMode = options.shadowMode ?? false;
|
|
657
780
|
this.headers = Object.freeze({
|
|
658
781
|
"x-api-key": this.apiKey,
|
|
@@ -661,34 +784,21 @@ var Firewall = class {
|
|
|
661
784
|
}
|
|
662
785
|
async classify(text, options = {}) {
|
|
663
786
|
const chunks = chunkText(text);
|
|
787
|
+
const threshold = adaptiveThreshold(chunks.length);
|
|
664
788
|
if (chunks.length === 1) {
|
|
665
|
-
|
|
666
|
-
if (options.hook !== void 0) {
|
|
667
|
-
payload.hook = options.hook;
|
|
668
|
-
}
|
|
669
|
-
if (options.toolName !== void 0) {
|
|
670
|
-
payload.tool_name = options.toolName;
|
|
671
|
-
}
|
|
672
|
-
if (options.metadata !== void 0) {
|
|
673
|
-
payload.metadata = options.metadata;
|
|
674
|
-
}
|
|
675
|
-
const data = await this.postWithRetry(payload);
|
|
676
|
-
return blockResultFromResponse(data);
|
|
789
|
+
return this.classifySingleChunk(chunks[0], options, threshold);
|
|
677
790
|
}
|
|
678
|
-
const
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
batchOpts.toolNames = chunks.map(() => options.toolName);
|
|
684
|
-
}
|
|
685
|
-
if (options.metadata !== void 0) {
|
|
686
|
-
batchOpts.metadata = chunks.map(() => options.metadata);
|
|
687
|
-
}
|
|
688
|
-
const results = await this.classifyBatch(chunks, batchOpts);
|
|
791
|
+
const results = await mapWithConcurrency(
|
|
792
|
+
chunks,
|
|
793
|
+
this.chunkConcurrency,
|
|
794
|
+
(chunk) => this.classifySingleChunk(chunk, options, threshold)
|
|
795
|
+
);
|
|
689
796
|
return results.reduce((best, r) => r.score > best.score ? r : best);
|
|
690
797
|
}
|
|
691
798
|
async classifyBatch(texts, options = {}) {
|
|
799
|
+
if (texts.length === 0) {
|
|
800
|
+
throw new Error("Firewall: texts must not be empty");
|
|
801
|
+
}
|
|
692
802
|
if (options.hooks !== void 0 && options.hooks.length !== texts.length) {
|
|
693
803
|
throw new Error(
|
|
694
804
|
`Firewall: hooks length ${options.hooks.length} does not match texts length ${texts.length}`
|
|
@@ -704,7 +814,11 @@ var Firewall = class {
|
|
|
704
814
|
`Firewall: metadata length ${options.metadata.length} does not match texts length ${texts.length}`
|
|
705
815
|
);
|
|
706
816
|
}
|
|
707
|
-
const
|
|
817
|
+
const threshold = adaptiveThreshold(texts.length);
|
|
818
|
+
const payload = {
|
|
819
|
+
texts: texts.map((text) => sanitizeText(text)),
|
|
820
|
+
threshold
|
|
821
|
+
};
|
|
708
822
|
if (options.hooks && options.hooks.length > 0) {
|
|
709
823
|
payload.hooks = options.hooks.map((h) => String(h));
|
|
710
824
|
}
|
|
@@ -715,7 +829,7 @@ var Firewall = class {
|
|
|
715
829
|
payload.metadata = options.metadata.map((m) => m === void 0 ? null : m);
|
|
716
830
|
}
|
|
717
831
|
const data = await this.postWithRetry(payload);
|
|
718
|
-
return data.predictions.map((p) => blockResultFromResponse(p));
|
|
832
|
+
return data.predictions.map((p) => blockResultFromResponse(p, threshold));
|
|
719
833
|
}
|
|
720
834
|
asLangChainHandler(options = {}) {
|
|
721
835
|
return Promise.resolve().then(() => (init_langchain(), langchain_exports)).then((m) => m.createLangChainHandler(this, options));
|
|
@@ -723,23 +837,18 @@ var Firewall = class {
|
|
|
723
837
|
asMiddleware(options = {}) {
|
|
724
838
|
return createMiddleware(this, options);
|
|
725
839
|
}
|
|
726
|
-
effectiveThreshold(hook) {
|
|
727
|
-
if (hook === void 0) {
|
|
728
|
-
return this.threshold;
|
|
729
|
-
}
|
|
730
|
-
return this.hookThresholds[hook] ?? this.threshold;
|
|
731
|
-
}
|
|
732
840
|
async postWithRetry(payload, maxRetries = DEFAULT_MAX_RETRIES) {
|
|
733
841
|
for (let attempt = 0; attempt <= maxRetries; attempt++) {
|
|
734
842
|
const response = await fetch(this.apiUrl, {
|
|
735
843
|
method: "POST",
|
|
736
844
|
headers: this.headers,
|
|
737
845
|
body: JSON.stringify(payload),
|
|
846
|
+
redirect: "error",
|
|
738
847
|
signal: AbortSignal.timeout(this.timeoutMs)
|
|
739
848
|
});
|
|
740
849
|
if (response.status !== 429 || attempt === maxRetries) {
|
|
741
850
|
if (!response.ok) {
|
|
742
|
-
const body = await response
|
|
851
|
+
const body = await readCappedErrorBody(response);
|
|
743
852
|
throw new SilmarilApiError({
|
|
744
853
|
status: response.status,
|
|
745
854
|
statusText: response.statusText,
|
|
@@ -753,6 +862,20 @@ var Firewall = class {
|
|
|
753
862
|
}
|
|
754
863
|
throw new Error("Firewall: exhausted retries (unreachable)");
|
|
755
864
|
}
|
|
865
|
+
async classifySingleChunk(text, options, threshold) {
|
|
866
|
+
const payload = { text, threshold };
|
|
867
|
+
if (options.hook !== void 0) {
|
|
868
|
+
payload.hook = options.hook;
|
|
869
|
+
}
|
|
870
|
+
if (options.toolName !== void 0) {
|
|
871
|
+
payload.tool_name = options.toolName;
|
|
872
|
+
}
|
|
873
|
+
if (options.metadata !== void 0) {
|
|
874
|
+
payload.metadata = options.metadata;
|
|
875
|
+
}
|
|
876
|
+
const data = await this.postWithRetry(payload);
|
|
877
|
+
return blockResultFromResponse(data, threshold);
|
|
878
|
+
}
|
|
756
879
|
};
|
|
757
880
|
|
|
758
881
|
// src/index.ts
|
|
@@ -764,9 +887,8 @@ export {
|
|
|
764
887
|
CHUNK_OVERLAP_CHARS,
|
|
765
888
|
CHUNK_WINDOW,
|
|
766
889
|
CHUNK_WINDOW_CHARS,
|
|
890
|
+
DEFAULT_CHUNK_CONCURRENCY,
|
|
767
891
|
DEFAULT_HOOKS,
|
|
768
|
-
DEFAULT_HOOK_THRESHOLDS,
|
|
769
|
-
DEFAULT_THRESHOLD,
|
|
770
892
|
DEFAULT_TIMEOUT_MS,
|
|
771
893
|
FIREWALL_HOOK_TO_LABEL,
|
|
772
894
|
Firewall,
|
|
@@ -781,6 +903,7 @@ export {
|
|
|
781
903
|
chunkText,
|
|
782
904
|
prependHook,
|
|
783
905
|
prependToolName,
|
|
784
|
-
resolveHooks
|
|
906
|
+
resolveHooks,
|
|
907
|
+
sanitizeText
|
|
785
908
|
};
|
|
786
909
|
//# sourceMappingURL=index.js.map
|