@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.
Files changed (223) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +163 -0
  3. package/dist/advisories.d.ts +23 -0
  4. package/dist/advisories.d.ts.map +1 -0
  5. package/dist/advisories.js +376 -0
  6. package/dist/advisories.js.map +1 -0
  7. package/dist/aws-sigv4.d.ts +88 -0
  8. package/dist/aws-sigv4.d.ts.map +1 -0
  9. package/dist/aws-sigv4.js +117 -0
  10. package/dist/aws-sigv4.js.map +1 -0
  11. package/dist/baseline.d.ts +171 -0
  12. package/dist/baseline.d.ts.map +1 -0
  13. package/dist/baseline.js +273 -0
  14. package/dist/baseline.js.map +1 -0
  15. package/dist/cache.d.ts +26 -0
  16. package/dist/cache.d.ts.map +1 -0
  17. package/dist/cache.js +28 -0
  18. package/dist/cache.js.map +1 -0
  19. package/dist/changes.d.ts +29 -0
  20. package/dist/changes.d.ts.map +1 -0
  21. package/dist/changes.js +142 -0
  22. package/dist/changes.js.map +1 -0
  23. package/dist/compare.d.ts +65 -0
  24. package/dist/compare.d.ts.map +1 -0
  25. package/dist/compare.js +58 -0
  26. package/dist/compare.js.map +1 -0
  27. package/dist/config-schema.d.ts +118 -0
  28. package/dist/config-schema.d.ts.map +1 -0
  29. package/dist/config-schema.js +315 -0
  30. package/dist/config-schema.js.map +1 -0
  31. package/dist/config.d.ts +47 -0
  32. package/dist/config.d.ts.map +1 -0
  33. package/dist/config.js +112 -0
  34. package/dist/config.js.map +1 -0
  35. package/dist/detect.d.ts +70 -0
  36. package/dist/detect.d.ts.map +1 -0
  37. package/dist/detect.js +228 -0
  38. package/dist/detect.js.map +1 -0
  39. package/dist/evaluate.d.ts +98 -0
  40. package/dist/evaluate.d.ts.map +1 -0
  41. package/dist/evaluate.js +110 -0
  42. package/dist/evaluate.js.map +1 -0
  43. package/dist/extract.d.ts +81 -0
  44. package/dist/extract.d.ts.map +1 -0
  45. package/dist/extract.js +280 -0
  46. package/dist/extract.js.map +1 -0
  47. package/dist/gcp-auth.d.ts +58 -0
  48. package/dist/gcp-auth.d.ts.map +1 -0
  49. package/dist/gcp-auth.js +113 -0
  50. package/dist/gcp-auth.js.map +1 -0
  51. package/dist/glob.d.ts +49 -0
  52. package/dist/glob.d.ts.map +1 -0
  53. package/dist/glob.js +154 -0
  54. package/dist/glob.js.map +1 -0
  55. package/dist/host.d.ts +30 -0
  56. package/dist/host.d.ts.map +1 -0
  57. package/dist/host.js +69 -0
  58. package/dist/host.js.map +1 -0
  59. package/dist/i18n/en.d.ts +4 -0
  60. package/dist/i18n/en.d.ts.map +1 -0
  61. package/dist/i18n/en.js +168 -0
  62. package/dist/i18n/en.js.map +1 -0
  63. package/dist/i18n/es.d.ts +4 -0
  64. package/dist/i18n/es.d.ts.map +1 -0
  65. package/dist/i18n/es.js +168 -0
  66. package/dist/i18n/es.js.map +1 -0
  67. package/dist/i18n/index.d.ts +36 -0
  68. package/dist/i18n/index.d.ts.map +1 -0
  69. package/dist/i18n/index.js +50 -0
  70. package/dist/i18n/index.js.map +1 -0
  71. package/dist/i18n/types.d.ts +180 -0
  72. package/dist/i18n/types.d.ts.map +1 -0
  73. package/dist/i18n/types.js +11 -0
  74. package/dist/i18n/types.js.map +1 -0
  75. package/dist/index.d.ts +66 -0
  76. package/dist/index.d.ts.map +1 -0
  77. package/dist/index.js +69 -0
  78. package/dist/index.js.map +1 -0
  79. package/dist/llm.d.ts +226 -0
  80. package/dist/llm.d.ts.map +1 -0
  81. package/dist/llm.js +485 -0
  82. package/dist/llm.js.map +1 -0
  83. package/dist/nearest.d.ts +20 -0
  84. package/dist/nearest.d.ts.map +1 -0
  85. package/dist/nearest.js +54 -0
  86. package/dist/nearest.js.map +1 -0
  87. package/dist/net.d.ts +90 -0
  88. package/dist/net.d.ts.map +1 -0
  89. package/dist/net.js +203 -0
  90. package/dist/net.js.map +1 -0
  91. package/dist/node.d.ts +32 -0
  92. package/dist/node.d.ts.map +1 -0
  93. package/dist/node.js +33 -0
  94. package/dist/node.js.map +1 -0
  95. package/dist/openrouter.d.ts +25 -0
  96. package/dist/openrouter.d.ts.map +1 -0
  97. package/dist/openrouter.js +72 -0
  98. package/dist/openrouter.js.map +1 -0
  99. package/dist/optimize.d.ts +38 -0
  100. package/dist/optimize.d.ts.map +1 -0
  101. package/dist/optimize.js +183 -0
  102. package/dist/optimize.js.map +1 -0
  103. package/dist/otlp.d.ts +91 -0
  104. package/dist/otlp.d.ts.map +1 -0
  105. package/dist/otlp.js +102 -0
  106. package/dist/otlp.js.map +1 -0
  107. package/dist/phrases.d.ts +169 -0
  108. package/dist/phrases.d.ts.map +1 -0
  109. package/dist/phrases.js +939 -0
  110. package/dist/phrases.js.map +1 -0
  111. package/dist/pricing-overlay.d.ts +55 -0
  112. package/dist/pricing-overlay.d.ts.map +1 -0
  113. package/dist/pricing-overlay.js +241 -0
  114. package/dist/pricing-overlay.js.map +1 -0
  115. package/dist/pricing.d.ts +115 -0
  116. package/dist/pricing.d.ts.map +1 -0
  117. package/dist/pricing.js +400 -0
  118. package/dist/pricing.js.map +1 -0
  119. package/dist/profile.d.ts +71 -0
  120. package/dist/profile.d.ts.map +1 -0
  121. package/dist/profile.js +55 -0
  122. package/dist/profile.js.map +1 -0
  123. package/dist/promptfoo.d.ts +58 -0
  124. package/dist/promptfoo.d.ts.map +1 -0
  125. package/dist/promptfoo.js +149 -0
  126. package/dist/promptfoo.js.map +1 -0
  127. package/dist/prune.d.ts +91 -0
  128. package/dist/prune.d.ts.map +1 -0
  129. package/dist/prune.js +110 -0
  130. package/dist/prune.js.map +1 -0
  131. package/dist/reorder.d.ts +82 -0
  132. package/dist/reorder.d.ts.map +1 -0
  133. package/dist/reorder.js +215 -0
  134. package/dist/reorder.js.map +1 -0
  135. package/dist/review.d.ts +54 -0
  136. package/dist/review.d.ts.map +1 -0
  137. package/dist/review.js +131 -0
  138. package/dist/review.js.map +1 -0
  139. package/dist/rules.d.ts +5 -0
  140. package/dist/rules.d.ts.map +1 -0
  141. package/dist/rules.js +279 -0
  142. package/dist/rules.js.map +1 -0
  143. package/dist/savings.d.ts +36 -0
  144. package/dist/savings.d.ts.map +1 -0
  145. package/dist/savings.js +83 -0
  146. package/dist/savings.js.map +1 -0
  147. package/dist/segment.d.ts +8 -0
  148. package/dist/segment.d.ts.map +1 -0
  149. package/dist/segment.js +74 -0
  150. package/dist/segment.js.map +1 -0
  151. package/dist/shared-prefix.d.ts +63 -0
  152. package/dist/shared-prefix.d.ts.map +1 -0
  153. package/dist/shared-prefix.js +151 -0
  154. package/dist/shared-prefix.js.map +1 -0
  155. package/dist/similarity.d.ts +13 -0
  156. package/dist/similarity.d.ts.map +1 -0
  157. package/dist/similarity.js +30 -0
  158. package/dist/similarity.js.map +1 -0
  159. package/dist/structure.d.ts +144 -0
  160. package/dist/structure.d.ts.map +1 -0
  161. package/dist/structure.js +455 -0
  162. package/dist/structure.js.map +1 -0
  163. package/dist/suggest.d.ts +100 -0
  164. package/dist/suggest.d.ts.map +1 -0
  165. package/dist/suggest.js +151 -0
  166. package/dist/suggest.js.map +1 -0
  167. package/dist/tokenizer.d.ts +57 -0
  168. package/dist/tokenizer.d.ts.map +1 -0
  169. package/dist/tokenizer.js +157 -0
  170. package/dist/tokenizer.js.map +1 -0
  171. package/dist/types.d.ts +296 -0
  172. package/dist/types.d.ts.map +1 -0
  173. package/dist/types.js +3 -0
  174. package/dist/types.js.map +1 -0
  175. package/dist/walk.d.ts +40 -0
  176. package/dist/walk.d.ts.map +1 -0
  177. package/dist/walk.js +95 -0
  178. package/dist/walk.js.map +1 -0
  179. package/package.json +56 -0
  180. package/src/advisories.ts +431 -0
  181. package/src/aws-sigv4.ts +174 -0
  182. package/src/baseline.ts +390 -0
  183. package/src/cache.ts +54 -0
  184. package/src/changes.ts +158 -0
  185. package/src/compare.ts +131 -0
  186. package/src/config-schema.ts +451 -0
  187. package/src/config.ts +161 -0
  188. package/src/detect.ts +312 -0
  189. package/src/evaluate.ts +188 -0
  190. package/src/extract.ts +336 -0
  191. package/src/gcp-auth.ts +166 -0
  192. package/src/glob.ts +160 -0
  193. package/src/host.ts +90 -0
  194. package/src/i18n/en.ts +236 -0
  195. package/src/i18n/es.ts +236 -0
  196. package/src/i18n/index.ts +68 -0
  197. package/src/i18n/types.ts +230 -0
  198. package/src/index.ts +228 -0
  199. package/src/llm.ts +708 -0
  200. package/src/nearest.ts +61 -0
  201. package/src/net.ts +233 -0
  202. package/src/node.ts +63 -0
  203. package/src/openrouter.ts +125 -0
  204. package/src/optimize.ts +228 -0
  205. package/src/otlp.ts +179 -0
  206. package/src/phrases.ts +1047 -0
  207. package/src/pricing-overlay.ts +319 -0
  208. package/src/pricing.ts +468 -0
  209. package/src/profile.ts +124 -0
  210. package/src/promptfoo.ts +213 -0
  211. package/src/prune.ts +211 -0
  212. package/src/reorder.ts +307 -0
  213. package/src/review.ts +180 -0
  214. package/src/rules.ts +324 -0
  215. package/src/savings.ts +121 -0
  216. package/src/segment.ts +106 -0
  217. package/src/shared-prefix.ts +198 -0
  218. package/src/similarity.ts +28 -0
  219. package/src/structure.ts +652 -0
  220. package/src/suggest.ts +254 -0
  221. package/src/tokenizer.ts +190 -0
  222. package/src/types.ts +323 -0
  223. package/src/walk.ts +117 -0
