@rudra-js/attested 0.0.0 → 0.4.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.
@@ -0,0 +1 @@
1
+ {"version":3,"file":"numerals.js","sourceRoot":"","sources":["../src/numerals.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,13 @@
1
+ /** Claims with no value behind them. Stored in the form `normalisePhrasing` produces. */
2
+ export declare const BANNED_PHRASES: readonly string[];
3
+ /** One shape for text and phrases, so a hyphen or a stray mark cannot hide a claim. */
4
+ export declare function normalisePhrasing(text: string): string;
5
+ export interface Span {
6
+ start: number;
7
+ /** The last character of the match, not one past it. */
8
+ end: number;
9
+ }
10
+ /** Both arguments must already be normalised. Spans index the text passed in. */
11
+ export declare function phraseSpans(text: string, phrase: string): Span[];
12
+ export declare function phraseIn(text: string, phrase: string): boolean;
13
+ //# sourceMappingURL=phrases.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"phrases.d.ts","sourceRoot":"","sources":["../src/phrases.ts"],"names":[],"mappings":"AAEA,yFAAyF;AACzF,eAAO,MAAM,cAAc,EAAE,SAAS,MAAM,EAkF3C,CAAC;AA0CF,uFAAuF;AACvF,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAetD;AAQD,MAAM,WAAW,IAAI;IACnB,KAAK,EAAE,MAAM,CAAC;IACd,wDAAwD;IACxD,GAAG,EAAE,MAAM,CAAC;CACb;AAED,iFAAiF;AAGjF,wBAAgB,WAAW,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,IAAI,EAAE,CAuChE;AAED,wBAAgB,QAAQ,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAE9D"}
@@ -0,0 +1,188 @@
1
+ import { stripInvisible, stripMarks } from './hidden.js';
2
+ /** Claims with no value behind them. Stored in the form `normalisePhrasing` produces. */
3
+ export const BANNED_PHRASES = [
4
+ 'cheap',
5
+ 'cheaper',
6
+ 'cheapest',
7
+ 'affordable',
8
+ 'bargain',
9
+ 'great value',
10
+ 'best value',
11
+ 'low price',
12
+ 'lower price',
13
+ 'lowest price',
14
+ 'best price',
15
+ 'price drop',
16
+ 'price dropped',
17
+ 'just dropped',
18
+ 'price cut',
19
+ 'reduced price',
20
+ 'sale',
21
+ 'marked down',
22
+ 'half price',
23
+ 'half off',
24
+ 'discount',
25
+ 'discounted',
26
+ 'clearance',
27
+ 'free delivery',
28
+ 'free shipping',
29
+ 'free postage',
30
+ 'free returns',
31
+ 'ships free',
32
+ 'fast delivery',
33
+ 'next day',
34
+ 'same day',
35
+ 'overnight delivery',
36
+ 'arrives tomorrow',
37
+ 'delivered tomorrow',
38
+ 'by tomorrow',
39
+ 'in stock',
40
+ 'out of stock',
41
+ 'low stock',
42
+ 'limited stock',
43
+ 'running low',
44
+ 'running out',
45
+ 'back in stock',
46
+ 'restocked',
47
+ 'sold out',
48
+ 'selling out',
49
+ 'almost sold out',
50
+ 'nearly sold out',
51
+ 'selling fast',
52
+ 'going fast',
53
+ 'going quick',
54
+ 'flying off',
55
+ 'while stocks last',
56
+ 'almost gone',
57
+ 'nearly gone',
58
+ 'few left',
59
+ 'last few',
60
+ 'last chance',
61
+ 'limited time',
62
+ 'today only',
63
+ 'ends soon',
64
+ 'hurry',
65
+ 'act fast',
66
+ 'do not miss',
67
+ "don't miss",
68
+ 'best seller',
69
+ 'best selling',
70
+ 'bestseller',
71
+ 'bestselling',
72
+ 'most popular',
73
+ 'popular pick',
74
+ 'top pick',
75
+ 'top rated',
76
+ 'highly rated',
77
+ 'highest rated',
78
+ 'rated highest',
79
+ 'best rated',
80
+ 'five star',
81
+ 'star rating',
82
+ 'customer favourite',
83
+ 'customer favorite',
84
+ 'money back',
85
+ ];
86
+ /**
87
+ * Letters that render as a Latin letter but are not one. Folded so a Cyrillic о in
88
+ * "in stоck" cannot walk a claim past its own entry on the list. Both the text and
89
+ * the phrase are folded, so a genuine Cyrillic phrase still matches itself.
90
+ */
91
+ const CONFUSABLES = {
92
+ а: 'a',
93
+ в: 'b',
94
+ е: 'e',
95
+ к: 'k',
96
+ м: 'm',
97
+ н: 'h',
98
+ о: 'o',
99
+ р: 'p',
100
+ с: 'c',
101
+ т: 't',
102
+ х: 'x',
103
+ у: 'y',
104
+ ѕ: 's',
105
+ і: 'i',
106
+ ј: 'j',
107
+ ԁ: 'd',
108
+ һ: 'h',
109
+ ӏ: 'l',
110
+ α: 'a',
111
+ ε: 'e',
112
+ ι: 'i',
113
+ κ: 'k',
114
+ ν: 'v',
115
+ ο: 'o',
116
+ ρ: 'p',
117
+ τ: 't',
118
+ υ: 'u',
119
+ χ: 'x',
120
+ ϲ: 'c',
121
+ };
122
+ /** A run of whitespace holding one of these is a break the shopper sees. */
123
+ const LINE_BREAK = /[\n\v\f\r\u0085\u2028\u2029]/;
124
+ /** One shape for text and phrases, so a hyphen or a stray mark cannot hide a claim. */
125
+ export function normalisePhrasing(text) {
126
+ // Marks come off after composition, so an accented e keeps its accent while an
127
+ // overline, which composes with nothing, does not hide `free` from its own entry.
128
+ const lowered = stripMarks(stripInvisible(text).normalize('NFKC').toLowerCase().normalize('NFC'));
129
+ let folded = '';
130
+ for (const char of lowered)
131
+ folded += CONFUSABLES[char] ?? char;
132
+ // A line break survives as itself. Matching reads straight through it, and only
133
+ // the allowance in verify.ts cares, because it will not forgive across a break.
134
+ return folded
135
+ .replace(/[\u2018\u2019]/g, "'")
136
+ .replace(/[-_\u2010-\u2015]+/g, ' ')
137
+ .replace(/\s+/g, (run) => (LINE_BREAK.test(run) ? '\n' : ' '))
138
+ .trim();
139
+ }
140
+ const ASCII_WORD = /[a-z0-9]/;
141
+ function isGlued(char) {
142
+ return char !== undefined && ASCII_WORD.test(char);
143
+ }
144
+ /** Both arguments must already be normalised. Spans index the text passed in. */
145
+ // The word-gap rule only guards an ASCII edge, so `cheap` misses `cheapskate`
146
+ // while a Japanese phrase — which has no word gaps to find — still matches.
147
+ export function phraseSpans(text, phrase) {
148
+ // Matching runs with every space dropped, because 送料 無料 is 送料無料 to anyone
149
+ // and a space is free. `spots` carries each character back to where it really sat,
150
+ // so the word-gap guard still reads the neighbours the shopper sees.
151
+ const tight = [];
152
+ const spots = [];
153
+ for (let index = 0; index < text.length; index += 1) {
154
+ const char = text[index] ?? '';
155
+ // A line break reads like a space here, so a claim split over two lines is caught.
156
+ if (char === ' ' || char === '\n')
157
+ continue;
158
+ tight.push(char);
159
+ spots.push(index);
160
+ }
161
+ const spans = [];
162
+ const needle = phrase.replaceAll(' ', '').replaceAll('\n', '');
163
+ if (needle.length === 0)
164
+ return spans;
165
+ const guardStart = ASCII_WORD.test(needle[0] ?? '');
166
+ const guardEnd = ASCII_WORD.test(needle[needle.length - 1] ?? '');
167
+ const haystack = tight.join('');
168
+ let from = 0;
169
+ for (;;) {
170
+ const at = haystack.indexOf(needle, from);
171
+ if (at === -1)
172
+ return spans;
173
+ const start = spots[at] ?? 0;
174
+ const end = spots[at + needle.length - 1] ?? 0;
175
+ const next = text[end + 1];
176
+ // A trailing s is a plural, not a different word: `discount` catches `discounts`.
177
+ const plural = next === 's' && !isGlued(text[end + 2]);
178
+ const before = guardStart && isGlued(text[start - 1]);
179
+ const after = guardEnd && isGlued(next) && !plural;
180
+ if (!before && !after)
181
+ spans.push({ start, end });
182
+ from = at + 1;
183
+ }
184
+ }
185
+ export function phraseIn(text, phrase) {
186
+ return phraseSpans(text, phrase).length > 0;
187
+ }
188
+ //# sourceMappingURL=phrases.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"phrases.js","sourceRoot":"","sources":["../src/phrases.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,50 @@
1
+ export type Layer = 'quantity' | 'wording';
2
+ export interface Finding {
3
+ layer: Layer;
4
+ /** The numeral as the text wrote it, or the phrase as the denylist holds it. */
5
+ token: string;
6
+ /** Audit evidence. Names the layer and the token. */
7
+ reason: string;
8
+ }
9
+ export interface LayerReport {
10
+ supported: boolean;
11
+ /**
12
+ * What this layer's verdict is worth. `proof` means every numeral in the text is
13
+ * a value the host supplied — not that the sentence around it is true.
14
+ */
15
+ strength: 'proof' | 'best-effort';
16
+ /** Numerals found, or phrases screened against. Zero means nothing was checked. */
17
+ checked: number;
18
+ /** One per distinct token that failed. */
19
+ findings: Finding[];
20
+ }
21
+ export interface VerifyResult {
22
+ /** True when both layers are. */
23
+ supported: boolean;
24
+ quantity: LayerReport;
25
+ wording: LayerReport;
26
+ }
27
+ export interface Facts {
28
+ /** In any shape they are written: `39`, `'$1,299.00'`, `'4,8'`. Every numeral in one counts. */
29
+ values: readonly (string | number | bigint)[];
30
+ /** Claims to ban on top of the built-in English list, for the host's own language. */
31
+ bannedPhrases?: readonly string[];
32
+ /** Wording the shop stands behind. A banned claim inside one of these is not reported. */
33
+ allowedPhrases?: readonly string[];
34
+ }
35
+ export interface FieldResult {
36
+ field: string;
37
+ result: VerifyResult;
38
+ }
39
+ export interface BatchResult {
40
+ /** True when every field is, and when the fields read as one carry no banned claim. */
41
+ supported: boolean;
42
+ fields: FieldResult[];
43
+ /** The wording layer over every field joined, so a phrase split across two fields still reads. */
44
+ acrossFields: LayerReport;
45
+ }
46
+ /** Whether one piece of model-written text stands on the facts the host supplied. */
47
+ export declare function verify(text: string, facts: Facts): VerifyResult;
48
+ /** The same check over every field of one product, without repeating the facts. */
49
+ export declare function verifyFields(fields: Record<string, string>, facts: Facts): BatchResult;
50
+ //# sourceMappingURL=verify.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"verify.d.ts","sourceRoot":"","sources":["../src/verify.ts"],"names":[],"mappings":"AAKA,MAAM,MAAM,KAAK,GAAG,UAAU,GAAG,SAAS,CAAC;AAE3C,MAAM,WAAW,OAAO;IACtB,KAAK,EAAE,KAAK,CAAC;IACb,gFAAgF;IAChF,KAAK,EAAE,MAAM,CAAC;IACd,qDAAqD;IACrD,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,WAAW;IAC1B,SAAS,EAAE,OAAO,CAAC;IACnB;;;OAGG;IACH,QAAQ,EAAE,OAAO,GAAG,aAAa,CAAC;IAClC,mFAAmF;IACnF,OAAO,EAAE,MAAM,CAAC;IAChB,0CAA0C;IAC1C,QAAQ,EAAE,OAAO,EAAE,CAAC;CACrB;AAED,MAAM,WAAW,YAAY;IAC3B,iCAAiC;IACjC,SAAS,EAAE,OAAO,CAAC;IACnB,QAAQ,EAAE,WAAW,CAAC;IACtB,OAAO,EAAE,WAAW,CAAC;CACtB;AAED,MAAM,WAAW,KAAK;IACpB,gGAAgG;IAChG,MAAM,EAAE,SAAS,CAAC,MAAM,GAAG,MAAM,GAAG,MAAM,CAAC,EAAE,CAAC;IAC9C,sFAAsF;IACtF,aAAa,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IAClC,0FAA0F;IAC1F,cAAc,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;CACpC;AAED,MAAM,WAAW,WAAW;IAC1B,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,YAAY,CAAC;CACtB;AAED,MAAM,WAAW,WAAW;IAC1B,uFAAuF;IACvF,SAAS,EAAE,OAAO,CAAC;IACnB,MAAM,EAAE,WAAW,EAAE,CAAC;IACtB,kGAAkG;IAClG,YAAY,EAAE,WAAW,CAAC;CAC3B;AA8GD,qFAAqF;AACrF,wBAAgB,MAAM,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,KAAK,GAAG,YAAY,CAE/D;AAED,mFAAmF;AACnF,wBAAgB,YAAY,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,EAAE,KAAK,EAAE,KAAK,GAAG,WAAW,CAqBtF"}
package/dist/verify.js ADDED
@@ -0,0 +1,124 @@
1
+ import { numeralsIn, supportedValues } from './numerals.js';
2
+ import { BANNED_PHRASES, normalisePhrasing, phraseSpans } from './phrases.js';
3
+ function phrasesOf(facts) {
4
+ const phrases = new Set(BANNED_PHRASES);
5
+ for (const phrase of facts.bannedPhrases ?? []) {
6
+ const normalised = normalisePhrasing(phrase);
7
+ if (normalised.length > 0)
8
+ phrases.add(normalised);
9
+ }
10
+ return [...phrases];
11
+ }
12
+ function allowedOf(facts) {
13
+ const allowed = [];
14
+ for (const phrase of facts.allowedPhrases ?? []) {
15
+ const normalised = normalisePhrasing(phrase);
16
+ if (normalised.length > 0)
17
+ allowed.push(normalised);
18
+ }
19
+ return allowed;
20
+ }
21
+ function reasonFor(numeral) {
22
+ if (numeral.kind === 'other-numeral') {
23
+ return `quantity: "${numeral.token}" is a numeric character this layer cannot read, so no fact can back it`;
24
+ }
25
+ if (numeral.kind === 'magnitude') {
26
+ return `quantity: "${numeral.token}" ends in a magnitude mark this layer cannot read, so the value it shows is unchecked`;
27
+ }
28
+ return `quantity: "${numeral.token}" is not a value the supplied facts carry`;
29
+ }
30
+ function checkQuantities(text, supported) {
31
+ const numerals = numeralsIn(text);
32
+ const findings = [];
33
+ const reported = new Set();
34
+ for (const numeral of numerals) {
35
+ let backed = false;
36
+ for (const form of numeral.forms) {
37
+ if (supported.has(form))
38
+ backed = true;
39
+ }
40
+ if (backed || reported.has(numeral.token))
41
+ continue;
42
+ reported.add(numeral.token);
43
+ findings.push({ layer: 'quantity', token: numeral.token, reason: reasonFor(numeral) });
44
+ }
45
+ return {
46
+ supported: findings.length === 0,
47
+ strength: 'proof',
48
+ checked: numerals.length,
49
+ findings,
50
+ };
51
+ }
52
+ function standsBehind(allowed, hit) {
53
+ for (const span of allowed) {
54
+ if (span.start <= hit.start && hit.end <= span.end)
55
+ return true;
56
+ }
57
+ return false;
58
+ }
59
+ function checkWording(text, phrases, allowed) {
60
+ const normalised = normalisePhrasing(text);
61
+ const spans = [];
62
+ for (const phrase of allowed) {
63
+ for (const span of phraseSpans(normalised, phrase)) {
64
+ // The denylist reads through a break and the allowance does not, so both rules
65
+ // err toward reporting. A model that puts the negation in one block and the
66
+ // claim in another has written two things the shopper reads apart.
67
+ if (!normalised.slice(span.start, span.end + 1).includes('\n'))
68
+ spans.push(span);
69
+ }
70
+ }
71
+ const findings = [];
72
+ for (const phrase of phrases) {
73
+ // One hit inside allowed wording is forgiven; the same phrase elsewhere is not.
74
+ let caught = false;
75
+ for (const hit of phraseSpans(normalised, phrase)) {
76
+ if (!standsBehind(spans, hit))
77
+ caught = true;
78
+ }
79
+ if (!caught)
80
+ continue;
81
+ findings.push({
82
+ layer: 'wording',
83
+ token: phrase,
84
+ reason: `wording: "${phrase}" is a claim with no value to check, caught by a best-effort denylist`,
85
+ });
86
+ }
87
+ return {
88
+ supported: findings.length === 0,
89
+ strength: 'best-effort',
90
+ checked: phrases.length,
91
+ findings,
92
+ };
93
+ }
94
+ function check(text, supported, phrases, allowed) {
95
+ const quantity = checkQuantities(text, supported);
96
+ const wording = checkWording(text, phrases, allowed);
97
+ return { supported: quantity.supported && wording.supported, quantity, wording };
98
+ }
99
+ /** Whether one piece of model-written text stands on the facts the host supplied. */
100
+ export function verify(text, facts) {
101
+ return check(text, supportedValues(facts.values), phrasesOf(facts), allowedOf(facts));
102
+ }
103
+ /** The same check over every field of one product, without repeating the facts. */
104
+ export function verifyFields(fields, facts) {
105
+ const supported = supportedValues(facts.values);
106
+ const phrases = phrasesOf(facts);
107
+ const allowed = allowedOf(facts);
108
+ const results = [];
109
+ const written = [];
110
+ let allSupported = true;
111
+ for (const field of Object.keys(fields)) {
112
+ const text = fields[field] ?? '';
113
+ const result = check(text, supported, phrases, allowed);
114
+ if (!result.supported)
115
+ allSupported = false;
116
+ results.push({ field, result });
117
+ written.push(text);
118
+ }
119
+ // A card renders its fields next to each other, so `Free` and `delivery on every
120
+ // order` are one sentence to a shopper even though neither field carries one.
121
+ const acrossFields = checkWording(written.join(' '), phrases, allowed);
122
+ return { supported: allSupported && acrossFields.supported, fields: results, acrossFields };
123
+ }
124
+ //# sourceMappingURL=verify.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"verify.js","sourceRoot":"","sources":["../src/verify.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,14 +1,50 @@
1
1
  {
2
2
  "name": "@rudra-js/attested",
3
- "version": "0.0.0",
4
- "description": "Placeholder so a trusted publisher can be configured. Use 0.4.0 or later.",
3
+ "version": "0.4.0",
4
+ "description": "Checks model-written product copy against the facts a shop stands behind",
5
+ "keywords": [
6
+ "llm",
7
+ "hallucination",
8
+ "grounding",
9
+ "verification",
10
+ "ecommerce",
11
+ "guardrails"
12
+ ],
5
13
  "license": "MIT",
6
14
  "author": "Clive Dsouza",
7
15
  "homepage": "https://github.com/clivedsouza1010/rudra-js/tree/main/packages/attested#readme",
16
+ "bugs": {
17
+ "url": "https://github.com/clivedsouza1010/rudra-js/issues"
18
+ },
8
19
  "repository": {
9
20
  "type": "git",
10
21
  "url": "git+https://github.com/clivedsouza1010/rudra-js.git",
11
22
  "directory": "packages/attested"
12
23
  },
13
- "private": false
24
+ "engines": {
25
+ "node": ">=22.12.0"
26
+ },
27
+ "type": "module",
28
+ "main": "./dist/index.js",
29
+ "types": "./dist/index.d.ts",
30
+ "exports": {
31
+ ".": {
32
+ "types": "./dist/index.d.ts",
33
+ "default": "./dist/index.js"
34
+ },
35
+ "./package.json": "./package.json"
36
+ },
37
+ "files": [
38
+ "dist",
39
+ "src/**/*.ts",
40
+ "!src/**/*.test.ts"
41
+ ],
42
+ "sideEffects": false,
43
+ "scripts": {
44
+ "build": "tsc -p tsconfig.json",
45
+ "prepack": "npm run build"
46
+ },
47
+ "publishConfig": {
48
+ "access": "public"
49
+ }
14
50
  }
package/src/hidden.ts ADDED
@@ -0,0 +1,26 @@
1
+ // One definition for both layers. It lived in two files, and that is why widening it
2
+ // for the quantity layer left the wording layer open twice running.
3
+
4
+ /**
5
+ * Characters that render as nothing: format characters, default-ignorable code points
6
+ * and the controls. Cf and Default_Ignorable each hold characters the other does not.
7
+ */
8
+ const INVISIBLE = /[\p{Cf}\p{Default_Ignorable_Code_Point}\p{Cc}]/gu;
9
+
10
+ /** The controls that do take room. Deleting these would read `Only 2\n3 left` as 23. */
11
+ const SPACING = /[\t\n\v\f\r\u0085]/;
12
+
13
+ /**
14
+ * Marks that hang on the character before them instead of taking a column of their own,
15
+ * so `4<U+0305>9` reads as 49. Spacing marks (Mc) are not here: a Devanagari matra is
16
+ * a visible gap between two digits and separates them for a reader.
17
+ */
18
+ const MARKS = /[\p{Mn}\p{Me}]/gu;
19
+
20
+ export function stripInvisible(text: string): string {
21
+ return text.replace(INVISIBLE, (char) => (SPACING.test(char) ? char : ''));
22
+ }
23
+
24
+ export function stripMarks(text: string): string {
25
+ return text.replace(MARKS, '');
26
+ }
package/src/index.ts ADDED
@@ -0,0 +1,15 @@
1
+ // @rudra-js/attested — the guarantee that a model cannot lie about a product.
2
+
3
+ export {
4
+ verify,
5
+ verifyFields,
6
+ type BatchResult,
7
+ type Facts,
8
+ type FieldResult,
9
+ type Finding,
10
+ type Layer,
11
+ type LayerReport,
12
+ type VerifyResult,
13
+ } from './verify.js';
14
+
15
+ export { BANNED_PHRASES } from './phrases.js';