llm-entropy-filter 1.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/LICENSE +93 -0
- package/README.md +178 -0
- package/dist/index.cjs +265 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +53 -0
- package/dist/index.d.ts +53 -0
- package/dist/index.js +233 -0
- package/dist/index.js.map +1 -0
- package/package.json +64 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
Apache License
|
|
2
|
+
Version 2.0, January 2004
|
|
3
|
+
http://www.apache.org/licenses/
|
|
4
|
+
|
|
5
|
+
Copyright 2026 Ernesto Rosati
|
|
6
|
+
|
|
7
|
+
Licensed under the Apache License, Version 2.0 (the "License");
|
|
8
|
+
you may not use this file except in compliance with the License.
|
|
9
|
+
You may obtain a copy of the License at
|
|
10
|
+
|
|
11
|
+
http://www.apache.org/licenses/LICENSE-2.0
|
|
12
|
+
|
|
13
|
+
Unless required by applicable law or agreed to in writing, software
|
|
14
|
+
distributed under the License is distributed on an "AS IS" BASIS,
|
|
15
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
16
|
+
See the License for the specific language governing permissions and
|
|
17
|
+
limitations under the License.
|
|
18
|
+
|
|
19
|
+
--------------------------------------------------------------------
|
|
20
|
+
|
|
21
|
+
Apache License
|
|
22
|
+
Version 2.0, January 2004
|
|
23
|
+
|
|
24
|
+
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
|
25
|
+
|
|
26
|
+
1. Definitions.
|
|
27
|
+
|
|
28
|
+
"License" shall mean the terms and conditions for use, reproduction,
|
|
29
|
+
and distribution as defined by Sections 1 through 9 of this document.
|
|
30
|
+
|
|
31
|
+
"Licensor" shall mean the copyright owner or entity authorized by
|
|
32
|
+
the copyright owner that is granting the License.
|
|
33
|
+
|
|
34
|
+
"Legal Entity" shall mean the union of the acting entity and all
|
|
35
|
+
other entities that control, are controlled by, or are under common
|
|
36
|
+
control with that entity.
|
|
37
|
+
|
|
38
|
+
"You" (or "Your") shall mean an individual or Legal Entity
|
|
39
|
+
exercising permissions granted by this License.
|
|
40
|
+
|
|
41
|
+
"Source" form shall mean the preferred form for making modifications,
|
|
42
|
+
including but not limited to software source code.
|
|
43
|
+
|
|
44
|
+
"Object" form shall mean any form resulting from mechanical
|
|
45
|
+
transformation or translation of a Source form.
|
|
46
|
+
|
|
47
|
+
"Work" shall mean the work of authorship made available under the License.
|
|
48
|
+
|
|
49
|
+
"Derivative Works" shall mean any work based upon the Work.
|
|
50
|
+
|
|
51
|
+
2. Grant of Copyright License.
|
|
52
|
+
|
|
53
|
+
Subject to the terms and conditions of this License, each Contributor
|
|
54
|
+
hereby grants You a perpetual, worldwide, non-exclusive, no-charge,
|
|
55
|
+
royalty-free, irrevocable copyright license to reproduce, prepare
|
|
56
|
+
Derivative Works of, publicly display, publicly perform, sublicense,
|
|
57
|
+
and distribute the Work.
|
|
58
|
+
|
|
59
|
+
3. Grant of Patent License.
|
|
60
|
+
|
|
61
|
+
Each Contributor hereby grants You a perpetual, worldwide,
|
|
62
|
+
non-exclusive, no-charge, royalty-free, irrevocable patent license
|
|
63
|
+
to make, use, sell, offer to sell, import, and otherwise transfer
|
|
64
|
+
the Work.
|
|
65
|
+
|
|
66
|
+
4. Redistribution.
|
|
67
|
+
|
|
68
|
+
You may reproduce and distribute copies of the Work provided that
|
|
69
|
+
You give recipients a copy of this License and retain attribution notices.
|
|
70
|
+
|
|
71
|
+
5. Submission of Contributions.
|
|
72
|
+
|
|
73
|
+
Unless You explicitly state otherwise, any Contribution intentionally
|
|
74
|
+
submitted for inclusion in the Work shall be under the terms of this License.
|
|
75
|
+
|
|
76
|
+
6. Trademarks.
|
|
77
|
+
|
|
78
|
+
This License does not grant permission to use the trade names,
|
|
79
|
+
trademarks, service marks, or product names of the Licensor.
|
|
80
|
+
|
|
81
|
+
7. Disclaimer of Warranty.
|
|
82
|
+
|
|
83
|
+
The Work is provided "AS IS", WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND.
|
|
84
|
+
|
|
85
|
+
8. Limitation of Liability.
|
|
86
|
+
|
|
87
|
+
In no event shall any Contributor be liable for damages arising from
|
|
88
|
+
the use of the Work.
|
|
89
|
+
|
|
90
|
+
9. Accepting Warranty or Additional Liability.
|
|
91
|
+
|
|
92
|
+
You may offer support or warranty protection for a fee, but only
|
|
93
|
+
on Your own behalf and responsibility.
|
package/README.md
ADDED
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
README.md reescrito (v1.0.0 listo para publicar)
|
|
2
|
+
|
|
3
|
+
Basado en tu README actual
|
|
4
|
+
|
|
5
|
+
README (5)
|
|
6
|
+
|
|
7
|
+
, aquí va una versión limpia, con tus métricas reales, endpoints, bench y dataset.
|
|
8
|
+
|
|
9
|
+
Copia y pega esto como README.md:
|
|
10
|
+
|
|
11
|
+
# llm-entropy-filter
|
|
12
|
+
|
|
13
|
+
Minimal, fast **entropy + intent gate** for LLM inputs.
|
|
14
|
+
|
|
15
|
+
This package runs a **local, deterministic heuristic gate** to detect high-entropy / low-signal inputs (spam, coercion, vague conspiracies, pseudo-science, truth relativism, broken causality) and returns an **ALLOW / WARN / BLOCK** verdict.
|
|
16
|
+
|
|
17
|
+
Use it **before** calling an LLM to reduce hallucinations, cost, and risk.
|
|
18
|
+
|
|
19
|
+
---
|
|
20
|
+
|
|
21
|
+
## What you get
|
|
22
|
+
|
|
23
|
+
### Core (library)
|
|
24
|
+
- `gate(text)` → `{ action, entropy_score, flags, intention, confidence, rationale }`
|
|
25
|
+
- `gateLLM(text)` → alias of `gate(text)` (kept for compatibility)
|
|
26
|
+
- `runEntropyFilter(text)` → underlying entropy + intention analysis utilities
|
|
27
|
+
|
|
28
|
+
### Demo (server)
|
|
29
|
+
- `POST /analyze` → runs local `gate(text)` + returns `meta.ts` + `meta.version`
|
|
30
|
+
- `POST /triad` → optional OpenAI analysis (only if `OPENAI_API_KEY` is set)
|
|
31
|
+
|
|
32
|
+
---
|
|
33
|
+
|
|
34
|
+
## Install
|
|
35
|
+
|
|
36
|
+
```bash
|
|
37
|
+
npm i llm-entropy-filter
|
|
38
|
+
|
|
39
|
+
Quickstart (library)
|
|
40
|
+
import { gate } from "llm-entropy-filter";
|
|
41
|
+
|
|
42
|
+
const r = gate("¡¡COMPRA YA!! Oferta limitada 90% OFF $$$");
|
|
43
|
+
console.log(r);
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
Example output:
|
|
47
|
+
|
|
48
|
+
{
|
|
49
|
+
"action": "BLOCK",
|
|
50
|
+
"entropy_score": 0.7,
|
|
51
|
+
"flags": ["urgency","spam_sales","money_signal","shouting"],
|
|
52
|
+
"intention": "marketing_spam",
|
|
53
|
+
"confidence": 0.85,
|
|
54
|
+
"rationale": "Detecté señales de venta agresiva/urgencia/dinero."
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
Demo server (Express)
|
|
58
|
+
|
|
59
|
+
Start:
|
|
60
|
+
|
|
61
|
+
npm run serve
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
Health:
|
|
65
|
+
|
|
66
|
+
curl -s http://127.0.0.1:3000/health
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
Local gate:
|
|
70
|
+
|
|
71
|
+
curl -s -X POST http://127.0.0.1:3000/analyze \
|
|
72
|
+
-H "Content-Type: application/json" \
|
|
73
|
+
-d '{"text":"Congratulations! You won a FREE iPhone. Click here to claim now!"}' | jq .
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
You will also see:
|
|
77
|
+
|
|
78
|
+
"meta": { "ts": 1769546511060, "version": "1.0.0" }
|
|
79
|
+
|
|
80
|
+
Optional: OpenAI triad demo
|
|
81
|
+
export OPENAI_API_KEY="YOUR_KEY"
|
|
82
|
+
export OPENAI_MODEL="gpt-4.1-mini"
|
|
83
|
+
|
|
84
|
+
curl -s -X POST http://127.0.0.1:3000/triad \
|
|
85
|
+
-H "Content-Type: application/json" \
|
|
86
|
+
-d '{"text":"Vivimos en una simulación y todos lo esconden."}' | jq .
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
/triad is a demo layer. The product is the local gate().
|
|
90
|
+
|
|
91
|
+
Benchmarks (measured)
|
|
92
|
+
HTTP gate /analyze (local, deterministic)
|
|
93
|
+
|
|
94
|
+
Command:
|
|
95
|
+
|
|
96
|
+
npx autocannon -m POST -c 30 -d 10 --renderStatusCodes \
|
|
97
|
+
http://127.0.0.1:3000/analyze \
|
|
98
|
+
-H "Content-Type: application/json" \
|
|
99
|
+
-b '{"text":"Congratulations. You won a FREE iPhone. Click here to claim now."}'
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
Observed (typical run):
|
|
103
|
+
|
|
104
|
+
~5.2k req/s
|
|
105
|
+
|
|
106
|
+
~5.1 ms avg latency (p50 ~4 ms)
|
|
107
|
+
|
|
108
|
+
LLM demo /triad (OpenAI)
|
|
109
|
+
|
|
110
|
+
Command:
|
|
111
|
+
|
|
112
|
+
npx autocannon -m POST -c 2 -d 30 --renderStatusCodes \
|
|
113
|
+
http://127.0.0.1:3000/triad \
|
|
114
|
+
-H "Content-Type: application/json" \
|
|
115
|
+
-b '{"text":"Texto real de prueba (1-3 párrafos) ..."}'
|
|
116
|
+
|
|
117
|
+
|
|
118
|
+
Observed (typical run):
|
|
119
|
+
|
|
120
|
+
~1.7 req/s
|
|
121
|
+
|
|
122
|
+
~1.17 s avg latency
|
|
123
|
+
|
|
124
|
+
Dataset mini + bench script (no HTTP)
|
|
125
|
+
|
|
126
|
+
A tiny CSV lives at:
|
|
127
|
+
|
|
128
|
+
bench/sms_spam.csv
|
|
129
|
+
|
|
130
|
+
Run the bench:
|
|
131
|
+
|
|
132
|
+
node bench/sms_spam_bench.mjs bench/sms_spam.csv
|
|
133
|
+
cat bench/reports/sms_spam_report.md
|
|
134
|
+
|
|
135
|
+
|
|
136
|
+
Typical report:
|
|
137
|
+
|
|
138
|
+
Throughput: ~9–10k samples/sec
|
|
139
|
+
|
|
140
|
+
Actions: ALLOW / WARN / BLOCK distribution
|
|
141
|
+
|
|
142
|
+
Confusion table (ground truth spam/ham → action)
|
|
143
|
+
|
|
144
|
+
Top flags + intentions
|
|
145
|
+
|
|
146
|
+
JSON + Markdown reports written to bench/reports/
|
|
147
|
+
|
|
148
|
+
Design goals
|
|
149
|
+
|
|
150
|
+
Fast: pure heuristics, no network calls
|
|
151
|
+
|
|
152
|
+
Portable: works in any Node environment
|
|
153
|
+
|
|
154
|
+
Composable: middleware/wrapper before calling an LLM
|
|
155
|
+
|
|
156
|
+
Transparent: flags explain why an input is risky
|
|
157
|
+
|
|
158
|
+
Observable: /analyze returns meta.ts and meta.version
|
|
159
|
+
|
|
160
|
+
Roadmap
|
|
161
|
+
|
|
162
|
+
Expand multilingual spam patterns
|
|
163
|
+
|
|
164
|
+
Optional suggested_rewrite to lower entropy
|
|
165
|
+
|
|
166
|
+
Example integrations: Next.js / Vercel, Express, Cloudflare Workers
|
|
167
|
+
|
|
168
|
+
Extended dataset benches + cost-savings estimates
|
|
169
|
+
|
|
170
|
+
License
|
|
171
|
+
|
|
172
|
+
Apache-2.0
|
|
173
|
+
|
|
174
|
+
Copyright (c) 2026 Ernesto Rosati
|
|
175
|
+
|
|
176
|
+
|
|
177
|
+
---
|
|
178
|
+
|
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,265 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
|
|
20
|
+
// src/index.ts
|
|
21
|
+
var index_exports = {};
|
|
22
|
+
__export(index_exports, {
|
|
23
|
+
analyzeEntropy: () => analyzeEntropy,
|
|
24
|
+
entropyMiddleware: () => entropyMiddleware,
|
|
25
|
+
evaluateIntention: () => evaluateIntention,
|
|
26
|
+
gate: () => gateLLM,
|
|
27
|
+
gateLLM: () => gateLLM,
|
|
28
|
+
runEntropyFilter: () => runEntropyFilter
|
|
29
|
+
});
|
|
30
|
+
module.exports = __toCommonJS(index_exports);
|
|
31
|
+
|
|
32
|
+
// src/entropy.ts
|
|
33
|
+
function clamp01(x) {
|
|
34
|
+
return Math.max(0, Math.min(1, x));
|
|
35
|
+
}
|
|
36
|
+
function countRegex(text, re) {
|
|
37
|
+
const m = text.match(re);
|
|
38
|
+
return m ? m.length : 0;
|
|
39
|
+
}
|
|
40
|
+
function analyzeEntropy(text) {
|
|
41
|
+
const raw = text || "";
|
|
42
|
+
const t = raw.toLowerCase();
|
|
43
|
+
const flags = [];
|
|
44
|
+
let score = 0;
|
|
45
|
+
if (/\b(ahora|ya|urgente|última|hoy|inmediato)\b/.test(t)) {
|
|
46
|
+
flags.push("urgency");
|
|
47
|
+
score += 0.2;
|
|
48
|
+
}
|
|
49
|
+
if (/\b(compra|oferta|promo|descuento|gratis|clic|click|off)\b/.test(t)) {
|
|
50
|
+
flags.push("spam_sales");
|
|
51
|
+
score += 0.25;
|
|
52
|
+
}
|
|
53
|
+
const moneyHits = countRegex(raw, /\$+/g);
|
|
54
|
+
if (moneyHits > 0) {
|
|
55
|
+
flags.push("money_signal");
|
|
56
|
+
score += Math.min(0.2, moneyHits * 0.05);
|
|
57
|
+
}
|
|
58
|
+
const exclam = countRegex(raw, /!/g);
|
|
59
|
+
const capsRatio = (() => {
|
|
60
|
+
const letters = raw.match(/[A-Za-zÁÉÍÓÚÜÑáéíóúüñ]/g) || [];
|
|
61
|
+
if (letters.length === 0) return 0;
|
|
62
|
+
const caps = (raw.match(/[A-ZÁÉÍÓÚÜÑ]/g) || []).length;
|
|
63
|
+
return caps / letters.length;
|
|
64
|
+
})();
|
|
65
|
+
if (exclam >= 3 || capsRatio >= 0.35) {
|
|
66
|
+
flags.push("shouting");
|
|
67
|
+
score += 0.2;
|
|
68
|
+
}
|
|
69
|
+
if (/\b(si de verdad|si me quisieras|es tu culpa|no tienes opción|me debes)\b/.test(t)) {
|
|
70
|
+
flags.push("emotional_manipulation");
|
|
71
|
+
score += 0.35;
|
|
72
|
+
}
|
|
73
|
+
if (/\b(todos lo saben|lo esconden|la verdad oculta|ellos no quieren|simulación)\b/.test(t)) {
|
|
74
|
+
flags.push("conspiracy_vague");
|
|
75
|
+
score += 0.2;
|
|
76
|
+
}
|
|
77
|
+
if (/\b(la cultura lo prueba|es obvio|todo mundo sabe|se sabe|está claro)\b/.test(t)) {
|
|
78
|
+
flags.push("weak_evidence");
|
|
79
|
+
score += 0.2;
|
|
80
|
+
}
|
|
81
|
+
if (/\b(ellos|la élite|los de arriba)\b/.test(t) && /\b(esconden|ocultan|tapan)\b/.test(t)) {
|
|
82
|
+
flags.push("hidden_actor");
|
|
83
|
+
score += 0.15;
|
|
84
|
+
}
|
|
85
|
+
if (/\b(física cuántica|cuantica|cuántico|quantum)\b/.test(t)) {
|
|
86
|
+
flags.push("pseudo_science_quantum");
|
|
87
|
+
score += 0.2;
|
|
88
|
+
}
|
|
89
|
+
const manifestHits = countRegex(t, /\b(manifestar|manifestación|decretar|decreto|vibración|vibracion|frecuencia|energía|energia|ley de la atracción|universo me lo dará)\b/g) + countRegex(t, /\b(realine(a|ar)\b.*\bátom|\bátom|\batomos\b)/g);
|
|
90
|
+
if (manifestHits > 0) {
|
|
91
|
+
flags.push("magic_manifesting");
|
|
92
|
+
score += Math.min(0.35, 0.15 + manifestHits * 0.06);
|
|
93
|
+
}
|
|
94
|
+
if (/\b(no hay una verdad objetiva|no existe la verdad objetiva|tu verdad|mi verdad|la verdad es relativa|lo que importa es lo que sientes)\b/.test(t)) {
|
|
95
|
+
flags.push("truth_relativism");
|
|
96
|
+
score += 0.35;
|
|
97
|
+
}
|
|
98
|
+
if (/\b(deben|debe)\b.*\b(obligatoriamente|por lo tanto|por ende)\b/.test(t) || /\b(la materia)\b.*\b(se subordina|obedece)\b/.test(t)) {
|
|
99
|
+
flags.push("broken_causality");
|
|
100
|
+
score += 0.2;
|
|
101
|
+
}
|
|
102
|
+
score = clamp01(score);
|
|
103
|
+
return { score, flags };
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
// src/intention.ts
|
|
107
|
+
function clamp012(x) {
|
|
108
|
+
return Math.max(0, Math.min(1, x));
|
|
109
|
+
}
|
|
110
|
+
function evaluateIntention(text) {
|
|
111
|
+
const raw = text || "";
|
|
112
|
+
const t = raw.toLowerCase();
|
|
113
|
+
const isHelp = /\b(ayuda|ayúdame|explica|resume|resumir|cómo|como|puedes|podrías|por favor)\b/.test(
|
|
114
|
+
t
|
|
115
|
+
);
|
|
116
|
+
const isSpam = /\b(compra|oferta|promo|descuento|gratis|haz clic|click|off|90%|% off)\b/.test(
|
|
117
|
+
t
|
|
118
|
+
) || (raw.match(/\$+/g) || []).length > 0;
|
|
119
|
+
const isManip = /\b(si de verdad|si me quisieras|es tu culpa|no tienes opción|me debes)\b/.test(
|
|
120
|
+
t
|
|
121
|
+
);
|
|
122
|
+
const isConsp = /\b(simulación|todos lo saben|lo esconden|verdad oculta|ellos no quieren)\b/.test(
|
|
123
|
+
t
|
|
124
|
+
);
|
|
125
|
+
const isMisinformation = /\b(física cuántica|cuantica|cuántica|cuántico|quantum)\b/.test(t) || /\b(manifestar|manifestación|decretar|decreto|vibración|vibracion|frecuencia|energía|energia|ley de la atracción)\b/.test(t) || /\b(no hay una verdad objetiva|no existe la verdad objetiva|tu verdad|mi verdad|la verdad es relativa)\b/.test(t) || /\b(la materia)\b.*\b(se subordina|obedece)\b/.test(t);
|
|
126
|
+
let intention = "unknown";
|
|
127
|
+
let confidence = 0;
|
|
128
|
+
let rationale = "";
|
|
129
|
+
if (isSpam) {
|
|
130
|
+
intention = "marketing_spam";
|
|
131
|
+
confidence = 0.85;
|
|
132
|
+
rationale = "Detect\xE9 se\xF1ales de venta agresiva/urgencia/dinero.";
|
|
133
|
+
} else if (isManip) {
|
|
134
|
+
intention = "manipulation";
|
|
135
|
+
confidence = 0.85;
|
|
136
|
+
rationale = "Detect\xE9 coerci\xF3n/culpa/chantaje emocional.";
|
|
137
|
+
} else if (isConsp) {
|
|
138
|
+
intention = "conspiracy";
|
|
139
|
+
confidence = 0.75;
|
|
140
|
+
rationale = "Detect\xE9 marco conspirativo vago ('lo esconden', 'todos lo saben').";
|
|
141
|
+
} else if (isMisinformation) {
|
|
142
|
+
intention = "misinformation";
|
|
143
|
+
confidence = 0.85;
|
|
144
|
+
rationale = "Detect\xE9 patr\xF3n de pseudo-ciencia/pensamiento m\xE1gico/relativismo de la verdad (alta probabilidad de desinformaci\xF3n).";
|
|
145
|
+
} else if (isHelp) {
|
|
146
|
+
intention = "request_help";
|
|
147
|
+
confidence = 0.7;
|
|
148
|
+
rationale = "Parece una petici\xF3n leg\xEDtima de ayuda/explicaci\xF3n.";
|
|
149
|
+
}
|
|
150
|
+
return { intention, confidence: clamp012(confidence), rationale };
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
// src/wrapper.ts
|
|
154
|
+
function runEntropyFilter(text) {
|
|
155
|
+
const entropy_analysis = analyzeEntropy(text);
|
|
156
|
+
let intention_evaluation = evaluateIntention(text);
|
|
157
|
+
const hardFlags = new Set(entropy_analysis.flags);
|
|
158
|
+
const epistemicEntropy = hardFlags.has("truth_relativism") || hardFlags.has("magic_manifesting") || hardFlags.has("pseudo_science_quantum") || hardFlags.has("broken_causality");
|
|
159
|
+
if (epistemicEntropy) {
|
|
160
|
+
intention_evaluation = {
|
|
161
|
+
intention: "misinformation",
|
|
162
|
+
confidence: Math.max(intention_evaluation.confidence ?? 0.7, 0.8),
|
|
163
|
+
rationale: "Detect\xE9 patr\xF3n de pseudo-ciencia/pensamiento m\xE1gico/relativismo de la verdad; alta probabilidad de desinformaci\xF3n o argumento sin anclaje causal."
|
|
164
|
+
};
|
|
165
|
+
}
|
|
166
|
+
return { entropy_analysis, intention_evaluation };
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
// src/middleware.ts
|
|
170
|
+
function entropyMiddleware(req, _res, next) {
|
|
171
|
+
const text = req?.body?.text;
|
|
172
|
+
const result = typeof text === "string" ? runEntropyFilter(text) : runEntropyFilter("");
|
|
173
|
+
req.entropy = result;
|
|
174
|
+
next?.();
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
// src/gate.ts
|
|
178
|
+
function clamp013(x) {
|
|
179
|
+
return Math.max(0, Math.min(1, x));
|
|
180
|
+
}
|
|
181
|
+
function mergeFlags(base, extra) {
|
|
182
|
+
const set = new Set(base);
|
|
183
|
+
const out = [...base];
|
|
184
|
+
for (const f of extra) {
|
|
185
|
+
if (!set.has(f)) {
|
|
186
|
+
set.add(f);
|
|
187
|
+
out.push(f);
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
return out;
|
|
191
|
+
}
|
|
192
|
+
function englishSpamBooster(rawText) {
|
|
193
|
+
const t = (rawText || "").toLowerCase();
|
|
194
|
+
const patterns = [
|
|
195
|
+
{ re: /\bfree\b/g, score: 0.08, flag: "spam_kw_free" },
|
|
196
|
+
{ re: /\bwinner\b|\bwon\b|\bcongratulations\b/g, score: 0.1, flag: "spam_kw_winner" },
|
|
197
|
+
{ re: /\bclaim\b|\bredeem\b/g, score: 0.08, flag: "spam_kw_claim" },
|
|
198
|
+
{ re: /\bclick\b|\bclick here\b|\bopen link\b|\btap here\b/g, score: 0.1, flag: "spam_kw_click" },
|
|
199
|
+
{ re: /\blimited time\b|\bact now\b|\bfinal notice\b|\bbefore midnight\b/g, score: 0.1, flag: "spam_kw_urgency_en" },
|
|
200
|
+
{ re: /\bverify\b|\bconfirm\b|\baccount\b.*\b(suspended|locked)\b/g, score: 0.12, flag: "spam_kw_verify" },
|
|
201
|
+
{ re: /\bprize\b|\bgift card\b|\bgiftcard\b|\bvoucher\b|\biphone\b|\bsurvey\b/g, score: 0.1, flag: "spam_kw_prize" },
|
|
202
|
+
{ re: /\bloan\b|\bpre-?approved\b|\bno credit check\b/g, score: 0.12, flag: "spam_kw_loan" },
|
|
203
|
+
{ re: /\bcrypto\b|\bairdrop\b|\bwallet\b|\bseed phrase\b/g, score: 0.1, flag: "spam_kw_crypto" },
|
|
204
|
+
{ re: /\bdelivery failed\b|\breschedule\b|\bpackage\b|\bcourier\b/g, score: 0.1, flag: "spam_kw_delivery" },
|
|
205
|
+
{ re: /\btax refund\b|\bunpaid\b|\brefund\b|\bchargeback\b/g, score: 0.1, flag: "spam_kw_refund" }
|
|
206
|
+
];
|
|
207
|
+
let addScore = 0;
|
|
208
|
+
const addFlags = [];
|
|
209
|
+
let hitCount = 0;
|
|
210
|
+
for (const p of patterns) {
|
|
211
|
+
const m = t.match(p.re);
|
|
212
|
+
if (m && m.length > 0) {
|
|
213
|
+
hitCount += m.length;
|
|
214
|
+
addScore += p.score;
|
|
215
|
+
addFlags.push(p.flag);
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
addScore = Math.min(0.35, addScore);
|
|
219
|
+
if (hitCount > 0) addFlags.push("spam_keywords_en");
|
|
220
|
+
return { addScore, addFlags, hitCount };
|
|
221
|
+
}
|
|
222
|
+
function gateLLM(text, config = {}) {
|
|
223
|
+
const warnT = config.warnThreshold ?? 0.25;
|
|
224
|
+
const blockT = config.blockThreshold ?? 0.6;
|
|
225
|
+
const r = runEntropyFilter(text);
|
|
226
|
+
let score = r.entropy_analysis.score;
|
|
227
|
+
let flags = [...r.entropy_analysis.flags];
|
|
228
|
+
const booster = englishSpamBooster(text);
|
|
229
|
+
if (booster.hitCount > 0) {
|
|
230
|
+
score = clamp013(score + booster.addScore);
|
|
231
|
+
flags = mergeFlags(flags, booster.addFlags);
|
|
232
|
+
}
|
|
233
|
+
let intention = r.intention_evaluation.intention || "unknown";
|
|
234
|
+
let confidence = r.intention_evaluation.confidence ?? 0;
|
|
235
|
+
let rationale = r.intention_evaluation.rationale ?? "";
|
|
236
|
+
if (booster.hitCount > 0 && intention === "unknown") {
|
|
237
|
+
intention = "marketing_spam";
|
|
238
|
+
confidence = Math.max(confidence, 0.75);
|
|
239
|
+
rationale = (rationale ? rationale + " " : "") + "Detect\xE9 keywords t\xEDpicas de spam/phishing en ingl\xE9s.";
|
|
240
|
+
}
|
|
241
|
+
const hasSpam = flags.includes("spam_sales") || flags.includes("spam_keywords_en");
|
|
242
|
+
const hasMoneySignals = flags.includes("money_signal") || flags.includes("spam_kw_prize") || flags.includes("spam_kw_loan");
|
|
243
|
+
const strongSpam = hasSpam && hasMoneySignals;
|
|
244
|
+
let action = "ALLOW";
|
|
245
|
+
if (score > blockT || strongSpam) action = "BLOCK";
|
|
246
|
+
else if (score >= warnT) action = "WARN";
|
|
247
|
+
return {
|
|
248
|
+
action,
|
|
249
|
+
entropy_score: score,
|
|
250
|
+
flags,
|
|
251
|
+
intention,
|
|
252
|
+
confidence: clamp013(confidence),
|
|
253
|
+
rationale
|
|
254
|
+
};
|
|
255
|
+
}
|
|
256
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
257
|
+
0 && (module.exports = {
|
|
258
|
+
analyzeEntropy,
|
|
259
|
+
entropyMiddleware,
|
|
260
|
+
evaluateIntention,
|
|
261
|
+
gate,
|
|
262
|
+
gateLLM,
|
|
263
|
+
runEntropyFilter
|
|
264
|
+
});
|
|
265
|
+
//# sourceMappingURL=index.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/entropy.ts","../src/intention.ts","../src/wrapper.ts","../src/middleware.ts","../src/gate.ts"],"sourcesContent":["// src/index.ts\nexport { analyzeEntropy } from \"./entropy\";\nexport { evaluateIntention } from \"./intention\";\nexport { runEntropyFilter } from \"./wrapper\";\nexport { entropyMiddleware } from \"./middleware\";\n\nexport type {\n EntropyResult,\n IntentionEvaluation,\n FilterResult\n} from \"./types\";\nexport { gateLLM as gate } from \"./gate\";\nexport * from \"./gate\";\n","// src/entropy.ts\nexport type EntropyResult = {\n score: number; // 0..1\n flags: string[];\n};\n\nfunction clamp01(x: number) {\n return Math.max(0, Math.min(1, x));\n}\n\nfunction countRegex(text: string, re: RegExp) {\n const m = text.match(re);\n return m ? m.length : 0;\n}\n\nexport function analyzeEntropy(text: string): EntropyResult {\n const raw = text || \"\";\n const t = raw.toLowerCase();\n const flags: string[] = [];\n let score = 0;\n\n // 1) Urgencia / presión\n if (/\\b(ahora|ya|urgente|última|hoy|inmediato)\\b/.test(t)) {\n flags.push(\"urgency\");\n score += 0.20;\n }\n\n // 2) Spam / venta agresiva\n if (/\\b(compra|oferta|promo|descuento|gratis|clic|click|off)\\b/.test(t)) {\n flags.push(\"spam_sales\");\n score += 0.25;\n }\n\n // 3) Señales $$$ / símbolos\n const moneyHits = countRegex(raw, /\\$+/g);\n if (moneyHits > 0) {\n flags.push(\"money_signal\");\n score += Math.min(0.20, moneyHits * 0.05);\n }\n\n // 4) Exceso de signos / gritos\n const exclam = countRegex(raw, /!/g);\n const capsRatio = (() => {\n const letters = raw.match(/[A-Za-zÁÉÍÓÚÜÑáéíóúüñ]/g) || [];\n if (letters.length === 0) return 0;\n const caps = (raw.match(/[A-ZÁÉÍÓÚÜÑ]/g) || []).length;\n return caps / letters.length;\n })();\n\n if (exclam >= 3 || capsRatio >= 0.35) {\n flags.push(\"shouting\");\n score += 0.20;\n }\n\n // 5) Manipulación / culpa / coerción\n if (/\\b(si de verdad|si me quisieras|es tu culpa|no tienes opción|me debes)\\b/.test(t)) {\n flags.push(\"emotional_manipulation\");\n score += 0.35;\n }\n\n // 6) Conspiración vaga / “todos lo saben”\n if (/\\b(todos lo saben|lo esconden|la verdad oculta|ellos no quieren|simulación)\\b/.test(t)) {\n flags.push(\"conspiracy_vague\");\n score += 0.20;\n }\n\n // 6.5) “Prueba vaga” / apelación a cultura como evidencia\n if (/\\b(la cultura lo prueba|es obvio|todo mundo sabe|se sabe|está claro)\\b/.test(t)) {\n flags.push(\"weak_evidence\");\n score += 0.20;\n }\n\n // 6.6) Totalización + agente oculto (“ellos”)\n if (/\\b(ellos|la élite|los de arriba)\\b/.test(t) && /\\b(esconden|ocultan|tapan)\\b/.test(t)) {\n flags.push(\"hidden_actor\");\n score += 0.15;\n }\n\n // ------------------------------------------------------------\n // NUEVO: Entropía por pseudo-ciencia / pensamiento mágico / relativismo\n // ------------------------------------------------------------\n\n // 7) Pseudo-ciencia \"cuántica\" usada como licencia mágica\n if (/\\b(física cuántica|cuantica|cuántico|quantum)\\b/.test(t)) {\n flags.push(\"pseudo_science_quantum\");\n score += 0.20;\n }\n\n // 8) Manifestación mágica / decretos / vibración / energía como causalidad\n const manifestHits =\n countRegex(t, /\\b(manifestar|manifestación|decretar|decreto|vibración|vibracion|frecuencia|energía|energia|ley de la atracción|universo me lo dará)\\b/g) +\n countRegex(t, /\\b(realine(a|ar)\\b.*\\bátom|\\bátom|\\batomos\\b)/g);\n\n if (manifestHits > 0) {\n flags.push(\"magic_manifesting\");\n score += Math.min(0.35, 0.15 + manifestHits * 0.06);\n }\n\n // 9) Relativismo de la verdad / negación explícita de verdad objetiva\n if (/\\b(no hay una verdad objetiva|no existe la verdad objetiva|tu verdad|mi verdad|la verdad es relativa|lo que importa es lo que sientes)\\b/.test(t)) {\n flags.push(\"truth_relativism\");\n score += 0.35;\n }\n\n // 10) Causalidad rota / obligación metafísica (\"debe\" porque lo deseo)\n if (/\\b(deben|debe)\\b.*\\b(obligatoriamente|por lo tanto|por ende)\\b/.test(t) || /\\b(la materia)\\b.*\\b(se subordina|obedece)\\b/.test(t)) {\n flags.push(\"broken_causality\");\n score += 0.20;\n }\n\n // Normaliza: score final 0..1\n score = clamp01(score);\n\n return { score, flags };\n}\n","// src/intention.ts\nimport type { IntentionEvaluation, IntentionType } from \"./types\";\n\nfunction clamp01(x: number) {\n return Math.max(0, Math.min(1, x));\n}\n\nexport function evaluateIntention(text: string): IntentionEvaluation {\n const raw = text || \"\";\n const t = raw.toLowerCase();\n\n // Heurísticas rápidas (MVP)\n const isHelp =\n /\\b(ayuda|ayúdame|explica|resume|resumir|cómo|como|puedes|podrías|por favor)\\b/.test(\n t\n );\n\n const isSpam =\n /\\b(compra|oferta|promo|descuento|gratis|haz clic|click|off|90%|% off)\\b/.test(\n t\n ) || (raw.match(/\\$+/g) || []).length > 0;\n\n const isManip =\n /\\b(si de verdad|si me quisieras|es tu culpa|no tienes opción|me debes)\\b/.test(\n t\n );\n\n const isConsp =\n /\\b(simulación|todos lo saben|lo esconden|verdad oculta|ellos no quieren)\\b/.test(\n t\n );\n\n // NUEVO: pseudo-ciencia / pensamiento mágico / relativismo (desinformación)\n const isMisinformation =\n /\\b(física cuántica|cuantica|cuántica|cuántico|quantum)\\b/.test(t) ||\n /\\b(manifestar|manifestación|decretar|decreto|vibración|vibracion|frecuencia|energía|energia|ley de la atracción)\\b/.test(t) ||\n /\\b(no hay una verdad objetiva|no existe la verdad objetiva|tu verdad|mi verdad|la verdad es relativa)\\b/.test(t) ||\n /\\b(la materia)\\b.*\\b(se subordina|obedece)\\b/.test(t);\n\n let intention: IntentionType = \"unknown\";\n let confidence = 0.0;\n let rationale = \"\";\n\n if (isSpam) {\n intention = \"marketing_spam\";\n confidence = 0.85;\n rationale = \"Detecté señales de venta agresiva/urgencia/dinero.\";\n } else if (isManip) {\n intention = \"manipulation\";\n confidence = 0.85;\n rationale = \"Detecté coerción/culpa/chantaje emocional.\";\n } else if (isConsp) {\n intention = \"conspiracy\";\n confidence = 0.75;\n rationale = \"Detecté marco conspirativo vago ('lo esconden', 'todos lo saben').\";\n } else if (isMisinformation) {\n intention = \"misinformation\";\n confidence = 0.85;\n rationale =\n \"Detecté patrón de pseudo-ciencia/pensamiento mágico/relativismo de la verdad (alta probabilidad de desinformación).\";\n } else if (isHelp) {\n intention = \"request_help\";\n confidence = 0.7;\n rationale = \"Parece una petición legítima de ayuda/explicación.\";\n }\n\n return { intention, confidence: clamp01(confidence), rationale };\n}\n","// src/wrapper.ts\nimport { analyzeEntropy } from \"./entropy\";\nimport { evaluateIntention } from \"./intention\";\nimport type { FilterResult, IntentionEvaluation } from \"./types\";\n\nexport function runEntropyFilter(text: string): FilterResult {\n const entropy_analysis = analyzeEntropy(text);\n let intention_evaluation: IntentionEvaluation = evaluateIntention(text);\n\n // Corrección: si el texto trae señales fuertes de entropía epistemológica,\n // no lo clasifiques como \"request_help\" solo por ser pregunta larga.\n const hardFlags = new Set(entropy_analysis.flags);\n const epistemicEntropy =\n hardFlags.has(\"truth_relativism\") ||\n hardFlags.has(\"magic_manifesting\") ||\n hardFlags.has(\"pseudo_science_quantum\") ||\n hardFlags.has(\"broken_causality\");\n\n if (epistemicEntropy) {\n intention_evaluation = {\n intention: \"misinformation\",\n confidence: Math.max(intention_evaluation.confidence ?? 0.7, 0.8),\n rationale:\n \"Detecté patrón de pseudo-ciencia/pensamiento mágico/relativismo de la verdad; alta probabilidad de desinformación o argumento sin anclaje causal.\"\n };\n }\n\n return { entropy_analysis, intention_evaluation };\n}\n","// src/middleware.ts\nimport { runEntropyFilter } from \"./wrapper\";\nimport type { FilterResult } from \"./types\";\n\n/**\n * Middleware agnóstico (NO depende de express types).\n * Compatible con Express / Next API routes / cualquier framework estilo req-res-next.\n *\n * Espera: req.body.text (string)\n * Escribe: req.entropy = FilterResult\n */\nexport function entropyMiddleware(req: any, _res: any, next: any) {\n const text = req?.body?.text;\n\n const result: FilterResult =\n typeof text === \"string\" ? runEntropyFilter(text) : runEntropyFilter(\"\");\n\n req.entropy = result;\n next?.();\n}\n\n/**\n * Tipo auxiliar opcional (sin forzar dependencias).\n * Útil si quieres tipar tu req en tu app.\n */\nexport type EntropyAugmentedRequest = {\n body?: { text?: unknown };\n entropy?: FilterResult;\n};\n","// src/gate.ts\nimport { runEntropyFilter } from \"./wrapper\";\n\nexport type GateAction = \"ALLOW\" | \"WARN\" | \"BLOCK\";\n\nexport type GateResult = {\n action: GateAction;\n entropy_score: number;\n flags: string[];\n intention: string;\n confidence: number;\n rationale: string;\n};\n\nexport type GateConfig = {\n warnThreshold?: number; // default: 0.25\n blockThreshold?: number; // default: 0.60\n};\n\n/** Clamp 0..1 */\nfunction clamp01(x: number) {\n return Math.max(0, Math.min(1, x));\n}\n\n/** Add unique flags preserving order */\nfunction mergeFlags(base: string[], extra: string[]) {\n const set = new Set(base);\n const out = [...base];\n for (const f of extra) {\n if (!set.has(f)) {\n set.add(f);\n out.push(f);\n }\n }\n return out;\n}\n\n/**\n * Detección de spam/phishing en inglés por keywords.\n * Se implementa como “booster” de score + flags (sin tocar entropy.ts).\n */\nfunction englishSpamBooster(rawText: string): {\n addScore: number;\n addFlags: string[];\n hitCount: number;\n} {\n const t = (rawText || \"\").toLowerCase();\n\n // Patrones típicos (spam / phishing / promos agresivas)\n const patterns: Array<{ re: RegExp; score: number; flag: string }> = [\n { re: /\\bfree\\b/g, score: 0.08, flag: \"spam_kw_free\" },\n { re: /\\bwinner\\b|\\bwon\\b|\\bcongratulations\\b/g, score: 0.10, flag: \"spam_kw_winner\" },\n { re: /\\bclaim\\b|\\bredeem\\b/g, score: 0.08, flag: \"spam_kw_claim\" },\n { re: /\\bclick\\b|\\bclick here\\b|\\bopen link\\b|\\btap here\\b/g, score: 0.10, flag: \"spam_kw_click\" },\n { re: /\\blimited time\\b|\\bact now\\b|\\bfinal notice\\b|\\bbefore midnight\\b/g, score: 0.10, flag: \"spam_kw_urgency_en\" },\n { re: /\\bverify\\b|\\bconfirm\\b|\\baccount\\b.*\\b(suspended|locked)\\b/g, score: 0.12, flag: \"spam_kw_verify\" },\n { re: /\\bprize\\b|\\bgift card\\b|\\bgiftcard\\b|\\bvoucher\\b|\\biphone\\b|\\bsurvey\\b/g, score: 0.10, flag: \"spam_kw_prize\" },\n { re: /\\bloan\\b|\\bpre-?approved\\b|\\bno credit check\\b/g, score: 0.12, flag: \"spam_kw_loan\" },\n { re: /\\bcrypto\\b|\\bairdrop\\b|\\bwallet\\b|\\bseed phrase\\b/g, score: 0.10, flag: \"spam_kw_crypto\" },\n { re: /\\bdelivery failed\\b|\\breschedule\\b|\\bpackage\\b|\\bcourier\\b/g, score: 0.10, flag: \"spam_kw_delivery\" },\n { re: /\\btax refund\\b|\\bunpaid\\b|\\brefund\\b|\\bchargeback\\b/g, score: 0.10, flag: \"spam_kw_refund\" },\n ];\n\n let addScore = 0;\n const addFlags: string[] = [];\n let hitCount = 0;\n\n for (const p of patterns) {\n const m = t.match(p.re);\n if (m && m.length > 0) {\n hitCount += m.length;\n addScore += p.score; // suma “por patrón”, no por ocurrencia (controlado)\n addFlags.push(p.flag);\n }\n }\n\n // cap para no sobre-castigar\n addScore = Math.min(0.35, addScore);\n\n if (hitCount > 0) addFlags.push(\"spam_keywords_en\");\n\n return { addScore, addFlags, hitCount };\n}\n\n/**\n * Gate principal (producto): deterministic + barato.\n */\nexport function gateLLM(text: string, config: GateConfig = {}): GateResult {\n const warnT = config.warnThreshold ?? 0.25;\n const blockT = config.blockThreshold ?? 0.6;\n\n const r = runEntropyFilter(text);\n\n // base\n let score = r.entropy_analysis.score;\n let flags = [...r.entropy_analysis.flags];\n\n // booster EN\n const booster = englishSpamBooster(text);\n if (booster.hitCount > 0) {\n score = clamp01(score + booster.addScore);\n flags = mergeFlags(flags, booster.addFlags);\n }\n\n // intención base (intention.ts)\n let intention = r.intention_evaluation.intention || \"unknown\";\n let confidence = r.intention_evaluation.confidence ?? 0;\n let rationale = r.intention_evaluation.rationale ?? \"\";\n\n // si pegó booster EN y quedó unknown → marketing_spam\n if (booster.hitCount > 0 && intention === \"unknown\") {\n intention = \"marketing_spam\";\n confidence = Math.max(confidence, 0.75);\n rationale = (rationale ? rationale + \" \" : \"\") + \"Detecté keywords típicas de spam/phishing en inglés.\";\n }\n\n // reglas fuertes (para BLOCK “vendible”)\n const hasSpam =\n flags.includes(\"spam_sales\") ||\n flags.includes(\"spam_keywords_en\");\n\n const hasMoneySignals =\n flags.includes(\"money_signal\") ||\n flags.includes(\"spam_kw_prize\") ||\n flags.includes(\"spam_kw_loan\");\n\n const strongSpam = hasSpam && hasMoneySignals;\n\n let action: GateAction = \"ALLOW\";\n if (score > blockT || strongSpam) action = \"BLOCK\";\n else if (score >= warnT) action = \"WARN\";\n\n return {\n action,\n entropy_score: score,\n flags,\n intention,\n confidence: clamp01(confidence),\n rationale,\n };\n}\n\n// Alias “bonito” para tu server: gate(text)\nexport const gate = gateLLM;\n"],"mappings":"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","names":["clamp01","clamp01"]}
|
package/dist/index.d.cts
ADDED
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
type EntropyResult$1 = {
|
|
2
|
+
score: number;
|
|
3
|
+
flags: string[];
|
|
4
|
+
};
|
|
5
|
+
declare function analyzeEntropy(text: string): EntropyResult$1;
|
|
6
|
+
|
|
7
|
+
type EntropyResult = {
|
|
8
|
+
score: number;
|
|
9
|
+
flags: string[];
|
|
10
|
+
};
|
|
11
|
+
type IntentionType = "unknown" | "request_help" | "marketing_spam" | "manipulation" | "conspiracy" | "misinformation";
|
|
12
|
+
type IntentionEvaluation = {
|
|
13
|
+
intention: IntentionType;
|
|
14
|
+
confidence: number;
|
|
15
|
+
rationale?: string;
|
|
16
|
+
};
|
|
17
|
+
type FilterResult = {
|
|
18
|
+
entropy_analysis: EntropyResult;
|
|
19
|
+
intention_evaluation: IntentionEvaluation;
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
declare function evaluateIntention(text: string): IntentionEvaluation;
|
|
23
|
+
|
|
24
|
+
declare function runEntropyFilter(text: string): FilterResult;
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Middleware agnóstico (NO depende de express types).
|
|
28
|
+
* Compatible con Express / Next API routes / cualquier framework estilo req-res-next.
|
|
29
|
+
*
|
|
30
|
+
* Espera: req.body.text (string)
|
|
31
|
+
* Escribe: req.entropy = FilterResult
|
|
32
|
+
*/
|
|
33
|
+
declare function entropyMiddleware(req: any, _res: any, next: any): void;
|
|
34
|
+
|
|
35
|
+
type GateAction = "ALLOW" | "WARN" | "BLOCK";
|
|
36
|
+
type GateResult = {
|
|
37
|
+
action: GateAction;
|
|
38
|
+
entropy_score: number;
|
|
39
|
+
flags: string[];
|
|
40
|
+
intention: string;
|
|
41
|
+
confidence: number;
|
|
42
|
+
rationale: string;
|
|
43
|
+
};
|
|
44
|
+
type GateConfig = {
|
|
45
|
+
warnThreshold?: number;
|
|
46
|
+
blockThreshold?: number;
|
|
47
|
+
};
|
|
48
|
+
/**
|
|
49
|
+
* Gate principal (producto): deterministic + barato.
|
|
50
|
+
*/
|
|
51
|
+
declare function gateLLM(text: string, config?: GateConfig): GateResult;
|
|
52
|
+
|
|
53
|
+
export { type EntropyResult, type FilterResult, type GateAction, type GateConfig, type GateResult, type IntentionEvaluation, analyzeEntropy, entropyMiddleware, evaluateIntention, gateLLM as gate, gateLLM, runEntropyFilter };
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
type EntropyResult$1 = {
|
|
2
|
+
score: number;
|
|
3
|
+
flags: string[];
|
|
4
|
+
};
|
|
5
|
+
declare function analyzeEntropy(text: string): EntropyResult$1;
|
|
6
|
+
|
|
7
|
+
type EntropyResult = {
|
|
8
|
+
score: number;
|
|
9
|
+
flags: string[];
|
|
10
|
+
};
|
|
11
|
+
type IntentionType = "unknown" | "request_help" | "marketing_spam" | "manipulation" | "conspiracy" | "misinformation";
|
|
12
|
+
type IntentionEvaluation = {
|
|
13
|
+
intention: IntentionType;
|
|
14
|
+
confidence: number;
|
|
15
|
+
rationale?: string;
|
|
16
|
+
};
|
|
17
|
+
type FilterResult = {
|
|
18
|
+
entropy_analysis: EntropyResult;
|
|
19
|
+
intention_evaluation: IntentionEvaluation;
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
declare function evaluateIntention(text: string): IntentionEvaluation;
|
|
23
|
+
|
|
24
|
+
declare function runEntropyFilter(text: string): FilterResult;
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Middleware agnóstico (NO depende de express types).
|
|
28
|
+
* Compatible con Express / Next API routes / cualquier framework estilo req-res-next.
|
|
29
|
+
*
|
|
30
|
+
* Espera: req.body.text (string)
|
|
31
|
+
* Escribe: req.entropy = FilterResult
|
|
32
|
+
*/
|
|
33
|
+
declare function entropyMiddleware(req: any, _res: any, next: any): void;
|
|
34
|
+
|
|
35
|
+
type GateAction = "ALLOW" | "WARN" | "BLOCK";
|
|
36
|
+
type GateResult = {
|
|
37
|
+
action: GateAction;
|
|
38
|
+
entropy_score: number;
|
|
39
|
+
flags: string[];
|
|
40
|
+
intention: string;
|
|
41
|
+
confidence: number;
|
|
42
|
+
rationale: string;
|
|
43
|
+
};
|
|
44
|
+
type GateConfig = {
|
|
45
|
+
warnThreshold?: number;
|
|
46
|
+
blockThreshold?: number;
|
|
47
|
+
};
|
|
48
|
+
/**
|
|
49
|
+
* Gate principal (producto): deterministic + barato.
|
|
50
|
+
*/
|
|
51
|
+
declare function gateLLM(text: string, config?: GateConfig): GateResult;
|
|
52
|
+
|
|
53
|
+
export { type EntropyResult, type FilterResult, type GateAction, type GateConfig, type GateResult, type IntentionEvaluation, analyzeEntropy, entropyMiddleware, evaluateIntention, gateLLM as gate, gateLLM, runEntropyFilter };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,233 @@
|
|
|
1
|
+
// src/entropy.ts
|
|
2
|
+
function clamp01(x) {
|
|
3
|
+
return Math.max(0, Math.min(1, x));
|
|
4
|
+
}
|
|
5
|
+
function countRegex(text, re) {
|
|
6
|
+
const m = text.match(re);
|
|
7
|
+
return m ? m.length : 0;
|
|
8
|
+
}
|
|
9
|
+
function analyzeEntropy(text) {
|
|
10
|
+
const raw = text || "";
|
|
11
|
+
const t = raw.toLowerCase();
|
|
12
|
+
const flags = [];
|
|
13
|
+
let score = 0;
|
|
14
|
+
if (/\b(ahora|ya|urgente|última|hoy|inmediato)\b/.test(t)) {
|
|
15
|
+
flags.push("urgency");
|
|
16
|
+
score += 0.2;
|
|
17
|
+
}
|
|
18
|
+
if (/\b(compra|oferta|promo|descuento|gratis|clic|click|off)\b/.test(t)) {
|
|
19
|
+
flags.push("spam_sales");
|
|
20
|
+
score += 0.25;
|
|
21
|
+
}
|
|
22
|
+
const moneyHits = countRegex(raw, /\$+/g);
|
|
23
|
+
if (moneyHits > 0) {
|
|
24
|
+
flags.push("money_signal");
|
|
25
|
+
score += Math.min(0.2, moneyHits * 0.05);
|
|
26
|
+
}
|
|
27
|
+
const exclam = countRegex(raw, /!/g);
|
|
28
|
+
const capsRatio = (() => {
|
|
29
|
+
const letters = raw.match(/[A-Za-zÁÉÍÓÚÜÑáéíóúüñ]/g) || [];
|
|
30
|
+
if (letters.length === 0) return 0;
|
|
31
|
+
const caps = (raw.match(/[A-ZÁÉÍÓÚÜÑ]/g) || []).length;
|
|
32
|
+
return caps / letters.length;
|
|
33
|
+
})();
|
|
34
|
+
if (exclam >= 3 || capsRatio >= 0.35) {
|
|
35
|
+
flags.push("shouting");
|
|
36
|
+
score += 0.2;
|
|
37
|
+
}
|
|
38
|
+
if (/\b(si de verdad|si me quisieras|es tu culpa|no tienes opción|me debes)\b/.test(t)) {
|
|
39
|
+
flags.push("emotional_manipulation");
|
|
40
|
+
score += 0.35;
|
|
41
|
+
}
|
|
42
|
+
if (/\b(todos lo saben|lo esconden|la verdad oculta|ellos no quieren|simulación)\b/.test(t)) {
|
|
43
|
+
flags.push("conspiracy_vague");
|
|
44
|
+
score += 0.2;
|
|
45
|
+
}
|
|
46
|
+
if (/\b(la cultura lo prueba|es obvio|todo mundo sabe|se sabe|está claro)\b/.test(t)) {
|
|
47
|
+
flags.push("weak_evidence");
|
|
48
|
+
score += 0.2;
|
|
49
|
+
}
|
|
50
|
+
if (/\b(ellos|la élite|los de arriba)\b/.test(t) && /\b(esconden|ocultan|tapan)\b/.test(t)) {
|
|
51
|
+
flags.push("hidden_actor");
|
|
52
|
+
score += 0.15;
|
|
53
|
+
}
|
|
54
|
+
if (/\b(física cuántica|cuantica|cuántico|quantum)\b/.test(t)) {
|
|
55
|
+
flags.push("pseudo_science_quantum");
|
|
56
|
+
score += 0.2;
|
|
57
|
+
}
|
|
58
|
+
const manifestHits = countRegex(t, /\b(manifestar|manifestación|decretar|decreto|vibración|vibracion|frecuencia|energía|energia|ley de la atracción|universo me lo dará)\b/g) + countRegex(t, /\b(realine(a|ar)\b.*\bátom|\bátom|\batomos\b)/g);
|
|
59
|
+
if (manifestHits > 0) {
|
|
60
|
+
flags.push("magic_manifesting");
|
|
61
|
+
score += Math.min(0.35, 0.15 + manifestHits * 0.06);
|
|
62
|
+
}
|
|
63
|
+
if (/\b(no hay una verdad objetiva|no existe la verdad objetiva|tu verdad|mi verdad|la verdad es relativa|lo que importa es lo que sientes)\b/.test(t)) {
|
|
64
|
+
flags.push("truth_relativism");
|
|
65
|
+
score += 0.35;
|
|
66
|
+
}
|
|
67
|
+
if (/\b(deben|debe)\b.*\b(obligatoriamente|por lo tanto|por ende)\b/.test(t) || /\b(la materia)\b.*\b(se subordina|obedece)\b/.test(t)) {
|
|
68
|
+
flags.push("broken_causality");
|
|
69
|
+
score += 0.2;
|
|
70
|
+
}
|
|
71
|
+
score = clamp01(score);
|
|
72
|
+
return { score, flags };
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
// src/intention.ts
|
|
76
|
+
function clamp012(x) {
|
|
77
|
+
return Math.max(0, Math.min(1, x));
|
|
78
|
+
}
|
|
79
|
+
function evaluateIntention(text) {
|
|
80
|
+
const raw = text || "";
|
|
81
|
+
const t = raw.toLowerCase();
|
|
82
|
+
const isHelp = /\b(ayuda|ayúdame|explica|resume|resumir|cómo|como|puedes|podrías|por favor)\b/.test(
|
|
83
|
+
t
|
|
84
|
+
);
|
|
85
|
+
const isSpam = /\b(compra|oferta|promo|descuento|gratis|haz clic|click|off|90%|% off)\b/.test(
|
|
86
|
+
t
|
|
87
|
+
) || (raw.match(/\$+/g) || []).length > 0;
|
|
88
|
+
const isManip = /\b(si de verdad|si me quisieras|es tu culpa|no tienes opción|me debes)\b/.test(
|
|
89
|
+
t
|
|
90
|
+
);
|
|
91
|
+
const isConsp = /\b(simulación|todos lo saben|lo esconden|verdad oculta|ellos no quieren)\b/.test(
|
|
92
|
+
t
|
|
93
|
+
);
|
|
94
|
+
const isMisinformation = /\b(física cuántica|cuantica|cuántica|cuántico|quantum)\b/.test(t) || /\b(manifestar|manifestación|decretar|decreto|vibración|vibracion|frecuencia|energía|energia|ley de la atracción)\b/.test(t) || /\b(no hay una verdad objetiva|no existe la verdad objetiva|tu verdad|mi verdad|la verdad es relativa)\b/.test(t) || /\b(la materia)\b.*\b(se subordina|obedece)\b/.test(t);
|
|
95
|
+
let intention = "unknown";
|
|
96
|
+
let confidence = 0;
|
|
97
|
+
let rationale = "";
|
|
98
|
+
if (isSpam) {
|
|
99
|
+
intention = "marketing_spam";
|
|
100
|
+
confidence = 0.85;
|
|
101
|
+
rationale = "Detect\xE9 se\xF1ales de venta agresiva/urgencia/dinero.";
|
|
102
|
+
} else if (isManip) {
|
|
103
|
+
intention = "manipulation";
|
|
104
|
+
confidence = 0.85;
|
|
105
|
+
rationale = "Detect\xE9 coerci\xF3n/culpa/chantaje emocional.";
|
|
106
|
+
} else if (isConsp) {
|
|
107
|
+
intention = "conspiracy";
|
|
108
|
+
confidence = 0.75;
|
|
109
|
+
rationale = "Detect\xE9 marco conspirativo vago ('lo esconden', 'todos lo saben').";
|
|
110
|
+
} else if (isMisinformation) {
|
|
111
|
+
intention = "misinformation";
|
|
112
|
+
confidence = 0.85;
|
|
113
|
+
rationale = "Detect\xE9 patr\xF3n de pseudo-ciencia/pensamiento m\xE1gico/relativismo de la verdad (alta probabilidad de desinformaci\xF3n).";
|
|
114
|
+
} else if (isHelp) {
|
|
115
|
+
intention = "request_help";
|
|
116
|
+
confidence = 0.7;
|
|
117
|
+
rationale = "Parece una petici\xF3n leg\xEDtima de ayuda/explicaci\xF3n.";
|
|
118
|
+
}
|
|
119
|
+
return { intention, confidence: clamp012(confidence), rationale };
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
// src/wrapper.ts
|
|
123
|
+
function runEntropyFilter(text) {
|
|
124
|
+
const entropy_analysis = analyzeEntropy(text);
|
|
125
|
+
let intention_evaluation = evaluateIntention(text);
|
|
126
|
+
const hardFlags = new Set(entropy_analysis.flags);
|
|
127
|
+
const epistemicEntropy = hardFlags.has("truth_relativism") || hardFlags.has("magic_manifesting") || hardFlags.has("pseudo_science_quantum") || hardFlags.has("broken_causality");
|
|
128
|
+
if (epistemicEntropy) {
|
|
129
|
+
intention_evaluation = {
|
|
130
|
+
intention: "misinformation",
|
|
131
|
+
confidence: Math.max(intention_evaluation.confidence ?? 0.7, 0.8),
|
|
132
|
+
rationale: "Detect\xE9 patr\xF3n de pseudo-ciencia/pensamiento m\xE1gico/relativismo de la verdad; alta probabilidad de desinformaci\xF3n o argumento sin anclaje causal."
|
|
133
|
+
};
|
|
134
|
+
}
|
|
135
|
+
return { entropy_analysis, intention_evaluation };
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
// src/middleware.ts
|
|
139
|
+
function entropyMiddleware(req, _res, next) {
|
|
140
|
+
const text = req?.body?.text;
|
|
141
|
+
const result = typeof text === "string" ? runEntropyFilter(text) : runEntropyFilter("");
|
|
142
|
+
req.entropy = result;
|
|
143
|
+
next?.();
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
// src/gate.ts
|
|
147
|
+
function clamp013(x) {
|
|
148
|
+
return Math.max(0, Math.min(1, x));
|
|
149
|
+
}
|
|
150
|
+
function mergeFlags(base, extra) {
|
|
151
|
+
const set = new Set(base);
|
|
152
|
+
const out = [...base];
|
|
153
|
+
for (const f of extra) {
|
|
154
|
+
if (!set.has(f)) {
|
|
155
|
+
set.add(f);
|
|
156
|
+
out.push(f);
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
return out;
|
|
160
|
+
}
|
|
161
|
+
function englishSpamBooster(rawText) {
|
|
162
|
+
const t = (rawText || "").toLowerCase();
|
|
163
|
+
const patterns = [
|
|
164
|
+
{ re: /\bfree\b/g, score: 0.08, flag: "spam_kw_free" },
|
|
165
|
+
{ re: /\bwinner\b|\bwon\b|\bcongratulations\b/g, score: 0.1, flag: "spam_kw_winner" },
|
|
166
|
+
{ re: /\bclaim\b|\bredeem\b/g, score: 0.08, flag: "spam_kw_claim" },
|
|
167
|
+
{ re: /\bclick\b|\bclick here\b|\bopen link\b|\btap here\b/g, score: 0.1, flag: "spam_kw_click" },
|
|
168
|
+
{ re: /\blimited time\b|\bact now\b|\bfinal notice\b|\bbefore midnight\b/g, score: 0.1, flag: "spam_kw_urgency_en" },
|
|
169
|
+
{ re: /\bverify\b|\bconfirm\b|\baccount\b.*\b(suspended|locked)\b/g, score: 0.12, flag: "spam_kw_verify" },
|
|
170
|
+
{ re: /\bprize\b|\bgift card\b|\bgiftcard\b|\bvoucher\b|\biphone\b|\bsurvey\b/g, score: 0.1, flag: "spam_kw_prize" },
|
|
171
|
+
{ re: /\bloan\b|\bpre-?approved\b|\bno credit check\b/g, score: 0.12, flag: "spam_kw_loan" },
|
|
172
|
+
{ re: /\bcrypto\b|\bairdrop\b|\bwallet\b|\bseed phrase\b/g, score: 0.1, flag: "spam_kw_crypto" },
|
|
173
|
+
{ re: /\bdelivery failed\b|\breschedule\b|\bpackage\b|\bcourier\b/g, score: 0.1, flag: "spam_kw_delivery" },
|
|
174
|
+
{ re: /\btax refund\b|\bunpaid\b|\brefund\b|\bchargeback\b/g, score: 0.1, flag: "spam_kw_refund" }
|
|
175
|
+
];
|
|
176
|
+
let addScore = 0;
|
|
177
|
+
const addFlags = [];
|
|
178
|
+
let hitCount = 0;
|
|
179
|
+
for (const p of patterns) {
|
|
180
|
+
const m = t.match(p.re);
|
|
181
|
+
if (m && m.length > 0) {
|
|
182
|
+
hitCount += m.length;
|
|
183
|
+
addScore += p.score;
|
|
184
|
+
addFlags.push(p.flag);
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
addScore = Math.min(0.35, addScore);
|
|
188
|
+
if (hitCount > 0) addFlags.push("spam_keywords_en");
|
|
189
|
+
return { addScore, addFlags, hitCount };
|
|
190
|
+
}
|
|
191
|
+
function gateLLM(text, config = {}) {
|
|
192
|
+
const warnT = config.warnThreshold ?? 0.25;
|
|
193
|
+
const blockT = config.blockThreshold ?? 0.6;
|
|
194
|
+
const r = runEntropyFilter(text);
|
|
195
|
+
let score = r.entropy_analysis.score;
|
|
196
|
+
let flags = [...r.entropy_analysis.flags];
|
|
197
|
+
const booster = englishSpamBooster(text);
|
|
198
|
+
if (booster.hitCount > 0) {
|
|
199
|
+
score = clamp013(score + booster.addScore);
|
|
200
|
+
flags = mergeFlags(flags, booster.addFlags);
|
|
201
|
+
}
|
|
202
|
+
let intention = r.intention_evaluation.intention || "unknown";
|
|
203
|
+
let confidence = r.intention_evaluation.confidence ?? 0;
|
|
204
|
+
let rationale = r.intention_evaluation.rationale ?? "";
|
|
205
|
+
if (booster.hitCount > 0 && intention === "unknown") {
|
|
206
|
+
intention = "marketing_spam";
|
|
207
|
+
confidence = Math.max(confidence, 0.75);
|
|
208
|
+
rationale = (rationale ? rationale + " " : "") + "Detect\xE9 keywords t\xEDpicas de spam/phishing en ingl\xE9s.";
|
|
209
|
+
}
|
|
210
|
+
const hasSpam = flags.includes("spam_sales") || flags.includes("spam_keywords_en");
|
|
211
|
+
const hasMoneySignals = flags.includes("money_signal") || flags.includes("spam_kw_prize") || flags.includes("spam_kw_loan");
|
|
212
|
+
const strongSpam = hasSpam && hasMoneySignals;
|
|
213
|
+
let action = "ALLOW";
|
|
214
|
+
if (score > blockT || strongSpam) action = "BLOCK";
|
|
215
|
+
else if (score >= warnT) action = "WARN";
|
|
216
|
+
return {
|
|
217
|
+
action,
|
|
218
|
+
entropy_score: score,
|
|
219
|
+
flags,
|
|
220
|
+
intention,
|
|
221
|
+
confidence: clamp013(confidence),
|
|
222
|
+
rationale
|
|
223
|
+
};
|
|
224
|
+
}
|
|
225
|
+
export {
|
|
226
|
+
analyzeEntropy,
|
|
227
|
+
entropyMiddleware,
|
|
228
|
+
evaluateIntention,
|
|
229
|
+
gateLLM as gate,
|
|
230
|
+
gateLLM,
|
|
231
|
+
runEntropyFilter
|
|
232
|
+
};
|
|
233
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/entropy.ts","../src/intention.ts","../src/wrapper.ts","../src/middleware.ts","../src/gate.ts"],"sourcesContent":["// src/entropy.ts\nexport type EntropyResult = {\n score: number; // 0..1\n flags: string[];\n};\n\nfunction clamp01(x: number) {\n return Math.max(0, Math.min(1, x));\n}\n\nfunction countRegex(text: string, re: RegExp) {\n const m = text.match(re);\n return m ? m.length : 0;\n}\n\nexport function analyzeEntropy(text: string): EntropyResult {\n const raw = text || \"\";\n const t = raw.toLowerCase();\n const flags: string[] = [];\n let score = 0;\n\n // 1) Urgencia / presión\n if (/\\b(ahora|ya|urgente|última|hoy|inmediato)\\b/.test(t)) {\n flags.push(\"urgency\");\n score += 0.20;\n }\n\n // 2) Spam / venta agresiva\n if (/\\b(compra|oferta|promo|descuento|gratis|clic|click|off)\\b/.test(t)) {\n flags.push(\"spam_sales\");\n score += 0.25;\n }\n\n // 3) Señales $$$ / símbolos\n const moneyHits = countRegex(raw, /\\$+/g);\n if (moneyHits > 0) {\n flags.push(\"money_signal\");\n score += Math.min(0.20, moneyHits * 0.05);\n }\n\n // 4) Exceso de signos / gritos\n const exclam = countRegex(raw, /!/g);\n const capsRatio = (() => {\n const letters = raw.match(/[A-Za-zÁÉÍÓÚÜÑáéíóúüñ]/g) || [];\n if (letters.length === 0) return 0;\n const caps = (raw.match(/[A-ZÁÉÍÓÚÜÑ]/g) || []).length;\n return caps / letters.length;\n })();\n\n if (exclam >= 3 || capsRatio >= 0.35) {\n flags.push(\"shouting\");\n score += 0.20;\n }\n\n // 5) Manipulación / culpa / coerción\n if (/\\b(si de verdad|si me quisieras|es tu culpa|no tienes opción|me debes)\\b/.test(t)) {\n flags.push(\"emotional_manipulation\");\n score += 0.35;\n }\n\n // 6) Conspiración vaga / “todos lo saben”\n if (/\\b(todos lo saben|lo esconden|la verdad oculta|ellos no quieren|simulación)\\b/.test(t)) {\n flags.push(\"conspiracy_vague\");\n score += 0.20;\n }\n\n // 6.5) “Prueba vaga” / apelación a cultura como evidencia\n if (/\\b(la cultura lo prueba|es obvio|todo mundo sabe|se sabe|está claro)\\b/.test(t)) {\n flags.push(\"weak_evidence\");\n score += 0.20;\n }\n\n // 6.6) Totalización + agente oculto (“ellos”)\n if (/\\b(ellos|la élite|los de arriba)\\b/.test(t) && /\\b(esconden|ocultan|tapan)\\b/.test(t)) {\n flags.push(\"hidden_actor\");\n score += 0.15;\n }\n\n // ------------------------------------------------------------\n // NUEVO: Entropía por pseudo-ciencia / pensamiento mágico / relativismo\n // ------------------------------------------------------------\n\n // 7) Pseudo-ciencia \"cuántica\" usada como licencia mágica\n if (/\\b(física cuántica|cuantica|cuántico|quantum)\\b/.test(t)) {\n flags.push(\"pseudo_science_quantum\");\n score += 0.20;\n }\n\n // 8) Manifestación mágica / decretos / vibración / energía como causalidad\n const manifestHits =\n countRegex(t, /\\b(manifestar|manifestación|decretar|decreto|vibración|vibracion|frecuencia|energía|energia|ley de la atracción|universo me lo dará)\\b/g) +\n countRegex(t, /\\b(realine(a|ar)\\b.*\\bátom|\\bátom|\\batomos\\b)/g);\n\n if (manifestHits > 0) {\n flags.push(\"magic_manifesting\");\n score += Math.min(0.35, 0.15 + manifestHits * 0.06);\n }\n\n // 9) Relativismo de la verdad / negación explícita de verdad objetiva\n if (/\\b(no hay una verdad objetiva|no existe la verdad objetiva|tu verdad|mi verdad|la verdad es relativa|lo que importa es lo que sientes)\\b/.test(t)) {\n flags.push(\"truth_relativism\");\n score += 0.35;\n }\n\n // 10) Causalidad rota / obligación metafísica (\"debe\" porque lo deseo)\n if (/\\b(deben|debe)\\b.*\\b(obligatoriamente|por lo tanto|por ende)\\b/.test(t) || /\\b(la materia)\\b.*\\b(se subordina|obedece)\\b/.test(t)) {\n flags.push(\"broken_causality\");\n score += 0.20;\n }\n\n // Normaliza: score final 0..1\n score = clamp01(score);\n\n return { score, flags };\n}\n","// src/intention.ts\nimport type { IntentionEvaluation, IntentionType } from \"./types\";\n\nfunction clamp01(x: number) {\n return Math.max(0, Math.min(1, x));\n}\n\nexport function evaluateIntention(text: string): IntentionEvaluation {\n const raw = text || \"\";\n const t = raw.toLowerCase();\n\n // Heurísticas rápidas (MVP)\n const isHelp =\n /\\b(ayuda|ayúdame|explica|resume|resumir|cómo|como|puedes|podrías|por favor)\\b/.test(\n t\n );\n\n const isSpam =\n /\\b(compra|oferta|promo|descuento|gratis|haz clic|click|off|90%|% off)\\b/.test(\n t\n ) || (raw.match(/\\$+/g) || []).length > 0;\n\n const isManip =\n /\\b(si de verdad|si me quisieras|es tu culpa|no tienes opción|me debes)\\b/.test(\n t\n );\n\n const isConsp =\n /\\b(simulación|todos lo saben|lo esconden|verdad oculta|ellos no quieren)\\b/.test(\n t\n );\n\n // NUEVO: pseudo-ciencia / pensamiento mágico / relativismo (desinformación)\n const isMisinformation =\n /\\b(física cuántica|cuantica|cuántica|cuántico|quantum)\\b/.test(t) ||\n /\\b(manifestar|manifestación|decretar|decreto|vibración|vibracion|frecuencia|energía|energia|ley de la atracción)\\b/.test(t) ||\n /\\b(no hay una verdad objetiva|no existe la verdad objetiva|tu verdad|mi verdad|la verdad es relativa)\\b/.test(t) ||\n /\\b(la materia)\\b.*\\b(se subordina|obedece)\\b/.test(t);\n\n let intention: IntentionType = \"unknown\";\n let confidence = 0.0;\n let rationale = \"\";\n\n if (isSpam) {\n intention = \"marketing_spam\";\n confidence = 0.85;\n rationale = \"Detecté señales de venta agresiva/urgencia/dinero.\";\n } else if (isManip) {\n intention = \"manipulation\";\n confidence = 0.85;\n rationale = \"Detecté coerción/culpa/chantaje emocional.\";\n } else if (isConsp) {\n intention = \"conspiracy\";\n confidence = 0.75;\n rationale = \"Detecté marco conspirativo vago ('lo esconden', 'todos lo saben').\";\n } else if (isMisinformation) {\n intention = \"misinformation\";\n confidence = 0.85;\n rationale =\n \"Detecté patrón de pseudo-ciencia/pensamiento mágico/relativismo de la verdad (alta probabilidad de desinformación).\";\n } else if (isHelp) {\n intention = \"request_help\";\n confidence = 0.7;\n rationale = \"Parece una petición legítima de ayuda/explicación.\";\n }\n\n return { intention, confidence: clamp01(confidence), rationale };\n}\n","// src/wrapper.ts\nimport { analyzeEntropy } from \"./entropy\";\nimport { evaluateIntention } from \"./intention\";\nimport type { FilterResult, IntentionEvaluation } from \"./types\";\n\nexport function runEntropyFilter(text: string): FilterResult {\n const entropy_analysis = analyzeEntropy(text);\n let intention_evaluation: IntentionEvaluation = evaluateIntention(text);\n\n // Corrección: si el texto trae señales fuertes de entropía epistemológica,\n // no lo clasifiques como \"request_help\" solo por ser pregunta larga.\n const hardFlags = new Set(entropy_analysis.flags);\n const epistemicEntropy =\n hardFlags.has(\"truth_relativism\") ||\n hardFlags.has(\"magic_manifesting\") ||\n hardFlags.has(\"pseudo_science_quantum\") ||\n hardFlags.has(\"broken_causality\");\n\n if (epistemicEntropy) {\n intention_evaluation = {\n intention: \"misinformation\",\n confidence: Math.max(intention_evaluation.confidence ?? 0.7, 0.8),\n rationale:\n \"Detecté patrón de pseudo-ciencia/pensamiento mágico/relativismo de la verdad; alta probabilidad de desinformación o argumento sin anclaje causal.\"\n };\n }\n\n return { entropy_analysis, intention_evaluation };\n}\n","// src/middleware.ts\nimport { runEntropyFilter } from \"./wrapper\";\nimport type { FilterResult } from \"./types\";\n\n/**\n * Middleware agnóstico (NO depende de express types).\n * Compatible con Express / Next API routes / cualquier framework estilo req-res-next.\n *\n * Espera: req.body.text (string)\n * Escribe: req.entropy = FilterResult\n */\nexport function entropyMiddleware(req: any, _res: any, next: any) {\n const text = req?.body?.text;\n\n const result: FilterResult =\n typeof text === \"string\" ? runEntropyFilter(text) : runEntropyFilter(\"\");\n\n req.entropy = result;\n next?.();\n}\n\n/**\n * Tipo auxiliar opcional (sin forzar dependencias).\n * Útil si quieres tipar tu req en tu app.\n */\nexport type EntropyAugmentedRequest = {\n body?: { text?: unknown };\n entropy?: FilterResult;\n};\n","// src/gate.ts\nimport { runEntropyFilter } from \"./wrapper\";\n\nexport type GateAction = \"ALLOW\" | \"WARN\" | \"BLOCK\";\n\nexport type GateResult = {\n action: GateAction;\n entropy_score: number;\n flags: string[];\n intention: string;\n confidence: number;\n rationale: string;\n};\n\nexport type GateConfig = {\n warnThreshold?: number; // default: 0.25\n blockThreshold?: number; // default: 0.60\n};\n\n/** Clamp 0..1 */\nfunction clamp01(x: number) {\n return Math.max(0, Math.min(1, x));\n}\n\n/** Add unique flags preserving order */\nfunction mergeFlags(base: string[], extra: string[]) {\n const set = new Set(base);\n const out = [...base];\n for (const f of extra) {\n if (!set.has(f)) {\n set.add(f);\n out.push(f);\n }\n }\n return out;\n}\n\n/**\n * Detección de spam/phishing en inglés por keywords.\n * Se implementa como “booster” de score + flags (sin tocar entropy.ts).\n */\nfunction englishSpamBooster(rawText: string): {\n addScore: number;\n addFlags: string[];\n hitCount: number;\n} {\n const t = (rawText || \"\").toLowerCase();\n\n // Patrones típicos (spam / phishing / promos agresivas)\n const patterns: Array<{ re: RegExp; score: number; flag: string }> = [\n { re: /\\bfree\\b/g, score: 0.08, flag: \"spam_kw_free\" },\n { re: /\\bwinner\\b|\\bwon\\b|\\bcongratulations\\b/g, score: 0.10, flag: \"spam_kw_winner\" },\n { re: /\\bclaim\\b|\\bredeem\\b/g, score: 0.08, flag: \"spam_kw_claim\" },\n { re: /\\bclick\\b|\\bclick here\\b|\\bopen link\\b|\\btap here\\b/g, score: 0.10, flag: \"spam_kw_click\" },\n { re: /\\blimited time\\b|\\bact now\\b|\\bfinal notice\\b|\\bbefore midnight\\b/g, score: 0.10, flag: \"spam_kw_urgency_en\" },\n { re: /\\bverify\\b|\\bconfirm\\b|\\baccount\\b.*\\b(suspended|locked)\\b/g, score: 0.12, flag: \"spam_kw_verify\" },\n { re: /\\bprize\\b|\\bgift card\\b|\\bgiftcard\\b|\\bvoucher\\b|\\biphone\\b|\\bsurvey\\b/g, score: 0.10, flag: \"spam_kw_prize\" },\n { re: /\\bloan\\b|\\bpre-?approved\\b|\\bno credit check\\b/g, score: 0.12, flag: \"spam_kw_loan\" },\n { re: /\\bcrypto\\b|\\bairdrop\\b|\\bwallet\\b|\\bseed phrase\\b/g, score: 0.10, flag: \"spam_kw_crypto\" },\n { re: /\\bdelivery failed\\b|\\breschedule\\b|\\bpackage\\b|\\bcourier\\b/g, score: 0.10, flag: \"spam_kw_delivery\" },\n { re: /\\btax refund\\b|\\bunpaid\\b|\\brefund\\b|\\bchargeback\\b/g, score: 0.10, flag: \"spam_kw_refund\" },\n ];\n\n let addScore = 0;\n const addFlags: string[] = [];\n let hitCount = 0;\n\n for (const p of patterns) {\n const m = t.match(p.re);\n if (m && m.length > 0) {\n hitCount += m.length;\n addScore += p.score; // suma “por patrón”, no por ocurrencia (controlado)\n addFlags.push(p.flag);\n }\n }\n\n // cap para no sobre-castigar\n addScore = Math.min(0.35, addScore);\n\n if (hitCount > 0) addFlags.push(\"spam_keywords_en\");\n\n return { addScore, addFlags, hitCount };\n}\n\n/**\n * Gate principal (producto): deterministic + barato.\n */\nexport function gateLLM(text: string, config: GateConfig = {}): GateResult {\n const warnT = config.warnThreshold ?? 0.25;\n const blockT = config.blockThreshold ?? 0.6;\n\n const r = runEntropyFilter(text);\n\n // base\n let score = r.entropy_analysis.score;\n let flags = [...r.entropy_analysis.flags];\n\n // booster EN\n const booster = englishSpamBooster(text);\n if (booster.hitCount > 0) {\n score = clamp01(score + booster.addScore);\n flags = mergeFlags(flags, booster.addFlags);\n }\n\n // intención base (intention.ts)\n let intention = r.intention_evaluation.intention || \"unknown\";\n let confidence = r.intention_evaluation.confidence ?? 0;\n let rationale = r.intention_evaluation.rationale ?? \"\";\n\n // si pegó booster EN y quedó unknown → marketing_spam\n if (booster.hitCount > 0 && intention === \"unknown\") {\n intention = \"marketing_spam\";\n confidence = Math.max(confidence, 0.75);\n rationale = (rationale ? rationale + \" \" : \"\") + \"Detecté keywords típicas de spam/phishing en inglés.\";\n }\n\n // reglas fuertes (para BLOCK “vendible”)\n const hasSpam =\n flags.includes(\"spam_sales\") ||\n flags.includes(\"spam_keywords_en\");\n\n const hasMoneySignals =\n flags.includes(\"money_signal\") ||\n flags.includes(\"spam_kw_prize\") ||\n flags.includes(\"spam_kw_loan\");\n\n const strongSpam = hasSpam && hasMoneySignals;\n\n let action: GateAction = \"ALLOW\";\n if (score > blockT || strongSpam) action = \"BLOCK\";\n else if (score >= warnT) action = \"WARN\";\n\n return {\n action,\n entropy_score: score,\n flags,\n intention,\n confidence: clamp01(confidence),\n rationale,\n };\n}\n\n// Alias “bonito” para tu server: gate(text)\nexport const gate = gateLLM;\n"],"mappings":"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","names":["clamp01","clamp01"]}
|
package/package.json
ADDED
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "llm-entropy-filter",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Fast entropy and intent gate for LLM inputs (ALLOW/WARN/BLOCK). Reduces hallucinations, cost and spam before calling an LLM.",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"llm",
|
|
7
|
+
"ai",
|
|
8
|
+
"prompt-filter",
|
|
9
|
+
"input-validation",
|
|
10
|
+
"entropy",
|
|
11
|
+
"spam-detection",
|
|
12
|
+
"content-moderation",
|
|
13
|
+
"heuristics",
|
|
14
|
+
"openai",
|
|
15
|
+
"guardrails"
|
|
16
|
+
],
|
|
17
|
+
"license": "Apache-2.0",
|
|
18
|
+
"type": "module",
|
|
19
|
+
"sideEffects": false,
|
|
20
|
+
"engines": {
|
|
21
|
+
"node": ">=18"
|
|
22
|
+
},
|
|
23
|
+
"main": "./dist/index.cjs",
|
|
24
|
+
"module": "./dist/index.js",
|
|
25
|
+
"types": "./dist/index.d.ts",
|
|
26
|
+
"exports": {
|
|
27
|
+
".": {
|
|
28
|
+
"types": "./dist/index.d.ts",
|
|
29
|
+
"import": "./dist/index.js",
|
|
30
|
+
"require": "./dist/index.cjs"
|
|
31
|
+
}
|
|
32
|
+
},
|
|
33
|
+
"files": [
|
|
34
|
+
"dist",
|
|
35
|
+
"README.md"
|
|
36
|
+
],
|
|
37
|
+
"scripts": {
|
|
38
|
+
"clean": "rm -rf dist node_modules package-lock.json",
|
|
39
|
+
"clean:dist": "rm -rf dist",
|
|
40
|
+
"build": "tsup",
|
|
41
|
+
"demo": "npm run build && node demo/demo.mjs",
|
|
42
|
+
"bench": "npm run build && node bench/benchmark.mjs",
|
|
43
|
+
"serve": "npm run build && node demo/server.mjs",
|
|
44
|
+
"prepublishOnly": "npm run clean:dist && npm run build"
|
|
45
|
+
},
|
|
46
|
+
"repository": {
|
|
47
|
+
"type": "git",
|
|
48
|
+
"url": "git+https://github.com/rosatisoft/llm-entropy-filter.git"
|
|
49
|
+
},
|
|
50
|
+
"bugs": {
|
|
51
|
+
"url": "https://github.com/rosatisoft/llm-entropy-filter/issues"
|
|
52
|
+
},
|
|
53
|
+
"homepage": "https://github.com/rosatisoft/llm-entropy-filter#readme",
|
|
54
|
+
"devDependencies": {
|
|
55
|
+
"@types/express": "^5.0.6",
|
|
56
|
+
"@types/node": "^25.0.10",
|
|
57
|
+
"autocannon": "^8.0.0",
|
|
58
|
+
"minimist": "^1.2.8",
|
|
59
|
+
"tsup": "^8.5.1",
|
|
60
|
+
"typescript": "^5.9.3",
|
|
61
|
+
"express": "^5.2.1",
|
|
62
|
+
"openai": "^6.16.0"
|
|
63
|
+
}
|
|
64
|
+
}
|