@@ -0,0 +1,215 @@
1
+ import { BACKWARD_REFERENCES_BY_LANGUAGE, UNCOVERED_SCRIPTS } from './phrases.js';
2
+ import { segment } from './segment.js';
3
+ import { estimateTokens } from './tokenizer.js';
4
+ /**
5
+ * Finds the offset of the first template placeholder.
6
+ *
7
+ * Uses the same segmentation as everything else, so "placeholder" means exactly
8
+ * what the protection pass means by it — there is no second definition to drift.
9
+ */
10
+ function firstPlaceholderOffset(prompt) {
11
+ let offset = 0;
12
+ for (const seg of segment(prompt)) {
13
+ if (seg.kind === 'protected' && seg.protection === 'placeholder')
14
+ return offset;
15
+ offset += seg.text.length;
16
+ }
17
+ return null;
18
+ }
19
+ /** Blank-line separated blocks, with their original separators preserved. */
20
+ function toBlocks(text) {
21
+ // Splitting on a blank line and keeping the delimiter means rejoining cannot
22
+ // silently normalise somebody's spacing.
23
+ //
24
+ // Written as a line scan rather than the `split(/(?<=\n)(?=\s*\n)/)` it
25
+ // replaces. That regex reads better and is quadratic in the length of a run
26
+ // of blank lines, because the lookahead re-consumes the whole run at every
27
+ // position in it: 3.3 seconds on 60 KB of newlines, in a library reachable
28
+ // over HTTP. Here every character is visited once.
29
+ const blocks = [];
30
+ let start = 0;
31
+ let i = 0;
32
+ while (i < text.length) {
33
+ const nl = text.indexOf('\n', i);
34
+ const end = nl === -1 ? text.length : nl + 1;
35
+ // A blank line opens a block, and travels with the block it precedes.
36
+ if (i > start && text.slice(i, end).trim() === '') {
37
+ blocks.push(text.slice(start, i));
38
+ start = i;
39
+ }
40
+ i = end;
41
+ }
42
+ if (start < text.length)
43
+ blocks.push(text.slice(start));
44
+ return blocks;
45
+ }
46
+ const hasPlaceholder = (text) => segment(text).some((s) => s.kind === 'protected' && s.protection === 'placeholder');
47
+ /**
48
+ * The backward reference in a block, if any.
49
+ *
50
+ * Lowercased on both sides because a prompt written in title case is still a
51
+ * prompt. Word-boundary matched for languages written with spaces, so
52
+ * "aboveboard" does not pin a block — and *not* for Japanese and Chinese, where
53
+ * the neighbouring character is always a letter and a boundary-matched phrase
54
+ * could never fire.
55
+ *
56
+ * Every language's list runs against every prompt. Detecting the language first
57
+ * would be one more thing to get wrong, and the cost of testing a French prompt
58
+ * against German phrases is a saving not taken, which is the direction this
59
+ * module errs in on purpose.
60
+ */
61
+ function backwardReference(text) {
62
+ const haystack = text.toLowerCase();
63
+ const boundary = (c) => c === undefined || !/[\p{L}\p{N}]/u.test(c);
64
+ for (const set of Object.values(BACKWARD_REFERENCES_BY_LANGUAGE)) {
65
+ for (const phrase of set.phrases) {
66
+ const index = haystack.indexOf(phrase);
67
+ if (index === -1)
68
+ continue;
69
+ if (!set.wordBoundaries)
70
+ return phrase;
71
+ if (boundary(haystack[index - 1]) && boundary(haystack[index + phrase.length])) {
72
+ return phrase;
73
+ }
74
+ }
75
+ }
76
+ return undefined;
77
+ }
78
+ /**
79
+ * A script the phrase lists do not cover, if the prompt is written in one.
80
+ *
81
+ * The fourth refusal, and the one that exists because of what the other three
82
+ * were quietly not doing. Every safety argument in this module rests on
83
+ * recognising a backward reference, and for a Russian, Arabic or Korean prompt
84
+ * there was nothing to recognise it with — so `--reorder` rearranged freely and
85
+ * called it a saving. Refusing is the honest answer until the list exists.
86
+ *
87
+ * Deliberately triggered by a *single* character rather than by the prompt being
88
+ * mostly that script. A prompt in English with one Cyrillic instruction in it is
89
+ * exactly the case where a missed reference does damage, and the cost of being
90
+ * wrong here is a saving the author can still take by hand.
91
+ */
92
+ function uncoveredScript(text) {
93
+ return UNCOVERED_SCRIPTS.find((script) => script.pattern.test(text))?.name;
94
+ }
95
+ /**
96
+ * Rearranges a prompt so its stable instructions sit in the cacheable prefix.
97
+ *
98
+ * Returns the original text unchanged when nothing can safely move, and always
99
+ * reports what it declined and why — a saving Trazum silently chose not to take
100
+ * is one the author cannot evaluate.
101
+ */
102
+ export function reorderForCache(prompt, options = {}) {
103
+ const count = options.count ?? estimateTokens;
104
+ const minPrefixTokens = options.minPrefixTokens ?? 0;
105
+ const unchanged = () => {
106
+ const prefix = firstPlaceholderOffset(prompt);
107
+ const prefixTokens = prefix === null ? count(prompt) : count(prompt.slice(0, prefix));
108
+ return {
109
+ text: prompt,
110
+ moved: [],
111
+ declined: [],
112
+ tokensMoved: 0,
113
+ prefixTokensBefore: prefixTokens,
114
+ prefixTokensAfter: prefixTokens,
115
+ };
116
+ };
117
+ const offset = firstPlaceholderOffset(prompt);
118
+ if (offset === null)
119
+ return unchanged();
120
+ // Before anything else: if the prompt is written in a script whose backward
121
+ // references this module cannot recognise, none of the refusals below mean
122
+ // anything. Nothing moves, and the report says which script and why.
123
+ const script = uncoveredScript(prompt);
124
+ if (script !== undefined) {
125
+ return {
126
+ ...unchanged(),
127
+ declined: [{ text: prompt.slice(offset), reason: 'uncovered-script', script }],
128
+ };
129
+ }
130
+ // The placeholder's own line stays with the content after it: "Customer
131
+ // message: {{message}}" is one unit, and splitting it would strand the label.
132
+ const lineStart = prompt.lastIndexOf('\n', offset) + 1;
133
+ const head = prompt.slice(0, lineStart);
134
+ const rest = prompt.slice(lineStart);
135
+ const blocks = toBlocks(rest);
136
+ const prefixTokensBefore = count(head);
137
+ const moved = [];
138
+ const declined = [];
139
+ const stay = [];
140
+ let pinned = false;
141
+ for (const [index, block] of blocks.entries()) {
142
+ // The first block holds the placeholder itself; it can never move.
143
+ if (index === 0 || hasPlaceholder(block)) {
144
+ stay.push(block);
145
+ continue;
146
+ }
147
+ if (pinned) {
148
+ // Everything after a pinned block stays, because moving it would change
149
+ // its order relative to the block that had to stay.
150
+ stay.push(block);
151
+ if (block.trim() !== '')
152
+ declined.push({ text: block, reason: 'after-pinned' });
153
+ continue;
154
+ }
155
+ const phrase = backwardReference(block);
156
+ if (phrase !== undefined) {
157
+ pinned = true;
158
+ stay.push(block);
159
+ declined.push({ text: block, reason: 'backward-reference', phrase });
160
+ continue;
161
+ }
162
+ if (block.trim() === '') {
163
+ stay.push(block);
164
+ continue;
165
+ }
166
+ moved.push({ text: block, tokens: count(block) });
167
+ }
168
+ const tokensMoved = moved.reduce((sum, b) => sum + b.tokens, 0);
169
+ // Against the prefix this would produce, not against the amount moved. A head
170
+ // that already clears the minimum gains from any block that joins it.
171
+ if (moved.length === 0 || prefixTokensBefore + tokensMoved < minPrefixTokens) {
172
+ // Report the refusals even when nothing moved: "no saving here" and "there
173
+ // was a saving and it was not safe to take" are different answers.
174
+ return { ...unchanged(), declined };
175
+ }
176
+ // Normalise the seams rather than concatenating raw slices: a block carries the
177
+ // blank line that preceded it, so joining head + block + rest naively leaves a
178
+ // three-newline gap where two belong.
179
+ //
180
+ // In the author's own line ending, though. Rejoining a CRLF prompt with bare
181
+ // newlines would rewrite every seam in a file nobody asked to reformat — and
182
+ // where the whole point is a byte-for-byte cache prefix, a changed byte is a
183
+ // changed price.
184
+ const gap = prompt.includes('\r\n') ? '\r\n\r\n' : '\n\n';
185
+ const movedText = moved
186
+ // `^\s*\n+` drops the blank-line separator the block carries without taking
187
+ // the first line's own indentation with it, which `trimStart` would. It is
188
+ // anchored, so it is tried from one position and stays linear.
189
+ .map((b) => b.text.replace(/^\s*\n+/, '').trimEnd())
190
+ .join(gap);
191
+ const before = head.trimEnd();
192
+ const after = stay.join('').replace(/^\s*\n+/, '').trimEnd();
193
+ // No leading gap when the placeholder was on the very first line: there is no
194
+ // head for the moved blocks to sit after, and emitting one would open the
195
+ // prompt with a blank line.
196
+ const body = before === '' ? `${movedText}${gap}${after}` : `${before}${gap}${movedText}${gap}${after}`;
197
+ // However the prompt ended, it still ends that way. A block carries the blank
198
+ // line that followed it, so trimming the seams without restoring the original
199
+ // ending either strands a newline at the end or drops the one that was there.
200
+ // Collapsing runs of blank lines is the whitespace rule's job, not this one's.
201
+ //
202
+ // Via `trimEnd` rather than `/\s*$/`, which is quadratic on a prompt that
203
+ // holds a long whitespace run and does not end in one: 31 seconds on 200 KB,
204
+ // under the 400 KB the HTTP API accepts.
205
+ const text = `${body}${prompt.slice(prompt.trimEnd().length)}`;
206
+ return {
207
+ text,
208
+ moved,
209
+ declined,
210
+ tokensMoved,
211
+ prefixTokensBefore,
212
+ prefixTokensAfter: count(text.slice(0, firstPlaceholderOffset(text) ?? text.length)),
213
+ };
214
+ }
215
+ //# sourceMappingURL=reorder.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"reorder.js","sourceRoot":"","sources":["../src/reorder.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,54 @@
1
+ import type { LlmProvider, TokenCounter } from './types.js';
2
+ /**
3
+ * Semantic review of few-shot examples.
4
+ *
5
+ * The deterministic detector in `structure.ts` finds near-copies and stops
6
+ * there, on purpose: a paraphrase — the same lesson in different words —
7
+ * scores around 0.54 on word overlap, close enough to two genuinely distinct
8
+ * examples (~0.20) that catching it would mean flagging examples that teach
9
+ * different things.
10
+ *
11
+ * Deciding that "arrived quickly" and "arrived fast" demonstrate the same
12
+ * pattern needs a model. So this lives behind the optional LLM layer, costs a
13
+ * call, and is never on the path of an ordinary `optimize()`.
14
+ *
15
+ * It only ever reports. Nothing here edits a prompt, which is what lets it be
16
+ * relaxed about a model that answers badly: the worst outcome is a suggestion
17
+ * you ignore.
18
+ */
19
+ export declare const EXAMPLE_REVIEW_SYSTEM_PROMPT = "You judge whether few-shot examples in a prompt teach the same thing.\n\nYou are given numbered examples. Group together any that demonstrate the same pattern \u2014 the same kind of input mapping to the same kind of output \u2014 even when they are worded completely differently. Examples that differ only in surface detail (names, numbers, dates) teach the same thing.\n\nDo NOT group examples that look similar but demonstrate different behaviour, and do NOT group an example that exists to show a boundary or edge case. When in doubt, leave an example on its own: a false grouping costs the reader more than a missed one.\n\nReturn ONLY a JSON array, no prose and no code fences. Each element is a group of two or more examples that teach the same thing:\n\n[{\"keep\": 0, \"redundant\": [2], \"reason\": \"both classify a missing delivery as shipping\"}]\n\n\"keep\" is the example worth keeping (usually the earliest), \"redundant\" lists the others, and \"reason\" is one short clause. Return [] when every example teaches something distinct.";
20
+ export interface ExampleRedundancy {
21
+ /** Index of the example worth keeping. */
22
+ keep: number;
23
+ /** Indices the model considers redundant with it. */
24
+ redundant: number[];
25
+ /** The model's one-clause justification, for the reader to judge. */
26
+ reason: string;
27
+ /** Tokens held by the redundant examples. */
28
+ tokens: number;
29
+ }
30
+ export interface ExampleReview {
31
+ provider: string;
32
+ model: string;
33
+ /** Examples found in the prompt, in order. */
34
+ exampleCount: number;
35
+ groups: ExampleRedundancy[];
36
+ /** Tokens across every example marked redundant. */
37
+ redundantTokens: number;
38
+ /** Set when the model answered but nothing usable came back. */
39
+ unusableResponse?: string;
40
+ }
41
+ export interface ReviewExamplesOptions {
42
+ tokenCounter?: TokenCounter;
43
+ }
44
+ /**
45
+ * Asks the configured model which examples teach the same thing.
46
+ *
47
+ * Returns `null` when there is nothing to review — fewer than two examples —
48
+ * so the caller can skip the call rather than pay for a certain answer.
49
+ *
50
+ * Never throws on a bad answer. A provider that errors will still throw,
51
+ * because that is a configuration problem the caller should see.
52
+ */
53
+ export declare function reviewExamples(prompt: string, provider: LlmProvider, options?: ReviewExamplesOptions): Promise<ExampleReview | null>;
54
+ //# sourceMappingURL=review.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"review.d.ts","sourceRoot":"","sources":["../src/review.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAE5D;;;;;;;;;;;;;;;;GAgBG;AAEH,eAAO,MAAM,4BAA4B,8hCAU4I,CAAC;AAEtL,MAAM,WAAW,iBAAiB;IAChC,0CAA0C;IAC1C,IAAI,EAAE,MAAM,CAAC;IACb,qDAAqD;IACrD,SAAS,EAAE,MAAM,EAAE,CAAC;IACpB,qEAAqE;IACrE,MAAM,EAAE,MAAM,CAAC;IACf,6CAA6C;IAC7C,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,aAAa;IAC5B,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE,MAAM,CAAC;IACd,8CAA8C;IAC9C,YAAY,EAAE,MAAM,CAAC;IACrB,MAAM,EAAE,iBAAiB,EAAE,CAAC;IAC5B,oDAAoD;IACpD,eAAe,EAAE,MAAM,CAAC;IACxB,gEAAgE;IAChE,gBAAgB,CAAC,EAAE,MAAM,CAAC;CAC3B;AAED,MAAM,WAAW,qBAAqB;IACpC,YAAY,CAAC,EAAE,YAAY,CAAC;CAC7B;AAwDD;;;;;;;;GAQG;AACH,wBAAsB,cAAc,CAClC,MAAM,EAAE,MAAM,EACd,QAAQ,EAAE,WAAW,EACrB,OAAO,GAAE,qBAA0B,GAClC,OAAO,CAAC,aAAa,GAAG,IAAI,CAAC,CAmD/B"}
package/dist/review.js ADDED
@@ -0,0 +1,131 @@
1
+ import { findExamples } from './structure.js';
2
+ import { estimateTokens } from './tokenizer.js';
3
+ /**
4
+ * Semantic review of few-shot examples.
5
+ *
6
+ * The deterministic detector in `structure.ts` finds near-copies and stops
7
+ * there, on purpose: a paraphrase — the same lesson in different words —
8
+ * scores around 0.54 on word overlap, close enough to two genuinely distinct
9
+ * examples (~0.20) that catching it would mean flagging examples that teach
10
+ * different things.
11
+ *
12
+ * Deciding that "arrived quickly" and "arrived fast" demonstrate the same
13
+ * pattern needs a model. So this lives behind the optional LLM layer, costs a
14
+ * call, and is never on the path of an ordinary `optimize()`.
15
+ *
16
+ * It only ever reports. Nothing here edits a prompt, which is what lets it be
17
+ * relaxed about a model that answers badly: the worst outcome is a suggestion
18
+ * you ignore.
19
+ */
20
+ export const EXAMPLE_REVIEW_SYSTEM_PROMPT = `You judge whether few-shot examples in a prompt teach the same thing.
21
+
22
+ You are given numbered examples. Group together any that demonstrate the same pattern — the same kind of input mapping to the same kind of output — even when they are worded completely differently. Examples that differ only in surface detail (names, numbers, dates) teach the same thing.
23
+
24
+ Do NOT group examples that look similar but demonstrate different behaviour, and do NOT group an example that exists to show a boundary or edge case. When in doubt, leave an example on its own: a false grouping costs the reader more than a missed one.
25
+
26
+ Return ONLY a JSON array, no prose and no code fences. Each element is a group of two or more examples that teach the same thing:
27
+
28
+ [{"keep": 0, "redundant": [2], "reason": "both classify a missing delivery as shipping"}]
29
+
30
+ "keep" is the example worth keeping (usually the earliest), "redundant" lists the others, and "reason" is one short clause. Return [] when every example teaches something distinct.`;
31
+ /** Pulls the first JSON array out of a response, fences and prose included. */
32
+ function extractJsonArray(raw) {
33
+ const trimmed = raw.trim();
34
+ const fenced = /^(?:```|~~~)[a-zA-Z]*\n([\s\S]*?)\n?(?:```|~~~)$/.exec(trimmed);
35
+ const body = fenced?.[1] ?? trimmed;
36
+ const start = body.indexOf('[');
37
+ const end = body.lastIndexOf(']');
38
+ if (start === -1 || end === -1 || end < start)
39
+ return null;
40
+ try {
41
+ return JSON.parse(body.slice(start, end + 1));
42
+ }
43
+ catch {
44
+ return null;
45
+ }
46
+ }
47
+ /**
48
+ * Validates one group against the examples that actually exist.
49
+ *
50
+ * Everything the model returns is treated as a suggestion from an untrusted
51
+ * source: indices are range-checked, self-references dropped, duplicates
52
+ * collapsed, and the reason truncated. A model that answers with nonsense
53
+ * produces an empty review, not a crash and not a bogus saving.
54
+ */
55
+ function parseGroup(value, exampleCount) {
56
+ if (typeof value !== 'object' || value === null)
57
+ return null;
58
+ const record = value;
59
+ const keep = record.keep;
60
+ if (typeof keep !== 'number' || !Number.isInteger(keep) || keep < 0 || keep >= exampleCount) {
61
+ return null;
62
+ }
63
+ const rawRedundant = Array.isArray(record.redundant) ? record.redundant : [];
64
+ const redundant = [
65
+ ...new Set(rawRedundant.filter((index) => typeof index === 'number' &&
66
+ Number.isInteger(index) &&
67
+ index >= 0 &&
68
+ index < exampleCount &&
69
+ index !== keep)),
70
+ ].sort((a, b) => a - b);
71
+ if (redundant.length === 0)
72
+ return null;
73
+ const reason = typeof record.reason === 'string' ? record.reason.trim().slice(0, 160) : '';
74
+ return { keep, redundant, reason };
75
+ }
76
+ /**
77
+ * Asks the configured model which examples teach the same thing.
78
+ *
79
+ * Returns `null` when there is nothing to review — fewer than two examples —
80
+ * so the caller can skip the call rather than pay for a certain answer.
81
+ *
82
+ * Never throws on a bad answer. A provider that errors will still throw,
83
+ * because that is a configuration problem the caller should see.
84
+ */
85
+ export async function reviewExamples(prompt, provider, options = {}) {
86
+ const count = options.tokenCounter ?? estimateTokens;
87
+ const examples = findExamples(prompt, count);
88
+ if (examples.length < 2)
89
+ return null;
90
+ const numbered = examples
91
+ .map((example, index) => `--- Example ${index} ---\n${example.text}`)
92
+ .join('\n\n');
93
+ const raw = await provider.complete({
94
+ system: EXAMPLE_REVIEW_SYSTEM_PROMPT,
95
+ user: numbered,
96
+ });
97
+ const base = {
98
+ provider: provider.name,
99
+ model: provider.model,
100
+ exampleCount: examples.length,
101
+ };
102
+ const parsed = extractJsonArray(raw);
103
+ if (!Array.isArray(parsed)) {
104
+ return { ...base, groups: [], redundantTokens: 0, unusableResponse: raw.trim().slice(0, 200) };
105
+ }
106
+ // An example can only be claimed once. Without this a model that returns
107
+ // overlapping groups would have the same tokens counted twice, and the
108
+ // saving would read higher than the prompt could possibly deliver.
109
+ const claimed = new Set();
110
+ const groups = [];
111
+ for (const entry of parsed) {
112
+ const group = parseGroup(entry, examples.length);
113
+ if (!group)
114
+ continue;
115
+ const fresh = group.redundant.filter((index) => !claimed.has(index) && index !== group.keep);
116
+ if (fresh.length === 0)
117
+ continue;
118
+ fresh.forEach((index) => claimed.add(index));
119
+ groups.push({
120
+ ...group,
121
+ redundant: fresh,
122
+ tokens: fresh.reduce((sum, index) => sum + examples[index].tokens, 0),
123
+ });
124
+ }
125
+ return {
126
+ ...base,
127
+ groups,
128
+ redundantTokens: groups.reduce((sum, group) => sum + group.tokens, 0),
129
+ };
130
+ }
131
+ //# sourceMappingURL=review.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"review.js","sourceRoot":"","sources":["../src/review.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,5 @@
1
+ import type { Rule } from './types.js';
2
+ /** Every rule, in the order they must run. */
3
+ export declare const RULES: readonly Rule[];
4
+ export declare function getRule(id: string): Rule | undefined;
5
+ //# sourceMappingURL=rules.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rules.d.ts","sourceRoot":"","sources":["../src/rules.ts"],"names":[],"mappings":"AAaA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,YAAY,CAAC;AAiSvC,8CAA8C;AAC9C,eAAO,MAAM,KAAK,EAAE,SAAS,IAAI,EAgBhC,CAAC;AAEF,wBAAgB,OAAO,CAAC,EAAE,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,CAEpD"}