@trazum/core 1.8.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/LICENSE +21 -0
- package/README.md +163 -0
- package/dist/advisories.d.ts +23 -0
- package/dist/advisories.d.ts.map +1 -0
- package/dist/advisories.js +376 -0
- package/dist/advisories.js.map +1 -0
- package/dist/aws-sigv4.d.ts +88 -0
- package/dist/aws-sigv4.d.ts.map +1 -0
- package/dist/aws-sigv4.js +117 -0
- package/dist/aws-sigv4.js.map +1 -0
- package/dist/baseline.d.ts +171 -0
- package/dist/baseline.d.ts.map +1 -0
- package/dist/baseline.js +273 -0
- package/dist/baseline.js.map +1 -0
- package/dist/cache.d.ts +26 -0
- package/dist/cache.d.ts.map +1 -0
- package/dist/cache.js +28 -0
- package/dist/cache.js.map +1 -0
- package/dist/changes.d.ts +29 -0
- package/dist/changes.d.ts.map +1 -0
- package/dist/changes.js +142 -0
- package/dist/changes.js.map +1 -0
- package/dist/compare.d.ts +65 -0
- package/dist/compare.d.ts.map +1 -0
- package/dist/compare.js +58 -0
- package/dist/compare.js.map +1 -0
- package/dist/config-schema.d.ts +118 -0
- package/dist/config-schema.d.ts.map +1 -0
- package/dist/config-schema.js +315 -0
- package/dist/config-schema.js.map +1 -0
- package/dist/config.d.ts +47 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +112 -0
- package/dist/config.js.map +1 -0
- package/dist/detect.d.ts +70 -0
- package/dist/detect.d.ts.map +1 -0
- package/dist/detect.js +228 -0
- package/dist/detect.js.map +1 -0
- package/dist/evaluate.d.ts +98 -0
- package/dist/evaluate.d.ts.map +1 -0
- package/dist/evaluate.js +110 -0
- package/dist/evaluate.js.map +1 -0
- package/dist/extract.d.ts +81 -0
- package/dist/extract.d.ts.map +1 -0
- package/dist/extract.js +280 -0
- package/dist/extract.js.map +1 -0
- package/dist/gcp-auth.d.ts +58 -0
- package/dist/gcp-auth.d.ts.map +1 -0
- package/dist/gcp-auth.js +113 -0
- package/dist/gcp-auth.js.map +1 -0
- package/dist/glob.d.ts +49 -0
- package/dist/glob.d.ts.map +1 -0
- package/dist/glob.js +154 -0
- package/dist/glob.js.map +1 -0
- package/dist/host.d.ts +30 -0
- package/dist/host.d.ts.map +1 -0
- package/dist/host.js +69 -0
- package/dist/host.js.map +1 -0
- package/dist/i18n/en.d.ts +4 -0
- package/dist/i18n/en.d.ts.map +1 -0
- package/dist/i18n/en.js +168 -0
- package/dist/i18n/en.js.map +1 -0
- package/dist/i18n/es.d.ts +4 -0
- package/dist/i18n/es.d.ts.map +1 -0
- package/dist/i18n/es.js +168 -0
- package/dist/i18n/es.js.map +1 -0
- package/dist/i18n/index.d.ts +36 -0
- package/dist/i18n/index.d.ts.map +1 -0
- package/dist/i18n/index.js +50 -0
- package/dist/i18n/index.js.map +1 -0
- package/dist/i18n/types.d.ts +180 -0
- package/dist/i18n/types.d.ts.map +1 -0
- package/dist/i18n/types.js +11 -0
- package/dist/i18n/types.js.map +1 -0
- package/dist/index.d.ts +66 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +69 -0
- package/dist/index.js.map +1 -0
- package/dist/llm.d.ts +226 -0
- package/dist/llm.d.ts.map +1 -0
- package/dist/llm.js +485 -0
- package/dist/llm.js.map +1 -0
- package/dist/nearest.d.ts +20 -0
- package/dist/nearest.d.ts.map +1 -0
- package/dist/nearest.js +54 -0
- package/dist/nearest.js.map +1 -0
- package/dist/net.d.ts +90 -0
- package/dist/net.d.ts.map +1 -0
- package/dist/net.js +203 -0
- package/dist/net.js.map +1 -0
- package/dist/node.d.ts +32 -0
- package/dist/node.d.ts.map +1 -0
- package/dist/node.js +33 -0
- package/dist/node.js.map +1 -0
- package/dist/openrouter.d.ts +25 -0
- package/dist/openrouter.d.ts.map +1 -0
- package/dist/openrouter.js +72 -0
- package/dist/openrouter.js.map +1 -0
- package/dist/optimize.d.ts +38 -0
- package/dist/optimize.d.ts.map +1 -0
- package/dist/optimize.js +183 -0
- package/dist/optimize.js.map +1 -0
- package/dist/otlp.d.ts +91 -0
- package/dist/otlp.d.ts.map +1 -0
- package/dist/otlp.js +102 -0
- package/dist/otlp.js.map +1 -0
- package/dist/phrases.d.ts +169 -0
- package/dist/phrases.d.ts.map +1 -0
- package/dist/phrases.js +939 -0
- package/dist/phrases.js.map +1 -0
- package/dist/pricing-overlay.d.ts +55 -0
- package/dist/pricing-overlay.d.ts.map +1 -0
- package/dist/pricing-overlay.js +241 -0
- package/dist/pricing-overlay.js.map +1 -0
- package/dist/pricing.d.ts +115 -0
- package/dist/pricing.d.ts.map +1 -0
- package/dist/pricing.js +400 -0
- package/dist/pricing.js.map +1 -0
- package/dist/profile.d.ts +71 -0
- package/dist/profile.d.ts.map +1 -0
- package/dist/profile.js +55 -0
- package/dist/profile.js.map +1 -0
- package/dist/promptfoo.d.ts +58 -0
- package/dist/promptfoo.d.ts.map +1 -0
- package/dist/promptfoo.js +149 -0
- package/dist/promptfoo.js.map +1 -0
- package/dist/prune.d.ts +91 -0
- package/dist/prune.d.ts.map +1 -0
- package/dist/prune.js +110 -0
- package/dist/prune.js.map +1 -0
- package/dist/reorder.d.ts +82 -0
- package/dist/reorder.d.ts.map +1 -0
- package/dist/reorder.js +215 -0
- package/dist/reorder.js.map +1 -0
- package/dist/review.d.ts +54 -0
- package/dist/review.d.ts.map +1 -0
- package/dist/review.js +131 -0
- package/dist/review.js.map +1 -0
- package/dist/rules.d.ts +5 -0
- package/dist/rules.d.ts.map +1 -0
- package/dist/rules.js +279 -0
- package/dist/rules.js.map +1 -0
- package/dist/savings.d.ts +36 -0
- package/dist/savings.d.ts.map +1 -0
- package/dist/savings.js +83 -0
- package/dist/savings.js.map +1 -0
- package/dist/segment.d.ts +8 -0
- package/dist/segment.d.ts.map +1 -0
- package/dist/segment.js +74 -0
- package/dist/segment.js.map +1 -0
- package/dist/shared-prefix.d.ts +63 -0
- package/dist/shared-prefix.d.ts.map +1 -0
- package/dist/shared-prefix.js +151 -0
- package/dist/shared-prefix.js.map +1 -0
- package/dist/similarity.d.ts +13 -0
- package/dist/similarity.d.ts.map +1 -0
- package/dist/similarity.js +30 -0
- package/dist/similarity.js.map +1 -0
- package/dist/structure.d.ts +144 -0
- package/dist/structure.d.ts.map +1 -0
- package/dist/structure.js +455 -0
- package/dist/structure.js.map +1 -0
- package/dist/suggest.d.ts +100 -0
- package/dist/suggest.d.ts.map +1 -0
- package/dist/suggest.js +151 -0
- package/dist/suggest.js.map +1 -0
- package/dist/tokenizer.d.ts +57 -0
- package/dist/tokenizer.d.ts.map +1 -0
- package/dist/tokenizer.js +157 -0
- package/dist/tokenizer.js.map +1 -0
- package/dist/types.d.ts +296 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +3 -0
- package/dist/types.js.map +1 -0
- package/dist/walk.d.ts +40 -0
- package/dist/walk.d.ts.map +1 -0
- package/dist/walk.js +95 -0
- package/dist/walk.js.map +1 -0
- package/package.json +56 -0
- package/src/advisories.ts +431 -0
- package/src/aws-sigv4.ts +174 -0
- package/src/baseline.ts +390 -0
- package/src/cache.ts +54 -0
- package/src/changes.ts +158 -0
- package/src/compare.ts +131 -0
- package/src/config-schema.ts +451 -0
- package/src/config.ts +161 -0
- package/src/detect.ts +312 -0
- package/src/evaluate.ts +188 -0
- package/src/extract.ts +336 -0
- package/src/gcp-auth.ts +166 -0
- package/src/glob.ts +160 -0
- package/src/host.ts +90 -0
- package/src/i18n/en.ts +236 -0
- package/src/i18n/es.ts +236 -0
- package/src/i18n/index.ts +68 -0
- package/src/i18n/types.ts +230 -0
- package/src/index.ts +228 -0
- package/src/llm.ts +708 -0
- package/src/nearest.ts +61 -0
- package/src/net.ts +233 -0
- package/src/node.ts +63 -0
- package/src/openrouter.ts +125 -0
- package/src/optimize.ts +228 -0
- package/src/otlp.ts +179 -0
- package/src/phrases.ts +1047 -0
- package/src/pricing-overlay.ts +319 -0
- package/src/pricing.ts +468 -0
- package/src/profile.ts +124 -0
- package/src/promptfoo.ts +213 -0
- package/src/prune.ts +211 -0
- package/src/reorder.ts +307 -0
- package/src/review.ts +180 -0
- package/src/rules.ts +324 -0
- package/src/savings.ts +121 -0
- package/src/segment.ts +106 -0
- package/src/shared-prefix.ts +198 -0
- package/src/similarity.ts +28 -0
- package/src/structure.ts +652 -0
- package/src/suggest.ts +254 -0
- package/src/tokenizer.ts +190 -0
- package/src/types.ts +323 -0
- package/src/walk.ts +117 -0
|
@@ -0,0 +1,455 @@
|
|
|
1
|
+
import { segment } from './segment.js';
|
|
2
|
+
import { OUTPUT_CUES } from './phrases.js';
|
|
3
|
+
import { jaccard, normalizeForCompare } from './similarity.js';
|
|
4
|
+
/**
|
|
5
|
+
* Verbs that mark a sentence as being about the *response*.
|
|
6
|
+
*
|
|
7
|
+
* Requiring one is what keeps "translate this into English" from being read as
|
|
8
|
+
* "answer in English". Without it the language axis fires on any prompt that
|
|
9
|
+
* merely mentions a language, which is most translation prompts.
|
|
10
|
+
*/
|
|
11
|
+
const RESPOND = String.raw `(?:respond|reply|answer|write|speak|output|responde|contesta|responder|escribe|redacta)`;
|
|
12
|
+
const AXES = [
|
|
13
|
+
{
|
|
14
|
+
axis: 'response-language',
|
|
15
|
+
values: [
|
|
16
|
+
[
|
|
17
|
+
'fixed-language',
|
|
18
|
+
new RegExp(String.raw `\b${RESPOND}\b[^.!?\n]{0,40}?\bin\s+(?:english|spanish|french|german|italian|portuguese|ingl[ée]s|espa[nñ]ol|franc[ée]s|alem[áa]n)\b|\b${RESPOND}\b[^.!?\n]{0,40}?\ben\s+(?:ingl[ée]s|espa[nñ]ol|franc[ée]s|alem[áa]n)\b`, 'i'),
|
|
19
|
+
],
|
|
20
|
+
[
|
|
21
|
+
'mirror-language',
|
|
22
|
+
new RegExp(
|
|
23
|
+
// The qualifier slot ("own", "native", "preferred") is what makes
|
|
24
|
+
// this survive real prompts: "the customer's own language" is at
|
|
25
|
+
// least as common as the bare form.
|
|
26
|
+
String.raw `\bin\s+the\s+(?:same\s+)?language\s+(?:of|as|the\s+\w+\s+(?:used|wrote|speaks))|\bin\s+(?:the\s+(?:user|customer|client)'?s?|their)\s+(?:own\s+|native\s+|preferred\s+)?language\b|\bmatch\s+the\s+language\b|\ben\s+(?:el\s+)?(?:mismo\s+)?idioma\s+(?:del?\s+|en\s+que\s+)?(?:usuario|cliente|consulta|mensaje|escrib)`, 'i'),
|
|
27
|
+
],
|
|
28
|
+
],
|
|
29
|
+
},
|
|
30
|
+
{
|
|
31
|
+
axis: 'output-format',
|
|
32
|
+
values: [
|
|
33
|
+
[
|
|
34
|
+
'format-json',
|
|
35
|
+
new RegExp(String.raw `\b(?:${RESPOND}|return|format|devuelve|formatea)\b[^.!?\n]{0,40}?\b(?:as|in|with|using|only|en|como)\s+(?:valid\s+|solo\s+|válido\s+)?json\b|\bonly\s+json\b|\bsolo\s+json\b`, 'i'),
|
|
36
|
+
],
|
|
37
|
+
[
|
|
38
|
+
'format-markdown',
|
|
39
|
+
new RegExp(String.raw `\b(?:${RESPOND}|return|format|devuelve|formatea)\b[^.!?\n]{0,40}?\b(?:as|in|with|using|en|como)\s+markdown\b|\buse\s+markdown\b|\busa\s+markdown\b`, 'i'),
|
|
40
|
+
],
|
|
41
|
+
[
|
|
42
|
+
'format-plain-text',
|
|
43
|
+
new RegExp(String.raw `\bplain\s+text\b|\bno\s+markdown\b|\bwithout\s+markdown\b|\btexto\s+plano\b|\bsin\s+markdown\b`, 'i'),
|
|
44
|
+
],
|
|
45
|
+
],
|
|
46
|
+
},
|
|
47
|
+
{
|
|
48
|
+
axis: 'response-length',
|
|
49
|
+
values: [
|
|
50
|
+
[
|
|
51
|
+
'length-brief',
|
|
52
|
+
new RegExp(String.raw `\b(?:be\s+(?:brief|concise|succinct|terse)|keep\s+it\s+(?:short|brief)|as\s+short\s+as\s+possible|in\s+(?:one|a\s+single)\s+sentence|s[ée]\s+(?:breve|conciso)|de\s+forma\s+(?:breve|concisa)|brevemente)\b`, 'i'),
|
|
53
|
+
],
|
|
54
|
+
[
|
|
55
|
+
'length-detailed',
|
|
56
|
+
new RegExp(String.raw `\b(?:be\s+(?:detailed|comprehensive|thorough|exhaustive)|in\s+(?:great\s+)?depth|as\s+much\s+detail\s+as\s+possible|s[ée]\s+(?:exhaustivo|detallado)|de\s+forma\s+(?:detallada|exhaustiva)|con\s+todo\s+detalle|detalladamente)\b`, 'i'),
|
|
57
|
+
],
|
|
58
|
+
],
|
|
59
|
+
},
|
|
60
|
+
{
|
|
61
|
+
axis: 'reasoning-visibility',
|
|
62
|
+
values: [
|
|
63
|
+
[
|
|
64
|
+
'reasoning-shown',
|
|
65
|
+
new RegExp(String.raw `\b(?:explain\s+your\s+(?:reasoning|answer|thinking)|show\s+your\s+(?:work|reasoning|thinking)|think\s+step[\s-]by[\s-]step|justify\s+your\s+answer|explica\s+tu\s+razonamiento|razona\s+paso\s+a\s+paso|justifica\s+tu\s+respuesta)\b`, 'i'),
|
|
66
|
+
],
|
|
67
|
+
[
|
|
68
|
+
'reasoning-hidden',
|
|
69
|
+
new RegExp(String.raw `\b(?:no\s+(?:explanation|commentary|preamble)|without\s+(?:explanation|commentary)|do\s+not\s+explain|don'?t\s+explain|no\s+expliques|sin\s+(?:explicaciones|comentarios)|sin\s+preámbulo)\b`, 'i'),
|
|
70
|
+
],
|
|
71
|
+
],
|
|
72
|
+
},
|
|
73
|
+
];
|
|
74
|
+
/**
|
|
75
|
+
* Text the analysis is allowed to read.
|
|
76
|
+
*
|
|
77
|
+
* Protected segments are replaced by a space rather than kept: a JSON schema
|
|
78
|
+
* inside a code fence is the *specification* of the output format, not a
|
|
79
|
+
* second instruction about it, and reading it as one would report a
|
|
80
|
+
* contradiction in every well-written prompt that shows its schema.
|
|
81
|
+
*/
|
|
82
|
+
function analysableText(prompt) {
|
|
83
|
+
return segment(prompt)
|
|
84
|
+
.map((s) => (s.kind === 'mutable' ? s.text : ' '))
|
|
85
|
+
.join('');
|
|
86
|
+
}
|
|
87
|
+
/** Splits into sentences, keeping line breaks as boundaries. */
|
|
88
|
+
function sentences(text) {
|
|
89
|
+
return text
|
|
90
|
+
.split(/(?<=[.!?;:])\s+|\n+/)
|
|
91
|
+
.map((s) => s.trim())
|
|
92
|
+
.filter(Boolean);
|
|
93
|
+
}
|
|
94
|
+
function trimSnippet(sentence, max = 90) {
|
|
95
|
+
const clean = sentence.replace(/\s+/g, ' ').trim();
|
|
96
|
+
return clean.length <= max ? clean : `${clean.slice(0, max - 1)}…`;
|
|
97
|
+
}
|
|
98
|
+
/**
|
|
99
|
+
* Finds instructions that pull in opposite directions on the same axis.
|
|
100
|
+
*
|
|
101
|
+
* Only the first occurrence of each value is kept, and an axis reports at most
|
|
102
|
+
* one contradiction: a prompt that says "be concise" four times and "be
|
|
103
|
+
* thorough" once has one problem, not four.
|
|
104
|
+
*
|
|
105
|
+
* Two matches inside the same sentence are ignored. "Be concise but complete"
|
|
106
|
+
* is a deliberate trade-off an author wrote on purpose; flagging it would
|
|
107
|
+
* train people to ignore the advisory.
|
|
108
|
+
*/
|
|
109
|
+
export function findContradictions(prompt) {
|
|
110
|
+
const found = [];
|
|
111
|
+
const lines = sentences(analysableText(prompt));
|
|
112
|
+
for (const { axis, values } of AXES) {
|
|
113
|
+
const hits = new Map();
|
|
114
|
+
lines.forEach((sentence, index) => {
|
|
115
|
+
for (const [value, pattern] of values) {
|
|
116
|
+
if (!hits.has(value) && pattern.test(sentence)) {
|
|
117
|
+
hits.set(value, { snippet: trimSnippet(sentence), sentence: index });
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
});
|
|
121
|
+
if (hits.size < 2)
|
|
122
|
+
continue;
|
|
123
|
+
const [first, second] = [...hits.entries()];
|
|
124
|
+
if (!first || !second)
|
|
125
|
+
continue;
|
|
126
|
+
if (first[1].sentence === second[1].sentence)
|
|
127
|
+
continue;
|
|
128
|
+
found.push({
|
|
129
|
+
axis,
|
|
130
|
+
a: { value: first[0], snippet: first[1].snippet },
|
|
131
|
+
b: { value: second[0], snippet: second[1].snippet },
|
|
132
|
+
});
|
|
133
|
+
}
|
|
134
|
+
return found;
|
|
135
|
+
}
|
|
136
|
+
// --------------------------------------------------------------------------
|
|
137
|
+
// Redundant few-shot examples
|
|
138
|
+
// --------------------------------------------------------------------------
|
|
139
|
+
/**
|
|
140
|
+
* The two halves of an example label, as bounded fragments.
|
|
141
|
+
*
|
|
142
|
+
* Every quantifier here has an upper bound, and that is load-bearing rather
|
|
143
|
+
* than tidiness. The previous form ended `\s*\d*\s*[:.)]` — three adjacent
|
|
144
|
+
* unbounded quantifiers over overlapping character classes. On a line like
|
|
145
|
+
* `example` followed by 40 000 spaces and no terminator, the engine has to try
|
|
146
|
+
* every way of splitting that whitespace between the two `\s*` groups before
|
|
147
|
+
* it can fail: measured O(n²), 651 ms at 40 000 spaces, roughly a minute at
|
|
148
|
+
* the API's 400 KB cap — a denial of service in one request.
|
|
149
|
+
*
|
|
150
|
+
* Bounding turns the split count into a constant. A label reading
|
|
151
|
+
* `Example 12:` never needs more than a few spaces or six digits, so nothing
|
|
152
|
+
* real is lost.
|
|
153
|
+
*
|
|
154
|
+
* `[ \t]` rather than `\s` because these are matched per line, where `\s`
|
|
155
|
+
* matching newlines is wrong anyway.
|
|
156
|
+
*/
|
|
157
|
+
const LABEL_PREFIX = String.raw `^[ \t]{0,16}(?:#{1,6}[ \t]{0,4})?(?:[-*][ \t]{0,4})?`;
|
|
158
|
+
const LABEL_SUFFIX = String.raw `[ \t]{0,4}\d{0,6}[ \t]{0,4}[:.)]`;
|
|
159
|
+
/**
|
|
160
|
+
* Header styles that open a new example, most explicit first.
|
|
161
|
+
*
|
|
162
|
+
* Tiered rather than combined because they nest: a prompt using "Example 1:"
|
|
163
|
+
* almost always puts "Input:"/"Output:" *inside* each example, so matching
|
|
164
|
+
* both at once would cut every example in half and compare the halves. The
|
|
165
|
+
* first tier that finds at least two blocks wins.
|
|
166
|
+
*
|
|
167
|
+
* Deliberately conservative overall: an unlabelled example is
|
|
168
|
+
* indistinguishable from ordinary prose, and guessing would make the advisory
|
|
169
|
+
* fire on prompts containing no examples at all.
|
|
170
|
+
*/
|
|
171
|
+
const EXAMPLE_HEADER_TIERS = [
|
|
172
|
+
new RegExp(`${LABEL_PREFIX}(?:example|ejemplo)${LABEL_SUFFIX}`, 'i'),
|
|
173
|
+
new RegExp(`${LABEL_PREFIX}(?:input|entrada)${LABEL_SUFFIX}`, 'i'),
|
|
174
|
+
new RegExp(`${LABEL_PREFIX}(?:user|usuario|q)${LABEL_SUFFIX}`, 'i'),
|
|
175
|
+
];
|
|
176
|
+
/**
|
|
177
|
+
* A labelled field inside a few-shot example — `Input:`, `Output:`, `A:`…
|
|
178
|
+
*
|
|
179
|
+
* Exported because the duplicate-line rule needs it: two examples that share
|
|
180
|
+
* an output line are demonstrating that two different inputs map to the *same*
|
|
181
|
+
* answer, which is often the whole point of including both. Deduplicating
|
|
182
|
+
* those lines leaves an example with no output at all — worse than the
|
|
183
|
+
* repetition it removed.
|
|
184
|
+
*/
|
|
185
|
+
export const EXAMPLE_FIELD_LINE = new RegExp(`${LABEL_PREFIX}(?:input|output|entrada|salida|example|ejemplo|user|usuario|assistant|asistente|q|a|question|answer|pregunta|respuesta)${LABEL_SUFFIX}`, 'i');
|
|
186
|
+
/**
|
|
187
|
+
* Similarity at which two examples are treated as repeating each other.
|
|
188
|
+
*
|
|
189
|
+
* Lower than the 0.92 the near-duplicate *rule* uses, because that rule
|
|
190
|
+
* deletes and this only reports.
|
|
191
|
+
*
|
|
192
|
+
* What this threshold actually catches, measured on realistic pairs: a
|
|
193
|
+
* copy-pasted example with one field changed scores ~0.89, a lightly edited
|
|
194
|
+
* copy ~0.80, two genuinely different examples ~0.20. A *paraphrase* — the
|
|
195
|
+
* same lesson taught in different words — scores ~0.54 and is deliberately
|
|
196
|
+
* NOT caught, because it sits close enough to the distinct case that catching
|
|
197
|
+
* it would mean flagging examples that teach different things.
|
|
198
|
+
*
|
|
199
|
+
* So this finds copy-paste accumulation, which is how few-shot blocks actually
|
|
200
|
+
* grow, and not semantic redundancy. Recognising that "arrived quickly" and
|
|
201
|
+
* "arrived fast" teach the same thing needs a model, not a word-set overlap;
|
|
202
|
+
* that belongs to the optional LLM pass, not here.
|
|
203
|
+
*/
|
|
204
|
+
const EXAMPLE_SIMILARITY = 0.7;
|
|
205
|
+
/** Splits the prompt on one header style. */
|
|
206
|
+
function splitOnHeader(lines, header) {
|
|
207
|
+
const blocks = [];
|
|
208
|
+
let current = null;
|
|
209
|
+
let inFence = false;
|
|
210
|
+
for (const line of lines) {
|
|
211
|
+
// A header inside a fenced block is part of the example, not a new one.
|
|
212
|
+
if (/^\s*(?:```|~~~)/.test(line))
|
|
213
|
+
inFence = !inFence;
|
|
214
|
+
if (!inFence && header.test(line)) {
|
|
215
|
+
current = [line];
|
|
216
|
+
blocks.push(current);
|
|
217
|
+
continue;
|
|
218
|
+
}
|
|
219
|
+
if (current)
|
|
220
|
+
current.push(line);
|
|
221
|
+
}
|
|
222
|
+
return blocks.map((block) => trimToExample(block.join('\n'))).filter(Boolean);
|
|
223
|
+
}
|
|
224
|
+
/**
|
|
225
|
+
* Cuts a block off where the example stops and ordinary prose resumes.
|
|
226
|
+
*
|
|
227
|
+
* Without this the final example runs to the end of the prompt and absorbs
|
|
228
|
+
* every instruction that follows it, which inflates its length and drags its
|
|
229
|
+
* similarity to the others below any sensible threshold — so the one block
|
|
230
|
+
* most likely to be a duplicate is the one that never gets reported.
|
|
231
|
+
*
|
|
232
|
+
* A paragraph belongs to the example while it still contains a labelled field;
|
|
233
|
+
* the first that does not ends the block.
|
|
234
|
+
*/
|
|
235
|
+
function trimToExample(block) {
|
|
236
|
+
const paragraphs = block.split(/\n\s*\n/);
|
|
237
|
+
const kept = [paragraphs[0] ?? ''];
|
|
238
|
+
for (const paragraph of paragraphs.slice(1)) {
|
|
239
|
+
const isExampleContent = paragraph
|
|
240
|
+
.split('\n')
|
|
241
|
+
.some((line) => EXAMPLE_FIELD_LINE.test(line));
|
|
242
|
+
if (!isExampleContent)
|
|
243
|
+
break;
|
|
244
|
+
kept.push(paragraph);
|
|
245
|
+
}
|
|
246
|
+
return kept.join('\n\n').trim();
|
|
247
|
+
}
|
|
248
|
+
/** Splits the prompt into labelled example blocks. */
|
|
249
|
+
export function findExamples(prompt, count) {
|
|
250
|
+
const lines = prompt.split('\n');
|
|
251
|
+
for (const header of EXAMPLE_HEADER_TIERS) {
|
|
252
|
+
const blocks = splitOnHeader(lines, header);
|
|
253
|
+
if (blocks.length >= 2) {
|
|
254
|
+
return blocks.map((text) => ({ text, tokens: count(text) }));
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
return [];
|
|
258
|
+
}
|
|
259
|
+
/**
|
|
260
|
+
* Reports examples that repeat what an earlier example already demonstrates.
|
|
261
|
+
*
|
|
262
|
+
* Each example is compared against every earlier one, and blamed on the first
|
|
263
|
+
* match: in a run of three near-identical examples the second and third are
|
|
264
|
+
* both reported against the first, rather than chaining.
|
|
265
|
+
*/
|
|
266
|
+
export function analyzeExamples(prompt, count) {
|
|
267
|
+
const examples = findExamples(prompt, count);
|
|
268
|
+
const normalized = examples.map((e) => normalizeForCompare(e.text));
|
|
269
|
+
const redundant = [];
|
|
270
|
+
for (let i = 1; i < examples.length; i++) {
|
|
271
|
+
for (let j = 0; j < i; j++) {
|
|
272
|
+
const similarity = jaccard(normalized[i], normalized[j]);
|
|
273
|
+
if (similarity >= EXAMPLE_SIMILARITY) {
|
|
274
|
+
redundant.push({
|
|
275
|
+
duplicateOf: j,
|
|
276
|
+
index: i,
|
|
277
|
+
similarity,
|
|
278
|
+
tokens: examples[i].tokens,
|
|
279
|
+
});
|
|
280
|
+
break;
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
return {
|
|
285
|
+
examples,
|
|
286
|
+
redundant,
|
|
287
|
+
redundantTokens: redundant.reduce((sum, r) => sum + r.tokens, 0),
|
|
288
|
+
};
|
|
289
|
+
}
|
|
290
|
+
/** Fenced blocks, with their info string. */
|
|
291
|
+
function fencedBlocks(prompt) {
|
|
292
|
+
const blocks = [];
|
|
293
|
+
const fence = /^([ \t]{0,3})(`{3,}|~{3,})([^\n]*)\n([\s\S]*?)^\1\2[ \t]*$/gm;
|
|
294
|
+
let match;
|
|
295
|
+
while ((match = fence.exec(prompt)) !== null) {
|
|
296
|
+
blocks.push({ lang: (match[3] ?? '').trim().toLowerCase(), body: match[4] ?? '' });
|
|
297
|
+
}
|
|
298
|
+
return blocks;
|
|
299
|
+
}
|
|
300
|
+
/**
|
|
301
|
+
* Top-level keys of a JSON-ish block.
|
|
302
|
+
*
|
|
303
|
+
* Deliberately a scan rather than `JSON.parse`: schemas in prompts are
|
|
304
|
+
* routinely illustrative — trailing commas, `...`, comments, a placeholder
|
|
305
|
+
* where a value goes — and refusing to read those would skip exactly the
|
|
306
|
+
* prompts worth checking. Only keys at nesting depth 1 count, so a nested
|
|
307
|
+
* field name cannot be mistaken for a top-level one.
|
|
308
|
+
*/
|
|
309
|
+
function topLevelKeys(body) {
|
|
310
|
+
const keys = [];
|
|
311
|
+
let depth = 0;
|
|
312
|
+
let inString = false;
|
|
313
|
+
let quote = '';
|
|
314
|
+
let current = '';
|
|
315
|
+
for (let i = 0; i < body.length; i++) {
|
|
316
|
+
const ch = body[i];
|
|
317
|
+
if (inString) {
|
|
318
|
+
if (ch === '\\') {
|
|
319
|
+
current += body[i + 1] ?? '';
|
|
320
|
+
i++;
|
|
321
|
+
continue;
|
|
322
|
+
}
|
|
323
|
+
if (ch === quote) {
|
|
324
|
+
inString = false;
|
|
325
|
+
// A string is a key only when the next non-space character is a colon.
|
|
326
|
+
const rest = body.slice(i + 1);
|
|
327
|
+
const colon = /^\s*:/.test(rest);
|
|
328
|
+
if (colon && depth === 1 && current)
|
|
329
|
+
keys.push(current);
|
|
330
|
+
current = '';
|
|
331
|
+
continue;
|
|
332
|
+
}
|
|
333
|
+
current += ch;
|
|
334
|
+
continue;
|
|
335
|
+
}
|
|
336
|
+
if (ch === '"' || ch === "'") {
|
|
337
|
+
inString = true;
|
|
338
|
+
quote = ch;
|
|
339
|
+
current = '';
|
|
340
|
+
continue;
|
|
341
|
+
}
|
|
342
|
+
if (ch === '{' || ch === '[')
|
|
343
|
+
depth++;
|
|
344
|
+
else if (ch === '}' || ch === ']')
|
|
345
|
+
depth--;
|
|
346
|
+
}
|
|
347
|
+
return [...new Set(keys)];
|
|
348
|
+
}
|
|
349
|
+
/** Minimum keys that must be restated before this is worth reporting. */
|
|
350
|
+
const RESTATED_KEY_MINIMUM = 3;
|
|
351
|
+
/**
|
|
352
|
+
* Finds an output schema that the prose repeats.
|
|
353
|
+
*
|
|
354
|
+
* The threshold is deliberately blunt: naming one or two fields in prose is
|
|
355
|
+
* ordinary ("set `escalate` to true when the customer asks for a human"), and
|
|
356
|
+
* flagging that would make the advisory noise. Three or more, and the prose is
|
|
357
|
+
* walking the schema.
|
|
358
|
+
*/
|
|
359
|
+
export function findRestatedFormat(prompt, count) {
|
|
360
|
+
const blocks = fencedBlocks(prompt);
|
|
361
|
+
const keys = [
|
|
362
|
+
...new Set(blocks
|
|
363
|
+
.filter((b) => b.lang === '' || /json|jsonc|json5|yaml|yml/.test(b.lang))
|
|
364
|
+
.flatMap((b) => topLevelKeys(b.body))),
|
|
365
|
+
].filter((k) => k.length >= 3);
|
|
366
|
+
if (keys.length < RESTATED_KEY_MINIMUM)
|
|
367
|
+
return null;
|
|
368
|
+
// Prose only: the schema naming its own keys is not a restatement.
|
|
369
|
+
const prose = analysableText(prompt);
|
|
370
|
+
const proseSentences = sentences(prose);
|
|
371
|
+
const restatedKeys = new Set();
|
|
372
|
+
const guiltySentences = new Set();
|
|
373
|
+
proseSentences.forEach((sentence, index) => {
|
|
374
|
+
for (const key of keys) {
|
|
375
|
+
// Word boundary that also survives snake_case and kebab-case names.
|
|
376
|
+
const pattern = new RegExp(`(?<![\\p{L}\\p{N}_-])${escapeRegExp(key)}(?![\\p{L}\\p{N}_-])`, 'iu');
|
|
377
|
+
if (pattern.test(sentence)) {
|
|
378
|
+
restatedKeys.add(key);
|
|
379
|
+
guiltySentences.add(index);
|
|
380
|
+
}
|
|
381
|
+
}
|
|
382
|
+
});
|
|
383
|
+
if (restatedKeys.size < RESTATED_KEY_MINIMUM)
|
|
384
|
+
return null;
|
|
385
|
+
const restatedTokens = count([...guiltySentences].map((i) => proseSentences[i]).join(' '));
|
|
386
|
+
return {
|
|
387
|
+
keys,
|
|
388
|
+
restatedKeys: [...restatedKeys],
|
|
389
|
+
restatedTokens,
|
|
390
|
+
};
|
|
391
|
+
}
|
|
392
|
+
function escapeRegExp(text) {
|
|
393
|
+
return text.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
394
|
+
}
|
|
395
|
+
/** How far back to look for a cue introducing a block. */
|
|
396
|
+
const CUE_WINDOW = 240;
|
|
397
|
+
/** Fenced blocks with their offset, needed to read what precedes them. */
|
|
398
|
+
function fencedBlocksAt(prompt) {
|
|
399
|
+
const blocks = [];
|
|
400
|
+
const fence = /^([ \t]{0,3})(`{3,}|~{3,})([^\n]*)\n([\s\S]*?)^\1\2[ \t]*$/gm;
|
|
401
|
+
let match;
|
|
402
|
+
while ((match = fence.exec(prompt)) !== null) {
|
|
403
|
+
blocks.push({
|
|
404
|
+
lang: (match[3] ?? '').trim().toLowerCase(),
|
|
405
|
+
body: match[4] ?? '',
|
|
406
|
+
at: match.index,
|
|
407
|
+
raw: match[0],
|
|
408
|
+
});
|
|
409
|
+
}
|
|
410
|
+
return blocks;
|
|
411
|
+
}
|
|
412
|
+
export function findMovableSchema(prompt, count) {
|
|
413
|
+
const cued = [];
|
|
414
|
+
for (const block of fencedBlocksAt(prompt)) {
|
|
415
|
+
// Explicitly JSON-ish, or unlabelled but structured enough to be a shape.
|
|
416
|
+
// A labelled `python` block is somebody's code and never an output contract.
|
|
417
|
+
const jsonish = /^(json|jsonc|json5)$/.test(block.lang) || block.lang === '';
|
|
418
|
+
if (!jsonish)
|
|
419
|
+
continue;
|
|
420
|
+
/**
|
|
421
|
+
* No minimum key *length* here, unlike the restated-format detector.
|
|
422
|
+
* That filter exists there to stop a two-letter key matching a word in
|
|
423
|
+
* prose; nothing is matched against prose here, so all it would do is
|
|
424
|
+
* undercount schemas whose fields are called `id` or `ok`. It was copied
|
|
425
|
+
* across in the first draft and a mutation run found it: deleting it changed
|
|
426
|
+
* no test, which is what a line with no reason looks like.
|
|
427
|
+
*/
|
|
428
|
+
const keys = topLevelKeys(block.body);
|
|
429
|
+
// Fewer than three keys is an illustration, not a contract worth moving —
|
|
430
|
+
// the same threshold the restated-format advisory uses, for the same reason.
|
|
431
|
+
if (keys.length < RESTATED_KEY_MINIMUM)
|
|
432
|
+
continue;
|
|
433
|
+
/**
|
|
434
|
+
* The window before the fence, normalised the way every other comparison in
|
|
435
|
+
* this package normalises: case folded, accents stripped, punctuation
|
|
436
|
+
* flattened. `Formato de salida:` and `FORMATO DE SALIDA —` are the same cue,
|
|
437
|
+
* and a dictionary that only matched one of them would cover Spanish on
|
|
438
|
+
* paper.
|
|
439
|
+
*/
|
|
440
|
+
const lead = normalizeForCompare(prompt.slice(Math.max(0, block.at - CUE_WINDOW), block.at));
|
|
441
|
+
const cue = OUTPUT_CUES.find((phrase) => lead.includes(normalizeForCompare(phrase)));
|
|
442
|
+
if (cue === undefined)
|
|
443
|
+
continue;
|
|
444
|
+
cued.push({ raw: block.raw, keys, cue });
|
|
445
|
+
}
|
|
446
|
+
if (cued.length === 0)
|
|
447
|
+
return null;
|
|
448
|
+
return {
|
|
449
|
+
blocks: cued.length,
|
|
450
|
+
keys: [...new Set(cued.flatMap((block) => block.keys))],
|
|
451
|
+
tokens: count(cued.map((block) => block.raw).join('\n\n')),
|
|
452
|
+
cue: cued[0]?.cue ?? '',
|
|
453
|
+
};
|
|
454
|
+
}
|
|
455
|
+
//# sourceMappingURL=structure.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"structure.js","sourceRoot":"","sources":["../src/structure.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
import type { Locale } from './i18n/types.js';
|
|
2
|
+
import type { LlmProvider, TokenCounter } from './types.js';
|
|
3
|
+
/**
|
|
4
|
+
* Rewrites the rules cannot do, proposed one phrase at a time.
|
|
5
|
+
*
|
|
6
|
+
* The existing LLM pass (`refineWithLlm`) hands the model the whole prompt and
|
|
7
|
+
* takes the whole answer back, which makes it all-or-nothing: when the result
|
|
8
|
+
* fails a safety check the author gets *nothing*, and when it passes they get a
|
|
9
|
+
* wholesale rewrite they have to read end to end to trust. Both halves of that
|
|
10
|
+
* are worse than they need to be.
|
|
11
|
+
*
|
|
12
|
+
* This asks a different question. "Which exact phrases in this prompt say
|
|
13
|
+
* something in more words than they need to?" — and the answer is a list of
|
|
14
|
+
* `before → after` pairs, each one small enough to judge on sight:
|
|
15
|
+
*
|
|
16
|
+
* You should always make sure to → Always
|
|
17
|
+
* It is important to note that → (removed)
|
|
18
|
+
*
|
|
19
|
+
* Nothing is applied unless the caller asks. Eight surviving suggestions out of
|
|
20
|
+
* ten is a useful result; a wholesale rewrite that fails one check is not.
|
|
21
|
+
*
|
|
22
|
+
* ## What makes a suggestion survive
|
|
23
|
+
*
|
|
24
|
+
* The model is a source of proposals, not of truth, so every one is checked
|
|
25
|
+
* against the prompt before it is shown:
|
|
26
|
+
*
|
|
27
|
+
* 1. **`before` must appear in the prompt, byte for byte.** A model that
|
|
28
|
+
* paraphrases what it is quoting has invented a suggestion about text that
|
|
29
|
+
* does not exist, and applying it would do nothing or, worse, match
|
|
30
|
+
* something else.
|
|
31
|
+
* 2. **It must not touch protected content.** Code, URLs, placeholders and XML
|
|
32
|
+
* tags are copied verbatim by every other part of this project, and a
|
|
33
|
+
* suggestion that edits one is refused rather than negotiated.
|
|
34
|
+
* 3. **`after` must not introduce protected content.** A replacement that adds
|
|
35
|
+
* a `{{placeholder}}` or a URL is proposing new semantics, not shorter
|
|
36
|
+
* phrasing.
|
|
37
|
+
* 4. **It must actually save tokens.** A rephrasing that costs the same is a
|
|
38
|
+
* change of style, and this tool is not a style guide.
|
|
39
|
+
* 5. **Overlapping suggestions are dropped, later ones first.** Applying two
|
|
40
|
+
* edits that share characters produces text neither of them described.
|
|
41
|
+
*/
|
|
42
|
+
export interface RewriteSuggestion {
|
|
43
|
+
/** The exact text in the prompt, as it appears there. */
|
|
44
|
+
before: string;
|
|
45
|
+
/** What to put in its place. Empty means "delete this". */
|
|
46
|
+
after: string;
|
|
47
|
+
/** Character offsets in the prompt, one per surviving occurrence. */
|
|
48
|
+
offsets: number[];
|
|
49
|
+
/** Tokens saved if every occurrence is applied. */
|
|
50
|
+
tokensSaved: number;
|
|
51
|
+
}
|
|
52
|
+
export interface SuggestResult {
|
|
53
|
+
suggestions: RewriteSuggestion[];
|
|
54
|
+
/** Suggestions the model returned that did not survive, with the reason. */
|
|
55
|
+
rejected: Array<{
|
|
56
|
+
before: string;
|
|
57
|
+
after: string;
|
|
58
|
+
reason: RejectedReason;
|
|
59
|
+
}>;
|
|
60
|
+
provider: string;
|
|
61
|
+
model: string;
|
|
62
|
+
}
|
|
63
|
+
export type RejectedReason =
|
|
64
|
+
/** `before` is not in the prompt. The model paraphrased what it quoted. */
|
|
65
|
+
'not-found'
|
|
66
|
+
/** It would edit a code block, URL, placeholder or tag. */
|
|
67
|
+
| 'touches-protected'
|
|
68
|
+
/** `after` introduces protected content that was not there. */
|
|
69
|
+
| 'introduces-protected'
|
|
70
|
+
/** No shorter than what it replaces. */
|
|
71
|
+
| 'no-saving'
|
|
72
|
+
/** It shares characters with a suggestion already accepted. */
|
|
73
|
+
| 'overlaps';
|
|
74
|
+
export declare const SUGGEST_SYSTEM_PROMPT = "You find phrases in a prompt that say something in more words than they need.\n\nReturn ONLY a JSON array. Each element is {\"before\": \"...\", \"after\": \"...\"}.\n\nRules:\n- \"before\" MUST be copied character for character from the prompt. Do not paraphrase it, do not fix its punctuation, do not change its capitalisation. If you cannot copy it exactly, leave it out.\n- \"after\" says the same thing in fewer words. Use \"\" to delete the phrase entirely.\n- Preserve meaning exactly. Never change what the prompt asks for, its constraints, its output format or its success criteria.\n- Never touch code, URLs, template placeholders ({{x}}, ${x}, {x}) or XML/HTML tags \u2014 do not include them in \"before\" at all.\n- Keep the original language of the prompt.\n- Prefer a few high-value rewrites to many trivial ones. Return [] if there is nothing worth changing.\n\nNo explanation, no code fences, no commentary. The array alone.";
|
|
75
|
+
export interface SuggestOptions {
|
|
76
|
+
tokenCounter?: TokenCounter;
|
|
77
|
+
locale?: Locale;
|
|
78
|
+
/** Cap on how many survive, highest saving first. Defaults to 20. */
|
|
79
|
+
max?: number;
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* Asks the provider for rewrites and returns the ones that survive checking.
|
|
83
|
+
*
|
|
84
|
+
* A provider that returns something unparseable yields an empty result rather
|
|
85
|
+
* than throwing: the deterministic rules have already run, and a malformed
|
|
86
|
+
* answer from an optional pass should cost the caller nothing.
|
|
87
|
+
*/
|
|
88
|
+
export declare function suggestRewrites(prompt: string, provider: LlmProvider, options?: SuggestOptions): Promise<SuggestResult>;
|
|
89
|
+
/**
|
|
90
|
+
* Applies suggestions to the prompt.
|
|
91
|
+
*
|
|
92
|
+
* Right to left, so an earlier edit cannot move the offsets of a later one —
|
|
93
|
+
* the bug that makes every naive implementation of this corrupt long prompts,
|
|
94
|
+
* and one that shows up only when two suggestions are far enough apart that a
|
|
95
|
+
* short test never notices.
|
|
96
|
+
*/
|
|
97
|
+
export declare function applyRewrites(prompt: string, suggestions: readonly RewriteSuggestion[]): string;
|
|
98
|
+
/** A reason code turned into a sentence, for a report. */
|
|
99
|
+
export declare function rejectionText(reason: RejectedReason, locale: Locale): string;
|
|
100
|
+
//# sourceMappingURL=suggest.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"suggest.d.ts","sourceRoot":"","sources":["../src/suggest.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,iBAAiB,CAAC;AAG9C,OAAO,KAAK,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAE5D;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAsCG;AAEH,MAAM,WAAW,iBAAiB;IAChC,yDAAyD;IACzD,MAAM,EAAE,MAAM,CAAC;IACf,2DAA2D;IAC3D,KAAK,EAAE,MAAM,CAAC;IACd,qEAAqE;IACrE,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,mDAAmD;IACnD,WAAW,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,aAAa;IAC5B,WAAW,EAAE,iBAAiB,EAAE,CAAC;IACjC,4EAA4E;IAC5E,QAAQ,EAAE,KAAK,CAAC;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,cAAc,CAAA;KAAE,CAAC,CAAC;IAC3E,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE,MAAM,CAAC;CACf;AAED,MAAM,MAAM,cAAc;AACxB,2EAA2E;AACzE,WAAW;AACb,2DAA2D;GACzD,mBAAmB;AACrB,+DAA+D;GAC7D,sBAAsB;AACxB,wCAAwC;GACtC,WAAW;AACb,+DAA+D;GAC7D,UAAU,CAAC;AAEf,eAAO,MAAM,qBAAqB,k7BAY8B,CAAC;AAEjE,MAAM,WAAW,cAAc;IAC7B,YAAY,CAAC,EAAE,YAAY,CAAC;IAC5B,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,qEAAqE;IACrE,GAAG,CAAC,EAAE,MAAM,CAAC;CACd;AAwCD;;;;;;GAMG;AACH,wBAAsB,eAAe,CACnC,MAAM,EAAE,MAAM,EACd,QAAQ,EAAE,WAAW,EACrB,OAAO,GAAE,cAAmB,GAC3B,OAAO,CAAC,aAAa,CAAC,CAiFxB;AAED;;;;;;;GAOG;AACH,wBAAgB,aAAa,CAAC,MAAM,EAAE,MAAM,EAAE,WAAW,EAAE,SAAS,iBAAiB,EAAE,GAAG,MAAM,CAU/F;AAED,0DAA0D;AAC1D,wBAAgB,aAAa,CAAC,MAAM,EAAE,cAAc,EAAE,MAAM,EAAE,MAAM,GAAG,MAAM,CAE5E"}
|