@salimassili/ai-costguard 1.2.0 → 2.0.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/CHANGELOG.md +53 -0
- package/LICENSE +21 -0
- package/README.md +281 -103
- package/benchmarks/run.mjs +229 -0
- package/dist/cli.d.ts +50 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +178 -0
- package/dist/cli.js.map +1 -0
- package/dist/core/CostGuard.d.ts +3 -4
- package/dist/core/CostGuard.d.ts.map +1 -1
- package/dist/core/CostGuard.js +1 -2
- package/dist/core/CostGuard.js.map +1 -1
- package/dist/core/GuardCore.d.ts +93 -13
- package/dist/core/GuardCore.d.ts.map +1 -1
- package/dist/core/GuardCore.js +372 -158
- package/dist/core/GuardCore.js.map +1 -1
- package/dist/core/GuardFree.d.ts +42 -18
- package/dist/core/GuardFree.d.ts.map +1 -1
- package/dist/core/GuardFree.js +95 -140
- package/dist/core/GuardFree.js.map +1 -1
- package/dist/core/GuardPro.d.ts +85 -5
- package/dist/core/GuardPro.d.ts.map +1 -1
- package/dist/core/GuardPro.js +216 -121
- package/dist/core/GuardPro.js.map +1 -1
- package/dist/core/event-log.d.ts +37 -0
- package/dist/core/event-log.d.ts.map +1 -0
- package/dist/core/event-log.js +49 -0
- package/dist/core/event-log.js.map +1 -0
- package/dist/core/events.d.ts +20 -0
- package/dist/core/events.d.ts.map +1 -0
- package/dist/core/events.js +46 -0
- package/dist/core/events.js.map +1 -0
- package/dist/core/similarity.d.ts +13 -0
- package/dist/core/similarity.d.ts.map +1 -0
- package/dist/core/similarity.js +51 -0
- package/dist/core/similarity.js.map +1 -0
- package/dist/core/tokenizer.d.ts +18 -0
- package/dist/core/tokenizer.d.ts.map +1 -0
- package/dist/core/tokenizer.js +137 -0
- package/dist/core/tokenizer.js.map +1 -0
- package/dist/core/types.d.ts +153 -5
- package/dist/core/types.d.ts.map +1 -1
- package/dist/core/types.js +0 -3
- package/dist/core/types.js.map +1 -1
- package/dist/core/webhooks.d.ts +15 -0
- package/dist/core/webhooks.d.ts.map +1 -0
- package/dist/core/webhooks.js +58 -0
- package/dist/core/webhooks.js.map +1 -0
- package/dist/dashboard.d.ts +73 -0
- package/dist/dashboard.d.ts.map +1 -0
- package/dist/dashboard.js +201 -0
- package/dist/dashboard.js.map +1 -0
- package/dist/index.d.ts +3 -4
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -2
- package/dist/index.js.map +1 -1
- package/dist/pricing/index.d.ts +19 -2
- package/dist/pricing/index.d.ts.map +1 -1
- package/dist/pricing/index.js +93 -13
- package/dist/pricing/index.js.map +1 -1
- package/dist/pro.d.ts +3 -0
- package/dist/pro.d.ts.map +1 -0
- package/dist/pro.js +2 -0
- package/dist/pro.js.map +1 -0
- package/docs/BENCHMARKS.md +51 -0
- package/docs/DASHBOARD.md +61 -0
- package/docs/INTEGRATIONS.md +153 -0
- package/examples/integrations/anthropic-workflow-budget.mjs +36 -0
- package/examples/integrations/ci-budget-check.mjs +32 -0
- package/examples/integrations/crewai-budget-gate.mjs +31 -0
- package/examples/integrations/langchain-retry-storm.mjs +32 -0
- package/examples/integrations/mastra-agent.mjs +41 -0
- package/examples/integrations/openai-agent-loop.mjs +44 -0
- package/examples/integrations/vercel-ai-chatbot.mjs +29 -0
- package/package.json +35 -7
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
const TRIGRAM_SIZE = 3;
|
|
2
|
+
/**
|
|
3
|
+
* Builds a character trigram frequency vector for a prompt.
|
|
4
|
+
*/
|
|
5
|
+
export function characterTrigrams(input) {
|
|
6
|
+
const normalized = normalizeForSimilarity(input);
|
|
7
|
+
const vector = new Map();
|
|
8
|
+
if (normalized.length === 0)
|
|
9
|
+
return vector;
|
|
10
|
+
if (normalized.length < TRIGRAM_SIZE) {
|
|
11
|
+
vector.set(normalized, 1);
|
|
12
|
+
return vector;
|
|
13
|
+
}
|
|
14
|
+
for (let index = 0; index <= normalized.length - TRIGRAM_SIZE; index++) {
|
|
15
|
+
const trigram = normalized.slice(index, index + TRIGRAM_SIZE);
|
|
16
|
+
vector.set(trigram, (vector.get(trigram) ?? 0) + 1);
|
|
17
|
+
}
|
|
18
|
+
return vector;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Calculates cosine similarity between two strings using character trigrams.
|
|
22
|
+
*/
|
|
23
|
+
export function cosineSimilarity(left, right) {
|
|
24
|
+
const leftVector = characterTrigrams(left);
|
|
25
|
+
const rightVector = characterTrigrams(right);
|
|
26
|
+
if (leftVector.size === 0 || rightVector.size === 0)
|
|
27
|
+
return 0;
|
|
28
|
+
let dotProduct = 0;
|
|
29
|
+
let leftMagnitude = 0;
|
|
30
|
+
let rightMagnitude = 0;
|
|
31
|
+
for (const [key, leftValue] of leftVector) {
|
|
32
|
+
dotProduct += leftValue * (rightVector.get(key) ?? 0);
|
|
33
|
+
leftMagnitude += leftValue * leftValue;
|
|
34
|
+
}
|
|
35
|
+
for (const rightValue of rightVector.values()) {
|
|
36
|
+
rightMagnitude += rightValue * rightValue;
|
|
37
|
+
}
|
|
38
|
+
if (leftMagnitude === 0 || rightMagnitude === 0)
|
|
39
|
+
return 0;
|
|
40
|
+
return dotProduct / (Math.sqrt(leftMagnitude) * Math.sqrt(rightMagnitude));
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Returns the highest trigram cosine similarity between a prompt and a prompt history.
|
|
44
|
+
*/
|
|
45
|
+
export function maxCosineSimilarity(prompt, history) {
|
|
46
|
+
return history.reduce((max, candidate) => Math.max(max, cosineSimilarity(prompt, candidate)), 0);
|
|
47
|
+
}
|
|
48
|
+
function normalizeForSimilarity(input) {
|
|
49
|
+
return input.toLowerCase().replace(/\s+/g, ' ').trim();
|
|
50
|
+
}
|
|
51
|
+
//# sourceMappingURL=similarity.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"similarity.js","sourceRoot":"","sources":["../../src/core/similarity.ts"],"names":[],"mappings":"AAAA,MAAM,YAAY,GAAG,CAAC,CAAC;AAEvB;;GAEG;AACH,MAAM,UAAU,iBAAiB,CAAC,KAAa;IAC7C,MAAM,UAAU,GAAG,sBAAsB,CAAC,KAAK,CAAC,CAAC;IACjD,MAAM,MAAM,GAAG,IAAI,GAAG,EAAkB,CAAC;IAEzC,IAAI,UAAU,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,MAAM,CAAC;IAC3C,IAAI,UAAU,CAAC,MAAM,GAAG,YAAY,EAAE,CAAC;QACrC,MAAM,CAAC,GAAG,CAAC,UAAU,EAAE,CAAC,CAAC,CAAC;QAC1B,OAAO,MAAM,CAAC;IAChB,CAAC;IAED,KAAK,IAAI,KAAK,GAAG,CAAC,EAAE,KAAK,IAAI,UAAU,CAAC,MAAM,GAAG,YAAY,EAAE,KAAK,EAAE,EAAE,CAAC;QACvE,MAAM,OAAO,GAAG,UAAU,CAAC,KAAK,CAAC,KAAK,EAAE,KAAK,GAAG,YAAY,CAAC,CAAC;QAC9D,MAAM,CAAC,GAAG,CAAC,OAAO,EAAE,CAAC,MAAM,CAAC,GAAG,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC;IACtD,CAAC;IAED,OAAO,MAAM,CAAC;AAChB,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,gBAAgB,CAAC,IAAY,EAAE,KAAa;IAC1D,MAAM,UAAU,GAAG,iBAAiB,CAAC,IAAI,CAAC,CAAC;IAC3C,MAAM,WAAW,GAAG,iBAAiB,CAAC,KAAK,CAAC,CAAC;IAE7C,IAAI,UAAU,CAAC,IAAI,KAAK,CAAC,IAAI,WAAW,CAAC,IAAI,KAAK,CAAC;QAAE,OAAO,CAAC,CAAC;IAE9D,IAAI,UAAU,GAAG,CAAC,CAAC;IACnB,IAAI,aAAa,GAAG,CAAC,CAAC;IACtB,IAAI,cAAc,GAAG,CAAC,CAAC;IAEvB,KAAK,MAAM,CAAC,GAAG,EAAE,SAAS,CAAC,IAAI,UAAU,EAAE,CAAC;QAC1C,UAAU,IAAI,SAAS,GAAG,CAAC,WAAW,CAAC,GAAG,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC;QACtD,aAAa,IAAI,SAAS,GAAG,SAAS,CAAC;IACzC,CAAC;IAED,KAAK,MAAM,UAAU,IAAI,WAAW,CAAC,MAAM,EAAE,EAAE,CAAC;QAC9C,cAAc,IAAI,UAAU,GAAG,UAAU,CAAC;IAC5C,CAAC;IAED,IAAI,aAAa,KAAK,CAAC,IAAI,cAAc,KAAK,CAAC;QAAE,OAAO,CAAC,CAAC;IAC1D,OAAO,UAAU,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,aAAa,CAAC,GAAG,IAAI,CAAC,IAAI,CAAC,cAAc,CAAC,CAAC,CAAC;AAC7E,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,mBAAmB,CAAC,MAAc,EAAE,OAA0B;IAC5E,OAAO,OAAO,CAAC,MAAM,CAAC,CAAC,GAAG,EAAE,SAAS,EAAE,EAAE,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,EAAE,gBAAgB,CAAC,MAAM,EAAE,SAAS,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;AACnG,CAAC;AAED,SAAS,sBAAsB,CAAC,KAAa;IAC3C,OAAO,KAAK,CAAC,WAAW,EAAE,CAAC,OAAO,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC,IAAI,EAAE,CAAC;AACzD,CAAC"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Estimates tokens for a plain text string using a small inline BPE approximation.
|
|
3
|
+
*/
|
|
4
|
+
export declare function estimateTokensFromText(input: string): number;
|
|
5
|
+
/**
|
|
6
|
+
* Extracts text from OpenAI-like or Anthropic-like message content.
|
|
7
|
+
*/
|
|
8
|
+
export declare function extractText(value: unknown): string;
|
|
9
|
+
/**
|
|
10
|
+
* Estimates the input, output, and total token counts for an AI request payload.
|
|
11
|
+
*/
|
|
12
|
+
export declare function estimateRequestTokens(params: unknown): {
|
|
13
|
+
inputTokens: number;
|
|
14
|
+
outputTokens: number;
|
|
15
|
+
tokens: number;
|
|
16
|
+
prompt: string;
|
|
17
|
+
};
|
|
18
|
+
//# sourceMappingURL=tokenizer.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tokenizer.d.ts","sourceRoot":"","sources":["../../src/core/tokenizer.ts"],"names":[],"mappings":"AA+CA;;GAEG;AACH,wBAAgB,sBAAsB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAO5D;AAED;;GAEG;AACH,wBAAgB,WAAW,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,CAWlD;AAED;;GAEG;AACH,wBAAgB,qBAAqB,CAAC,MAAM,EAAE,OAAO,GAAG;IACtD,WAAW,EAAE,MAAM,CAAC;IACpB,YAAY,EAAE,MAAM,CAAC;IACrB,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;CAChB,CAiBA"}
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
const TOKEN_PATTERN = /'s|'t|'re|'ve|'m|'ll|'d| ?[\p{L}]+| ?\p{N}{1,3}| ?[^\s\p{L}\p{N}]+|\s+(?!\S)|\s+/gu;
|
|
2
|
+
const BPE_RANKS = new Map([
|
|
3
|
+
't h',
|
|
4
|
+
'h e',
|
|
5
|
+
'i n',
|
|
6
|
+
'e r',
|
|
7
|
+
'a n',
|
|
8
|
+
'r e',
|
|
9
|
+
'o n',
|
|
10
|
+
'a t',
|
|
11
|
+
'e n',
|
|
12
|
+
'n d',
|
|
13
|
+
's t',
|
|
14
|
+
'o r',
|
|
15
|
+
'a l',
|
|
16
|
+
'i t',
|
|
17
|
+
'i s',
|
|
18
|
+
't i',
|
|
19
|
+
'n g',
|
|
20
|
+
'c o',
|
|
21
|
+
'd e',
|
|
22
|
+
'l l',
|
|
23
|
+
'm e',
|
|
24
|
+
'p r',
|
|
25
|
+
'o m',
|
|
26
|
+
'p t',
|
|
27
|
+
'r e',
|
|
28
|
+
'e s',
|
|
29
|
+
's i',
|
|
30
|
+
'o u',
|
|
31
|
+
'a r',
|
|
32
|
+
'a i',
|
|
33
|
+
'g p',
|
|
34
|
+
'p t',
|
|
35
|
+
'c l',
|
|
36
|
+
'a u',
|
|
37
|
+
'u d',
|
|
38
|
+
'd e',
|
|
39
|
+
'm o',
|
|
40
|
+
'o d',
|
|
41
|
+
'e l',
|
|
42
|
+
].map((pair, index) => [pair, index]));
|
|
43
|
+
/**
|
|
44
|
+
* Estimates tokens for a plain text string using a small inline BPE approximation.
|
|
45
|
+
*/
|
|
46
|
+
export function estimateTokensFromText(input) {
|
|
47
|
+
if (input.length === 0)
|
|
48
|
+
return 0;
|
|
49
|
+
const pieces = input.match(TOKEN_PATTERN) ?? [];
|
|
50
|
+
const count = pieces.reduce((total, piece) => total + estimatePieceTokens(piece), 0);
|
|
51
|
+
return Math.max(1, count);
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Extracts text from OpenAI-like or Anthropic-like message content.
|
|
55
|
+
*/
|
|
56
|
+
export function extractText(value) {
|
|
57
|
+
if (typeof value === 'string')
|
|
58
|
+
return value;
|
|
59
|
+
if (typeof value === 'number' || typeof value === 'boolean')
|
|
60
|
+
return String(value);
|
|
61
|
+
if (Array.isArray(value))
|
|
62
|
+
return value.map(extractText).filter(Boolean).join(' ');
|
|
63
|
+
if (isRecord(value)) {
|
|
64
|
+
const typedText = value.text ?? value.content ?? value.input ?? value.message;
|
|
65
|
+
if (typedText !== undefined)
|
|
66
|
+
return extractText(typedText);
|
|
67
|
+
}
|
|
68
|
+
return '';
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* Estimates the input, output, and total token counts for an AI request payload.
|
|
72
|
+
*/
|
|
73
|
+
export function estimateRequestTokens(params) {
|
|
74
|
+
const record = isRecord(params) ? params : {};
|
|
75
|
+
const prompt = extractPrompt(record);
|
|
76
|
+
const modelOverhead = Array.isArray(record.messages) ? record.messages.length * 3 + 3 : 0;
|
|
77
|
+
const inputTokens = estimateTokensFromText(prompt) + modelOverhead;
|
|
78
|
+
const outputTokens = readPositiveNumber(record.max_tokens) ??
|
|
79
|
+
readPositiveNumber(record.max_completion_tokens) ??
|
|
80
|
+
readPositiveNumber(record.maxTokens) ??
|
|
81
|
+
readPositiveNumber(record.max_output_tokens) ??
|
|
82
|
+
1000;
|
|
83
|
+
return {
|
|
84
|
+
inputTokens,
|
|
85
|
+
outputTokens,
|
|
86
|
+
tokens: inputTokens + outputTokens,
|
|
87
|
+
prompt,
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
function estimatePieceTokens(piece) {
|
|
91
|
+
if (/^\s+$/u.test(piece))
|
|
92
|
+
return 1;
|
|
93
|
+
if (/^\p{N}{1,3}$/u.test(piece.trim()))
|
|
94
|
+
return 1;
|
|
95
|
+
const normalized = piece.normalize('NFKC');
|
|
96
|
+
if (/^[\p{P}\p{S}\s]+$/u.test(normalized)) {
|
|
97
|
+
return Math.max(1, Math.ceil([...normalized].length / 2));
|
|
98
|
+
}
|
|
99
|
+
const symbols = applyApproximateBpe([...normalized.toLowerCase()]);
|
|
100
|
+
return Math.max(1, symbols.length);
|
|
101
|
+
}
|
|
102
|
+
function applyApproximateBpe(initialSymbols) {
|
|
103
|
+
const symbols = [...initialSymbols];
|
|
104
|
+
while (symbols.length > 1) {
|
|
105
|
+
let bestIndex = -1;
|
|
106
|
+
let bestRank = Number.POSITIVE_INFINITY;
|
|
107
|
+
for (let index = 0; index < symbols.length - 1; index++) {
|
|
108
|
+
const rank = BPE_RANKS.get(`${symbols[index]} ${symbols[index + 1]}`);
|
|
109
|
+
if (rank !== undefined && rank < bestRank) {
|
|
110
|
+
bestRank = rank;
|
|
111
|
+
bestIndex = index;
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
if (bestIndex === -1)
|
|
115
|
+
break;
|
|
116
|
+
symbols.splice(bestIndex, 2, `${symbols[bestIndex]}${symbols[bestIndex + 1]}`);
|
|
117
|
+
}
|
|
118
|
+
return symbols;
|
|
119
|
+
}
|
|
120
|
+
function extractPrompt(record) {
|
|
121
|
+
if (Array.isArray(record.messages)) {
|
|
122
|
+
return record.messages
|
|
123
|
+
.map((message) => (isRecord(message) ? extractText(message.content) : extractText(message)))
|
|
124
|
+
.filter(Boolean)
|
|
125
|
+
.join(' ');
|
|
126
|
+
}
|
|
127
|
+
return extractText(record.prompt ?? record.input ?? record.content ?? record.message);
|
|
128
|
+
}
|
|
129
|
+
function readPositiveNumber(value) {
|
|
130
|
+
if (typeof value !== 'number' || !Number.isFinite(value) || value <= 0)
|
|
131
|
+
return undefined;
|
|
132
|
+
return value;
|
|
133
|
+
}
|
|
134
|
+
function isRecord(value) {
|
|
135
|
+
return typeof value === 'object' && value !== null;
|
|
136
|
+
}
|
|
137
|
+
//# sourceMappingURL=tokenizer.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tokenizer.js","sourceRoot":"","sources":["../../src/core/tokenizer.ts"],"names":[],"mappings":"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"}
|
package/dist/core/types.d.ts
CHANGED
|
@@ -1,24 +1,172 @@
|
|
|
1
|
+
import type { ModelPricing } from '../pricing/index.js';
|
|
1
2
|
/**
|
|
2
|
-
*
|
|
3
|
+
* Stable machine-readable reasons for blocked requests.
|
|
4
|
+
*/
|
|
5
|
+
export type GuardErrorCode = 'UNKNOWN_MODEL' | 'BUDGET_EXCEEDED' | 'MAX_STEPS_EXCEEDED' | 'LOOP_DETECTED' | 'RETRY_STORM_DETECTED'
|
|
6
|
+
/** Reserved for compatibility; current GuardPro does not enforce local licenses. */
|
|
7
|
+
| 'INVALID_LICENSE';
|
|
8
|
+
/**
|
|
9
|
+
* Webhook destinations used by the guard when a request is blocked.
|
|
10
|
+
*/
|
|
11
|
+
export interface GuardWebhookConfig {
|
|
12
|
+
/** Slack incoming webhook URL. */
|
|
13
|
+
slack?: string;
|
|
14
|
+
/** Discord incoming webhook URL. */
|
|
15
|
+
discord?: string;
|
|
16
|
+
/** Number of retry attempts after the first failed POST. Defaults to 2. */
|
|
17
|
+
retries?: number;
|
|
18
|
+
/** Per-request timeout in milliseconds. Defaults to 1500. */
|
|
19
|
+
timeoutMs?: number;
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Scope used to isolate budgets and behavior history.
|
|
23
|
+
*/
|
|
24
|
+
export interface GuardScope {
|
|
25
|
+
/** Product, tenant, or application project identifier. */
|
|
26
|
+
projectId?: string;
|
|
27
|
+
/** End-user identifier. */
|
|
28
|
+
userId?: string;
|
|
29
|
+
/** Agent run, workflow, request, or conversation identifier. */
|
|
30
|
+
sessionId?: string;
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Runtime configuration for the free process-local guard.
|
|
3
34
|
*/
|
|
4
35
|
export interface GuardConfig {
|
|
5
|
-
/**
|
|
6
|
-
budget
|
|
7
|
-
/**
|
|
36
|
+
/** Process-local budget in USD for each scope. Defaults to 10. */
|
|
37
|
+
budget?: number;
|
|
38
|
+
/** Enables loop, retry-storm, and step-count checks. Defaults to true. */
|
|
8
39
|
behaviorAnalysis?: boolean;
|
|
40
|
+
/** Maximum prompt history retained for similarity checks. Defaults to 32. */
|
|
41
|
+
maxHistory?: number;
|
|
42
|
+
/** Prompt/retry history TTL in milliseconds. Defaults to 5 minutes. */
|
|
43
|
+
historyTtlMs?: number;
|
|
44
|
+
/** Optional maximum number of allowed guarded calls in the current process. */
|
|
45
|
+
maxSteps?: number;
|
|
46
|
+
/** Cosine similarity threshold for trigram loop detection. Defaults to 0.85. */
|
|
47
|
+
loopSimilarityThreshold?: number;
|
|
48
|
+
/** Number of prior similar prompts required before loop blocking. Defaults to 2. */
|
|
49
|
+
loopMinRepeats?: number;
|
|
50
|
+
/** Number of retry/failure prompts allowed before a retry storm is blocked. Defaults to 2. */
|
|
51
|
+
retryThreshold?: number;
|
|
52
|
+
/** Known AI SDK method paths to guard. Defaults to common OpenAI/Anthropic create methods. */
|
|
53
|
+
guardedMethods?: string[];
|
|
54
|
+
/** How unknown model pricing is handled. Defaults to "block". */
|
|
55
|
+
unknownModelPolicy?: 'block' | 'fallback';
|
|
56
|
+
/** Fallback pricing used only when unknownModelPolicy is "fallback". */
|
|
57
|
+
unknownModelPricing?: ModelPricing;
|
|
58
|
+
/** Default scope for requests that do not provide one. */
|
|
59
|
+
scope?: GuardScope;
|
|
60
|
+
/** Optional runtime pricing entries that take precedence over built-ins. */
|
|
61
|
+
pricingOverrides?: ModelPricing[];
|
|
62
|
+
/** Slack and Discord webhook destinations for block events. */
|
|
63
|
+
webhooks?: GuardWebhookConfig;
|
|
64
|
+
/** Optional JSONL file path for local dashboard/event history. Disabled by default. */
|
|
65
|
+
eventLogPath?: string;
|
|
66
|
+
/** Prompt logging mode for eventLogPath. Defaults to "none". */
|
|
67
|
+
eventLogPrompt?: 'none' | 'preview';
|
|
68
|
+
/** Convenience Slack webhook URL. Equivalent to webhooks.slack. */
|
|
69
|
+
slackWebhook?: string;
|
|
70
|
+
/** Convenience Discord webhook URL. Equivalent to webhooks.discord. */
|
|
71
|
+
discordWebhook?: string;
|
|
9
72
|
}
|
|
73
|
+
/**
|
|
74
|
+
* Normalized request data evaluated before an AI API call is allowed.
|
|
75
|
+
*/
|
|
10
76
|
export interface RequestContext {
|
|
77
|
+
/** Model name supplied by the request, or "unknown" when missing. */
|
|
11
78
|
model: string;
|
|
79
|
+
/** True when pricing came from the registry, overrides, or configured fallback. */
|
|
80
|
+
pricingKnown?: boolean;
|
|
81
|
+
/** Pricing entry used for estimation, when available. */
|
|
82
|
+
pricing?: ModelPricing;
|
|
83
|
+
/** Estimated total tokens, input plus reserved output. */
|
|
12
84
|
tokens: number;
|
|
85
|
+
/** Estimated input tokens. */
|
|
86
|
+
inputTokens?: number;
|
|
87
|
+
/** Reserved or requested output tokens. */
|
|
88
|
+
outputTokens?: number;
|
|
89
|
+
/** Estimated USD cost for the request. */
|
|
13
90
|
estimatedCost: number;
|
|
91
|
+
/** Actual USD cost reconciled from a provider usage response, when available. */
|
|
92
|
+
actualCost?: number;
|
|
93
|
+
/** Unix timestamp in milliseconds when the context was created. */
|
|
14
94
|
timestamp: number;
|
|
95
|
+
/** Prompt text used for loop and retry detection. */
|
|
15
96
|
prompt: string;
|
|
97
|
+
/** Client method name being guarded, when known. */
|
|
98
|
+
method?: string;
|
|
99
|
+
/** Scope used for budget and history isolation. */
|
|
100
|
+
scope?: GuardScope;
|
|
101
|
+
/** Stable normalized key derived from scope. */
|
|
102
|
+
scopeKey?: string;
|
|
103
|
+
}
|
|
104
|
+
/**
|
|
105
|
+
* Prompt history entry retained by the process-local guard.
|
|
106
|
+
*/
|
|
107
|
+
export interface PromptHistoryEntry {
|
|
108
|
+
/** Prompt text retained for trigram similarity comparison. */
|
|
109
|
+
prompt: string;
|
|
110
|
+
/** Unix timestamp in milliseconds when the prompt was recorded. */
|
|
111
|
+
timestamp: number;
|
|
16
112
|
}
|
|
17
|
-
|
|
113
|
+
/**
|
|
114
|
+
* Mutable process-local state for one scope.
|
|
115
|
+
*/
|
|
116
|
+
export interface GuardScopeState {
|
|
117
|
+
/** Number of allowed requests. */
|
|
18
118
|
requestCount: number;
|
|
119
|
+
/** Estimated allowed spend in USD. */
|
|
19
120
|
totalCost: number;
|
|
121
|
+
/** Estimated cost of all guarded attempts, including blocked attempts. */
|
|
122
|
+
attemptedCost: number;
|
|
123
|
+
/** Estimated cost blocked before provider execution. */
|
|
124
|
+
blockedCost: number;
|
|
125
|
+
/** Actual provider-reported spend reconciled from usage fields when available. */
|
|
126
|
+
actualCost: number;
|
|
127
|
+
/** Unix timestamp in milliseconds for the last allowed request. */
|
|
20
128
|
lastRequestTime: number;
|
|
129
|
+
/** Number of blocked requests. */
|
|
21
130
|
blockedCount: number;
|
|
131
|
+
/** Recent prompts retained for cosine similarity loop detection. */
|
|
132
|
+
recentPrompts?: PromptHistoryEntry[];
|
|
133
|
+
/** Recent retry/failure prompts retained for retry-storm detection. */
|
|
134
|
+
recentRetries?: PromptHistoryEntry[];
|
|
135
|
+
/** Optional session expiry timestamp used by stateful guards. */
|
|
136
|
+
sessionExpiresAt?: number;
|
|
22
137
|
}
|
|
138
|
+
/**
|
|
139
|
+
* Mutable aggregate process-local guard state.
|
|
140
|
+
*/
|
|
141
|
+
export interface GuardState extends GuardScopeState {
|
|
142
|
+
/** Per-scope state keyed by project/user/session. */
|
|
143
|
+
scopes?: Record<string, GuardScopeState>;
|
|
144
|
+
}
|
|
145
|
+
/**
|
|
146
|
+
* Guard decision returned by the core evaluator.
|
|
147
|
+
*/
|
|
23
148
|
export type GuardDecision = 'allow' | 'block';
|
|
149
|
+
/**
|
|
150
|
+
* Supported event names emitted by guarded clients.
|
|
151
|
+
*/
|
|
152
|
+
export type GuardEventName = 'block' | 'allow' | 'cost';
|
|
153
|
+
/**
|
|
154
|
+
* Event payload emitted by a guard instance.
|
|
155
|
+
*/
|
|
156
|
+
export interface GuardEvent {
|
|
157
|
+
/** Event name. */
|
|
158
|
+
type: GuardEventName;
|
|
159
|
+
/** Request context associated with the event. */
|
|
160
|
+
context: RequestContext;
|
|
161
|
+
/** Machine-readable block reason, present for block events. */
|
|
162
|
+
code?: GuardErrorCode;
|
|
163
|
+
/** Human-readable block reason, present for block events. */
|
|
164
|
+
reason?: string;
|
|
165
|
+
/** Snapshot of state at emit time. */
|
|
166
|
+
state: Readonly<GuardState>;
|
|
167
|
+
}
|
|
168
|
+
/**
|
|
169
|
+
* Callback invoked when a guard event is emitted.
|
|
170
|
+
*/
|
|
171
|
+
export type GuardEventHandler = (event: GuardEvent) => void;
|
|
24
172
|
//# sourceMappingURL=types.d.ts.map
|
package/dist/core/types.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/core/types.ts"],"names":[],"mappings":"AAAA;;GAEG;
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/core/types.ts"],"names":[],"mappings":"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"}
|
package/dist/core/types.js
CHANGED
package/dist/core/types.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/core/types.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/core/types.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { GuardWebhookConfig, RequestContext } from './types.js';
|
|
2
|
+
/**
|
|
3
|
+
* Payload sent to configured block webhooks.
|
|
4
|
+
*/
|
|
5
|
+
export interface GuardWebhookPayload {
|
|
6
|
+
/** Human-readable reason the request was blocked. */
|
|
7
|
+
reason: string;
|
|
8
|
+
/** Request context associated with the block. */
|
|
9
|
+
context: RequestContext;
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* Posts a block notification to Slack and Discord webhooks with exponential backoff.
|
|
13
|
+
*/
|
|
14
|
+
export declare function notifyBlockWebhooks(config: GuardWebhookConfig | undefined, payload: GuardWebhookPayload): Promise<void>;
|
|
15
|
+
//# sourceMappingURL=webhooks.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"webhooks.d.ts","sourceRoot":"","sources":["../../src/core/webhooks.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,kBAAkB,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAErE;;GAEG;AACH,MAAM,WAAW,mBAAmB;IAClC,qDAAqD;IACrD,MAAM,EAAE,MAAM,CAAC;IACf,iDAAiD;IACjD,OAAO,EAAE,cAAc,CAAC;CACzB;AAED;;GAEG;AACH,wBAAsB,mBAAmB,CACvC,MAAM,EAAE,kBAAkB,GAAG,SAAS,EACtC,OAAO,EAAE,mBAAmB,GAC3B,OAAO,CAAC,IAAI,CAAC,CAOf"}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Posts a block notification to Slack and Discord webhooks with exponential backoff.
|
|
3
|
+
*/
|
|
4
|
+
export async function notifyBlockWebhooks(config, payload) {
|
|
5
|
+
if (!config?.slack && !config?.discord)
|
|
6
|
+
return;
|
|
7
|
+
await Promise.all([
|
|
8
|
+
config.slack ? postWithBackoff(config.slack, slackBody(payload), config) : Promise.resolve(),
|
|
9
|
+
config.discord ? postWithBackoff(config.discord, discordBody(payload), config) : Promise.resolve(),
|
|
10
|
+
]);
|
|
11
|
+
}
|
|
12
|
+
function slackBody(payload) {
|
|
13
|
+
return JSON.stringify({
|
|
14
|
+
text: `[AI CostGuard] Blocked request: ${payload.reason}\n` +
|
|
15
|
+
`Model: ${payload.context.model}\n` +
|
|
16
|
+
`Estimated cost: $${payload.context.estimatedCost.toFixed(6)}`,
|
|
17
|
+
});
|
|
18
|
+
}
|
|
19
|
+
function discordBody(payload) {
|
|
20
|
+
return JSON.stringify({
|
|
21
|
+
content: `[AI CostGuard] Blocked request: ${payload.reason}\n` +
|
|
22
|
+
`Model: ${payload.context.model}\n` +
|
|
23
|
+
`Estimated cost: $${payload.context.estimatedCost.toFixed(6)}`,
|
|
24
|
+
});
|
|
25
|
+
}
|
|
26
|
+
async function postWithBackoff(url, body, config) {
|
|
27
|
+
const retries = Math.max(0, config.retries ?? 2);
|
|
28
|
+
const timeoutMs = Math.max(100, config.timeoutMs ?? 1500);
|
|
29
|
+
for (let attempt = 0; attempt <= retries; attempt++) {
|
|
30
|
+
try {
|
|
31
|
+
const controller = new AbortController();
|
|
32
|
+
const timeout = setTimeout(() => controller.abort(), timeoutMs);
|
|
33
|
+
try {
|
|
34
|
+
const response = await fetch(url, {
|
|
35
|
+
method: 'POST',
|
|
36
|
+
headers: { 'content-type': 'application/json' },
|
|
37
|
+
body,
|
|
38
|
+
signal: controller.signal,
|
|
39
|
+
});
|
|
40
|
+
if (response.ok)
|
|
41
|
+
return;
|
|
42
|
+
}
|
|
43
|
+
finally {
|
|
44
|
+
clearTimeout(timeout);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
catch {
|
|
48
|
+
// Webhooks are observability only; guard enforcement must remain independent.
|
|
49
|
+
}
|
|
50
|
+
if (attempt < retries) {
|
|
51
|
+
await delay(100 * 2 ** attempt);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
function delay(ms) {
|
|
56
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
57
|
+
}
|
|
58
|
+
//# sourceMappingURL=webhooks.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"webhooks.js","sourceRoot":"","sources":["../../src/core/webhooks.ts"],"names":[],"mappings":"AAYA;;GAEG;AACH,MAAM,CAAC,KAAK,UAAU,mBAAmB,CACvC,MAAsC,EACtC,OAA4B;IAE5B,IAAI,CAAC,MAAM,EAAE,KAAK,IAAI,CAAC,MAAM,EAAE,OAAO;QAAE,OAAO;IAE/C,MAAM,OAAO,CAAC,GAAG,CAAC;QAChB,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,eAAe,CAAC,MAAM,CAAC,KAAK,EAAE,SAAS,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,OAAO,EAAE;QAC5F,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,eAAe,CAAC,MAAM,CAAC,OAAO,EAAE,WAAW,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,OAAO,EAAE;KACnG,CAAC,CAAC;AACL,CAAC;AAED,SAAS,SAAS,CAAC,OAA4B;IAC7C,OAAO,IAAI,CAAC,SAAS,CAAC;QACpB,IAAI,EACF,mCAAmC,OAAO,CAAC,MAAM,IAAI;YACrD,UAAU,OAAO,CAAC,OAAO,CAAC,KAAK,IAAI;YACnC,oBAAoB,OAAO,CAAC,OAAO,CAAC,aAAa,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE;KACjE,CAAC,CAAC;AACL,CAAC;AAED,SAAS,WAAW,CAAC,OAA4B;IAC/C,OAAO,IAAI,CAAC,SAAS,CAAC;QACpB,OAAO,EACL,mCAAmC,OAAO,CAAC,MAAM,IAAI;YACrD,UAAU,OAAO,CAAC,OAAO,CAAC,KAAK,IAAI;YACnC,oBAAoB,OAAO,CAAC,OAAO,CAAC,aAAa,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE;KACjE,CAAC,CAAC;AACL,CAAC;AAED,KAAK,UAAU,eAAe,CAAC,GAAW,EAAE,IAAY,EAAE,MAA0B;IAClF,MAAM,OAAO,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,MAAM,CAAC,OAAO,IAAI,CAAC,CAAC,CAAC;IACjD,MAAM,SAAS,GAAG,IAAI,CAAC,GAAG,CAAC,GAAG,EAAE,MAAM,CAAC,SAAS,IAAI,IAAI,CAAC,CAAC;IAE1D,KAAK,IAAI,OAAO,GAAG,CAAC,EAAE,OAAO,IAAI,OAAO,EAAE,OAAO,EAAE,EAAE,CAAC;QACpD,IAAI,CAAC;YACH,MAAM,UAAU,GAAG,IAAI,eAAe,EAAE,CAAC;YACzC,MAAM,OAAO,GAAG,UAAU,CAAC,GAAG,EAAE,CAAC,UAAU,CAAC,KAAK,EAAE,EAAE,SAAS,CAAC,CAAC;YAEhE,IAAI,CAAC;gBACH,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,GAAG,EAAE;oBAChC,MAAM,EAAE,MAAM;oBACd,OAAO,EAAE,EAAE,cAAc,EAAE,kBAAkB,EAAE;oBAC/C,IAAI;oBACJ,MAAM,EAAE,UAAU,CAAC,MAAM;iBAC1B,CAAC,CAAC;gBAEH,IAAI,QAAQ,CAAC,EAAE;oBAAE,OAAO;YAC1B,CAAC;oBAAS,CAAC;gBACT,YAAY,CAAC,OAAO,CAAC,CAAC;YACxB,CAAC;QACH,CAAC;QAAC,MAAM,CAAC;YACP,8EAA8E;QAChF,CAAC;QAED,IAAI,OAAO,GAAG,OAAO,EAAE,CAAC;YACtB,MAAM,KAAK,CAAC,GAAG,GAAG,CAAC,IAAI,OAAO,CAAC,CAAC;QAClC,CAAC;IACH,CAAC;AACH,CAAC;AAED,SAAS,KAAK,CAAC,EAAU;IACvB,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,UAAU,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC,CAAC;AAC3D,CAAC"}
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import { type Server } from 'node:http';
|
|
2
|
+
import type { GuardErrorCode, GuardEventName } from './core/types.js';
|
|
3
|
+
/**
|
|
4
|
+
* Parsed dashboard event from a JSONL event log.
|
|
5
|
+
*/
|
|
6
|
+
export interface DashboardEvent {
|
|
7
|
+
version: 1;
|
|
8
|
+
timestamp: string;
|
|
9
|
+
type: GuardEventName;
|
|
10
|
+
code?: GuardErrorCode;
|
|
11
|
+
reason?: string;
|
|
12
|
+
model: string;
|
|
13
|
+
method?: string;
|
|
14
|
+
scopeKey: string;
|
|
15
|
+
estimatedCost: number;
|
|
16
|
+
actualCost?: number;
|
|
17
|
+
inputTokens?: number;
|
|
18
|
+
outputTokens?: number;
|
|
19
|
+
tokens: number;
|
|
20
|
+
promptPreview?: string;
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Aggregated local dashboard metrics.
|
|
24
|
+
*/
|
|
25
|
+
export interface DashboardSummary {
|
|
26
|
+
eventLogPath: string;
|
|
27
|
+
generatedAt: string;
|
|
28
|
+
budgetUsd?: number;
|
|
29
|
+
budgetUsedPercent?: number;
|
|
30
|
+
requestsAllowed: number;
|
|
31
|
+
requestsBlocked: number;
|
|
32
|
+
estimatedSpendUsd: number;
|
|
33
|
+
estimatedSavingsUsd: number;
|
|
34
|
+
attemptedSpendUsd: number;
|
|
35
|
+
actualSpendUsd: number;
|
|
36
|
+
loopDetections: number;
|
|
37
|
+
retryDetections: number;
|
|
38
|
+
recentEvents: DashboardEvent[];
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Options for local dashboard summary/server commands.
|
|
42
|
+
*/
|
|
43
|
+
export interface DashboardOptions {
|
|
44
|
+
eventLogPath?: string;
|
|
45
|
+
budgetUsd?: number;
|
|
46
|
+
host?: string;
|
|
47
|
+
port?: number;
|
|
48
|
+
recentLimit?: number;
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Returns the default local event log path.
|
|
52
|
+
*/
|
|
53
|
+
export declare function getDefaultEventLogPath(): string;
|
|
54
|
+
/**
|
|
55
|
+
* Reads dashboard events from a local JSONL event log.
|
|
56
|
+
*/
|
|
57
|
+
export declare function readDashboardEvents(eventLogPath?: string): DashboardEvent[];
|
|
58
|
+
/**
|
|
59
|
+
* Builds dashboard metrics from a local event log.
|
|
60
|
+
*/
|
|
61
|
+
export declare function summarizeDashboard(options?: DashboardOptions): DashboardSummary;
|
|
62
|
+
/**
|
|
63
|
+
* Formats a dashboard summary for terminal output.
|
|
64
|
+
*/
|
|
65
|
+
export declare function formatDashboardSummary(summary: DashboardSummary): string;
|
|
66
|
+
/**
|
|
67
|
+
* Starts a local-only HTTP dashboard server.
|
|
68
|
+
*/
|
|
69
|
+
export declare function startDashboardServer(options?: DashboardOptions): Promise<{
|
|
70
|
+
server: Server;
|
|
71
|
+
url: string;
|
|
72
|
+
}>;
|
|
73
|
+
//# sourceMappingURL=dashboard.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"dashboard.d.ts","sourceRoot":"","sources":["../src/dashboard.ts"],"names":[],"mappings":"AACA,OAAO,EAAgB,KAAK,MAAM,EAAE,MAAM,WAAW,CAAC;AACtD,OAAO,KAAK,EAAE,cAAc,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;AAMtE;;GAEG;AACH,MAAM,WAAW,cAAc;IAC7B,OAAO,EAAE,CAAC,CAAC;IACX,SAAS,EAAE,MAAM,CAAC;IAClB,IAAI,EAAE,cAAc,CAAC;IACrB,IAAI,CAAC,EAAE,cAAc,CAAC;IACtB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,MAAM,CAAC;IACjB,aAAa,EAAE,MAAM,CAAC;IACtB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,MAAM,EAAE,MAAM,CAAC;IACf,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB;AAED;;GAEG;AACH,MAAM,WAAW,gBAAgB;IAC/B,YAAY,EAAE,MAAM,CAAC;IACrB,WAAW,EAAE,MAAM,CAAC;IACpB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,eAAe,EAAE,MAAM,CAAC;IACxB,eAAe,EAAE,MAAM,CAAC;IACxB,iBAAiB,EAAE,MAAM,CAAC;IAC1B,mBAAmB,EAAE,MAAM,CAAC;IAC5B,iBAAiB,EAAE,MAAM,CAAC;IAC1B,cAAc,EAAE,MAAM,CAAC;IACvB,cAAc,EAAE,MAAM,CAAC;IACvB,eAAe,EAAE,MAAM,CAAC;IACxB,YAAY,EAAE,cAAc,EAAE,CAAC;CAChC;AAED;;GAEG;AACH,MAAM,WAAW,gBAAgB;IAC/B,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED;;GAEG;AACH,wBAAgB,sBAAsB,IAAI,MAAM,CAE/C;AAED;;GAEG;AACH,wBAAgB,mBAAmB,CAAC,YAAY,SAAyB,GAAG,cAAc,EAAE,CAe3F;AAED;;GAEG;AACH,wBAAgB,kBAAkB,CAAC,OAAO,GAAE,gBAAqB,GAAG,gBAAgB,CA8BnF;AAED;;GAEG;AACH,wBAAgB,sBAAsB,CAAC,OAAO,EAAE,gBAAgB,GAAG,MAAM,CAuBxE;AAED;;GAEG;AACH,wBAAgB,oBAAoB,CAAC,OAAO,GAAE,gBAAqB,GAAG,OAAO,CAAC;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,CAAA;CAAE,CAAC,CAyB7G"}
|