mirror of
https://github.com/elder-plinius/P4RS3LT0NGV3.git
synced 2026-07-21 03:21:05 +02:00
Merge pull request #22 from GangGreenTemperTatum/ads/latin-promptcraft-anticlassifier
feat: add Latin-root lexeme analysis to prompt tools
This commit is contained in:
+137
-1
@@ -3946,6 +3946,143 @@ html {
|
||||
text-underline-offset: 2px;
|
||||
}
|
||||
|
||||
.lexeme-analysis-card {
|
||||
margin: 0 0 14px 0;
|
||||
padding: 14px;
|
||||
border: 1px solid var(--input-border);
|
||||
border-radius: 8px;
|
||||
background:
|
||||
linear-gradient(135deg, rgba(25, 118, 210, 0.08), rgba(102, 187, 106, 0.06)),
|
||||
var(--main-bg-color);
|
||||
box-shadow: 0 4px 14px rgba(0,0,0,0.08);
|
||||
}
|
||||
|
||||
.lexeme-analysis-header {
|
||||
display: flex;
|
||||
gap: 16px;
|
||||
align-items: flex-start;
|
||||
justify-content: space-between;
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
|
||||
.lexeme-analysis-header h4 {
|
||||
margin: 2px 0 6px 0;
|
||||
font-size: 1rem;
|
||||
}
|
||||
|
||||
.lexeme-analysis-header p {
|
||||
margin: 0;
|
||||
color: var(--text-muted);
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
.lexeme-analysis-kicker {
|
||||
font-size: 0.72rem;
|
||||
font-weight: 700;
|
||||
letter-spacing: 0.08em;
|
||||
text-transform: uppercase;
|
||||
color: var(--accent-color);
|
||||
}
|
||||
|
||||
.lexeme-neutralize-btn {
|
||||
align-self: center;
|
||||
}
|
||||
|
||||
.lexeme-analysis-list {
|
||||
display: grid;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.lexeme-analysis-item {
|
||||
border: 1px solid var(--input-border);
|
||||
border-radius: 8px;
|
||||
background: rgba(255,255,255,0.03);
|
||||
padding: 12px;
|
||||
}
|
||||
|
||||
.lexeme-analysis-item-top {
|
||||
display: flex;
|
||||
gap: 12px;
|
||||
align-items: flex-start;
|
||||
justify-content: space-between;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.lexeme-analysis-term-group {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 6px;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.lexeme-term {
|
||||
font-size: 0.95rem;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.lexeme-chip {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
padding: 2px 8px;
|
||||
border-radius: 999px;
|
||||
border: 1px solid var(--input-border);
|
||||
background: var(--secondary-bg);
|
||||
color: var(--text-muted);
|
||||
font-size: 0.74rem;
|
||||
line-height: 1.4;
|
||||
}
|
||||
|
||||
.lexeme-analysis-domain {
|
||||
color: var(--text-muted);
|
||||
font-size: 0.82rem;
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.lexeme-analysis-rationale {
|
||||
margin: 0 0 10px 0;
|
||||
line-height: 1.45;
|
||||
color: var(--text-color);
|
||||
}
|
||||
|
||||
.lexeme-analysis-rewrites {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.lexeme-rewrite-btn {
|
||||
padding: 7px 10px;
|
||||
border-radius: 999px;
|
||||
border: 1px solid rgba(25, 118, 210, 0.35);
|
||||
background: rgba(25, 118, 210, 0.08);
|
||||
color: var(--text-color);
|
||||
font: inherit;
|
||||
font-size: 0.82rem;
|
||||
cursor: pointer;
|
||||
transition: transform 0.15s ease, box-shadow 0.15s ease, background 0.15s ease;
|
||||
}
|
||||
|
||||
.lexeme-rewrite-btn:hover {
|
||||
transform: translateY(-1px);
|
||||
background: rgba(25, 118, 210, 0.14);
|
||||
box-shadow: 0 4px 10px rgba(25, 118, 210, 0.14);
|
||||
}
|
||||
|
||||
@media (max-width: 720px) {
|
||||
.lexeme-analysis-header,
|
||||
.lexeme-analysis-item-top {
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.lexeme-analysis-domain {
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.lexeme-neutralize-btn {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
.bijection-section .bij-options.options-grid {
|
||||
margin-top: 0;
|
||||
margin-bottom: 8px;
|
||||
@@ -5139,4 +5276,3 @@ html {
|
||||
margin-left: var(--spacing-md);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -410,6 +410,7 @@
|
||||
<script src="js/data/endSequences.js"></script>
|
||||
<script src="js/data/openrouterModels.js"></script>
|
||||
<script src="js/data/anticlassifierPrompt.js"></script>
|
||||
<script src="js/data/latinAffixPolicies.js"></script>
|
||||
|
||||
<!-- Load Configuration and Utilities (before modules that depend on them) -->
|
||||
<script src="js/config/constants.js"></script>
|
||||
@@ -426,6 +427,7 @@
|
||||
<script src="js/core/steganography.js"></script>
|
||||
<script src="js/core/transformOptions.js"></script>
|
||||
<script src="js/core/decoder.js"></script>
|
||||
<script src="js/core/lexemeAnalysis.js"></script>
|
||||
|
||||
<!-- Load Tool System -->
|
||||
<script src="js/tools/Tool.js"></script>
|
||||
|
||||
@@ -0,0 +1,180 @@
|
||||
/**
|
||||
* Shared Latin-root lexeme analysis and neutral rewrite generation.
|
||||
*/
|
||||
(function(root) {
|
||||
function uniq(values) {
|
||||
return Array.from(new Set(values.filter(Boolean)));
|
||||
}
|
||||
|
||||
function titleCase(text) {
|
||||
return String(text || '').replace(/\b[a-z]/g, function(match) {
|
||||
return match.toUpperCase();
|
||||
});
|
||||
}
|
||||
|
||||
function preserveCase(source, replacement) {
|
||||
if (!source) {
|
||||
return replacement;
|
||||
}
|
||||
if (source === source.toUpperCase()) {
|
||||
return replacement.toUpperCase();
|
||||
}
|
||||
if (source[0] === source[0].toUpperCase() && source.slice(1) === source.slice(1).toLowerCase()) {
|
||||
return titleCase(replacement);
|
||||
}
|
||||
return replacement;
|
||||
}
|
||||
|
||||
function normalizeRoot(rawRoot, aliases) {
|
||||
const rootText = String(rawRoot || '').toLowerCase();
|
||||
if (!rootText) {
|
||||
return '';
|
||||
}
|
||||
|
||||
if (aliases[rootText]) {
|
||||
return aliases[rootText];
|
||||
}
|
||||
|
||||
const variants = [
|
||||
rootText.endsWith('i') ? rootText.slice(0, -1) : '',
|
||||
rootText.endsWith('ic') ? rootText.slice(0, -2) : '',
|
||||
rootText.endsWith('o') ? rootText.slice(0, -1) : '',
|
||||
rootText.endsWith('al') ? rootText.slice(0, -2) : ''
|
||||
].filter(Boolean);
|
||||
|
||||
for (const variant of variants) {
|
||||
if (aliases[variant]) {
|
||||
return aliases[variant];
|
||||
}
|
||||
}
|
||||
|
||||
return '';
|
||||
}
|
||||
|
||||
function materializeTemplates(templates, domain) {
|
||||
return uniq((templates || []).map(function(template) {
|
||||
return String(template || '').replace(/\{domain\}/g, domain || 'risk');
|
||||
}));
|
||||
}
|
||||
|
||||
function resolveSuggestions(policy, domain) {
|
||||
if (domain) {
|
||||
return materializeTemplates(policy.rewriteTemplates, domain);
|
||||
}
|
||||
return uniq(policy.fallbackTemplates || []);
|
||||
}
|
||||
|
||||
function analyze(text, options) {
|
||||
const input = String(text || '');
|
||||
const policies = (options && options.policies) || root.LATIN_AFFIX_POLICIES || [];
|
||||
const aliases = (options && options.aliases) || root.LATIN_DOMAIN_ALIASES || {};
|
||||
|
||||
if (!input.trim()) {
|
||||
return {
|
||||
sourceText: input,
|
||||
totalFindings: 0,
|
||||
findings: [],
|
||||
families: [],
|
||||
summary: 'No Latin-root wording findings.'
|
||||
};
|
||||
}
|
||||
|
||||
const findings = [];
|
||||
const seen = new Set();
|
||||
|
||||
policies.forEach(function(policy) {
|
||||
(policy.patterns || []).forEach(function(pattern) {
|
||||
let match;
|
||||
while ((match = pattern.exec(input)) !== null) {
|
||||
const term = match[0];
|
||||
const key = policy.id + '::' + term.toLowerCase();
|
||||
if (seen.has(key)) {
|
||||
continue;
|
||||
}
|
||||
seen.add(key);
|
||||
|
||||
const rootCandidate = match[1] || '';
|
||||
const semanticDomain = normalizeRoot(rootCandidate, aliases);
|
||||
const rewrites = resolveSuggestions(policy, semanticDomain);
|
||||
|
||||
findings.push({
|
||||
id: key,
|
||||
term,
|
||||
normalizedTerm: term.toLowerCase(),
|
||||
family: policy.family,
|
||||
policyId: policy.id,
|
||||
affixes: policy.affixes || [],
|
||||
partOfSpeech: policy.partOfSpeech,
|
||||
extractedRoot: rootCandidate || '',
|
||||
semanticDomain: semanticDomain || '',
|
||||
severity: policy.severity,
|
||||
confidence: policy.confidence,
|
||||
semanticShift: policy.semanticShift,
|
||||
rationale: policy.explanation,
|
||||
rewrites,
|
||||
primaryRewrite: rewrites[0] || '',
|
||||
matchIndex: match.index
|
||||
});
|
||||
}
|
||||
pattern.lastIndex = 0;
|
||||
});
|
||||
});
|
||||
|
||||
findings.sort(function(a, b) {
|
||||
return a.matchIndex - b.matchIndex;
|
||||
});
|
||||
|
||||
return {
|
||||
sourceText: input,
|
||||
totalFindings: findings.length,
|
||||
findings,
|
||||
families: uniq(findings.map(function(item) { return item.family; })),
|
||||
summary: findings.length
|
||||
? 'Detected ' + findings.length + ' Latin-root wording pattern' + (findings.length === 1 ? '' : 's') + '.'
|
||||
: 'No Latin-root wording findings.'
|
||||
};
|
||||
}
|
||||
|
||||
function neutralizeText(text, analysis) {
|
||||
const input = String(text || '');
|
||||
if (!analysis || !Array.isArray(analysis.findings) || !analysis.findings.length) {
|
||||
return input;
|
||||
}
|
||||
|
||||
let output = input;
|
||||
analysis.findings.forEach(function(finding) {
|
||||
if (!finding.primaryRewrite) {
|
||||
return;
|
||||
}
|
||||
const matcher = new RegExp('\\b' + escapeRegExp(finding.term) + '\\b', 'gi');
|
||||
output = output.replace(matcher, function(match) {
|
||||
return preserveCase(match, finding.primaryRewrite);
|
||||
});
|
||||
});
|
||||
return output;
|
||||
}
|
||||
|
||||
function escapeRegExp(value) {
|
||||
return String(value || '').replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
||||
}
|
||||
|
||||
const api = {
|
||||
analyze,
|
||||
neutralizeText
|
||||
};
|
||||
|
||||
if (typeof module !== 'undefined' && module.exports) {
|
||||
const data = require('../data/latinAffixPolicies.js');
|
||||
module.exports = {
|
||||
analyze: function(text) {
|
||||
return analyze(text, {
|
||||
policies: data.POLICIES,
|
||||
aliases: data.DOMAIN_ALIASES
|
||||
});
|
||||
},
|
||||
neutralizeText
|
||||
};
|
||||
} else {
|
||||
root.LexemeAnalysis = api;
|
||||
}
|
||||
})(typeof window !== 'undefined' ? window : globalThis);
|
||||
@@ -0,0 +1,152 @@
|
||||
/**
|
||||
* Declarative Latin-root prompt wording policies.
|
||||
* Families define how loaded affixes should be interpreted and rewritten.
|
||||
*/
|
||||
(function(root) {
|
||||
const DOMAIN_ALIASES = {
|
||||
bacter: 'bacterial',
|
||||
bacteri: 'bacterial',
|
||||
bio: 'biological',
|
||||
bi: 'biological',
|
||||
fung: 'fungal',
|
||||
fungi: 'fungal',
|
||||
germ: 'microbial',
|
||||
herb: 'weed',
|
||||
homic: 'violence',
|
||||
human: 'human',
|
||||
insect: 'pest',
|
||||
larv: 'larval',
|
||||
pestic: 'pest',
|
||||
pesti: 'pest',
|
||||
rodent: 'rodent',
|
||||
suic: 'self-harm',
|
||||
viru: 'viral',
|
||||
virus: 'viral'
|
||||
};
|
||||
|
||||
const POLICIES = [
|
||||
{
|
||||
id: 'latin-destructive-noun',
|
||||
family: 'destructive_suffix',
|
||||
kind: 'suffix',
|
||||
partOfSpeech: 'noun',
|
||||
affixes: ['cide'],
|
||||
patterns: [/\b([a-z]+?)cide(s)?\b/gi],
|
||||
severity: 'high',
|
||||
confidence: 0.91,
|
||||
semanticShift: 'lethal_or_destructive',
|
||||
explanation: 'The Latin-derived -cide ending tends to foreground killing or eradication.',
|
||||
rewriteTemplates: [
|
||||
'{domain} management',
|
||||
'{domain} mitigation',
|
||||
'{domain} control',
|
||||
'{domain} stewardship'
|
||||
],
|
||||
fallbackTemplates: [
|
||||
'mitigation',
|
||||
'management',
|
||||
'deterrence',
|
||||
'stewardship'
|
||||
]
|
||||
},
|
||||
{
|
||||
id: 'latin-destructive-adjective',
|
||||
family: 'destructive_suffix',
|
||||
kind: 'suffix',
|
||||
partOfSpeech: 'adjective',
|
||||
affixes: ['cidal'],
|
||||
patterns: [/\b([a-z]+?)cidal(ly)?\b/gi, /\bcidal(ly)?\b/gi],
|
||||
severity: 'medium',
|
||||
confidence: 0.84,
|
||||
semanticShift: 'destructive_operational_framing',
|
||||
explanation: 'The Latin-derived -cidal ending can make otherwise neutral activity sound destructive.',
|
||||
rewriteTemplates: [
|
||||
'focused on {domain} management',
|
||||
'used for {domain} mitigation',
|
||||
'intended for {domain} control'
|
||||
],
|
||||
fallbackTemplates: [
|
||||
'framed around mitigation',
|
||||
'framed around management',
|
||||
'operationally neutral'
|
||||
]
|
||||
},
|
||||
{
|
||||
id: 'latin-eradication-verbs',
|
||||
family: 'eradication_language',
|
||||
kind: 'surface_pattern',
|
||||
partOfSpeech: 'verb',
|
||||
affixes: ['eradicate', 'exterminate', 'annihilate', 'obliterate'],
|
||||
patterns: [
|
||||
/\b(?:eradicate|eradicates|eradicated|eradicating|eradication)\b/gi,
|
||||
/\b(?:exterminate|exterminates|exterminated|exterminating|extermination)\b/gi,
|
||||
/\b(?:annihilate|annihilates|annihilated|annihilating|annihilation)\b/gi,
|
||||
/\b(?:obliterate|obliterates|obliterated|obliterating|obliteration)\b/gi
|
||||
],
|
||||
severity: 'high',
|
||||
confidence: 0.88,
|
||||
semanticShift: 'total_removal',
|
||||
explanation: 'Eradication language implies total destruction rather than bounded handling.',
|
||||
fallbackTemplates: [
|
||||
'reduce',
|
||||
'manage',
|
||||
'contain',
|
||||
'address'
|
||||
]
|
||||
},
|
||||
{
|
||||
id: 'latin-suppression-language',
|
||||
family: 'suppression_language',
|
||||
kind: 'surface_pattern',
|
||||
partOfSpeech: 'verb',
|
||||
affixes: ['neutralize', 'incapacitate', 'suppress'],
|
||||
patterns: [
|
||||
/\b(?:neutralize|neutralizes|neutralized|neutralizing|neutralization)\b/gi,
|
||||
/\b(?:incapacitate|incapacitates|incapacitated|incapacitating|incapacitation)\b/gi,
|
||||
/\b(?:suppress|suppresses|suppressed|suppressing|suppression)\b/gi
|
||||
],
|
||||
severity: 'medium',
|
||||
confidence: 0.82,
|
||||
semanticShift: 'forceful_control',
|
||||
explanation: 'Suppression language can read as coercive or force-centric rather than operationally neutral.',
|
||||
fallbackTemplates: [
|
||||
'limit',
|
||||
'reduce',
|
||||
'constrain',
|
||||
'manage'
|
||||
]
|
||||
},
|
||||
{
|
||||
id: 'latin-terminality-language',
|
||||
family: 'terminality_language',
|
||||
kind: 'surface_pattern',
|
||||
partOfSpeech: 'adjective',
|
||||
affixes: ['lethal', 'fatal', 'terminal', 'mortal'],
|
||||
patterns: [
|
||||
/\b(?:lethal|fatal|terminal|mortal)\b/gi
|
||||
],
|
||||
severity: 'medium',
|
||||
confidence: 0.8,
|
||||
semanticShift: 'fatal_outcome_framing',
|
||||
explanation: 'Terminality language frames outcomes as fatal or irreversible even when a softer operational phrasing would do.',
|
||||
fallbackTemplates: [
|
||||
'high-risk',
|
||||
'severe',
|
||||
'critical',
|
||||
'unsafe'
|
||||
]
|
||||
}
|
||||
];
|
||||
|
||||
const api = {
|
||||
DOMAIN_ALIASES,
|
||||
POLICIES
|
||||
};
|
||||
|
||||
if (typeof module !== 'undefined' && module.exports) {
|
||||
module.exports = api;
|
||||
} else {
|
||||
root.LATIN_AFFIX_POLICIES = POLICIES;
|
||||
root.LATIN_DOMAIN_ALIASES = DOMAIN_ALIASES;
|
||||
}
|
||||
})(typeof window !== 'undefined' ? window : globalThis);
|
||||
@@ -24,6 +24,7 @@ class AntiClassifierTool extends Tool {
|
||||
acInput: '',
|
||||
acOutput: '',
|
||||
acError: '',
|
||||
acLexemeAnalysis: { totalFindings: 0, findings: [], summary: 'No Latin-root wording findings.' },
|
||||
acLoading: false,
|
||||
acModel: localStorage.getItem('ac-model') || 'anthropic/claude-sonnet-4.6',
|
||||
acModels: models,
|
||||
@@ -49,6 +50,13 @@ class AntiClassifierTool extends Tool {
|
||||
? window.ANTICLASSIFIER_SYSTEM_PROMPT
|
||||
: '';
|
||||
},
|
||||
acRefreshLexemeAnalysis: function() {
|
||||
if (typeof window === 'undefined' || !window.LexemeAnalysis || typeof window.LexemeAnalysis.analyze !== 'function') {
|
||||
this.acLexemeAnalysis = { totalFindings: 0, findings: [], summary: 'Lexeme analysis unavailable.' };
|
||||
return;
|
||||
}
|
||||
this.acLexemeAnalysis = window.LexemeAnalysis.analyze(this.acInput);
|
||||
},
|
||||
acRun: async function() {
|
||||
const apiKey = this.acGetApiKey();
|
||||
if (!apiKey) {
|
||||
@@ -123,6 +131,39 @@ class AntiClassifierTool extends Tool {
|
||||
if (this.acOutput) {
|
||||
this.copyToClipboard(this.acOutput);
|
||||
}
|
||||
},
|
||||
acGetLexemeAnalysis: function() {
|
||||
return this.acLexemeAnalysis || { totalFindings: 0, findings: [], summary: 'No Latin-root wording findings.' };
|
||||
},
|
||||
acNeutralizeInput: function() {
|
||||
const analysis = this.acGetLexemeAnalysis();
|
||||
if (!analysis.totalFindings || !window.LexemeAnalysis || typeof window.LexemeAnalysis.neutralizeText !== 'function') {
|
||||
return;
|
||||
}
|
||||
this.acInput = window.LexemeAnalysis.neutralizeText(this.acInput, analysis);
|
||||
if (typeof this.showNotification === 'function') {
|
||||
this.showNotification('Applied neutral Latin-root rewrites', 'success', 'fas fa-seedling');
|
||||
}
|
||||
},
|
||||
acApplyLexemeRewrite: function(term, rewrite) {
|
||||
if (!term || !rewrite) {
|
||||
return;
|
||||
}
|
||||
const escapedTerm = String(term).replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
||||
this.acInput = this.acInput.replace(new RegExp('\\b' + escapedTerm + '\\b', 'i'), rewrite);
|
||||
if (typeof this.showNotification === 'function') {
|
||||
this.showNotification('Applied rewrite for ' + term, 'success', 'fas fa-pen');
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
getVueWatchers() {
|
||||
return {
|
||||
acInput: function() {
|
||||
if (typeof this.acRefreshLexemeAnalysis === 'function') {
|
||||
this.acRefreshLexemeAnalysis();
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -20,6 +20,7 @@ class BijectionTool extends Tool {
|
||||
bijectionIncludeExamples: true,
|
||||
bijectionAutoCopy: false,
|
||||
bijectionInput: '',
|
||||
bijectionLexemeAnalysis: { totalFindings: 0, findings: [], summary: 'No Latin-root wording findings.' },
|
||||
bijectionMapping: {},
|
||||
bijectionOutputs: []
|
||||
};
|
||||
@@ -27,6 +28,36 @@ class BijectionTool extends Tool {
|
||||
|
||||
getVueMethods() {
|
||||
return {
|
||||
bijectionRefreshLexemeAnalysis() {
|
||||
if (typeof window === 'undefined' || !window.LexemeAnalysis || typeof window.LexemeAnalysis.analyze !== 'function') {
|
||||
this.bijectionLexemeAnalysis = { totalFindings: 0, findings: [], summary: 'Lexeme analysis unavailable.' };
|
||||
return;
|
||||
}
|
||||
this.bijectionLexemeAnalysis = window.LexemeAnalysis.analyze(this.bijectionInput);
|
||||
},
|
||||
bijectionGetLexemeAnalysis() {
|
||||
return this.bijectionLexemeAnalysis || { totalFindings: 0, findings: [], summary: 'No Latin-root wording findings.' };
|
||||
},
|
||||
bijectionNeutralizeInput() {
|
||||
const analysis = this.bijectionGetLexemeAnalysis();
|
||||
if (!analysis.totalFindings || !window.LexemeAnalysis || typeof window.LexemeAnalysis.neutralizeText !== 'function') {
|
||||
return;
|
||||
}
|
||||
this.bijectionInput = window.LexemeAnalysis.neutralizeText(this.bijectionInput, analysis);
|
||||
if (typeof this.showNotification === 'function') {
|
||||
this.showNotification('Applied neutral Latin-root rewrites', 'success', 'fas fa-seedling');
|
||||
}
|
||||
},
|
||||
bijectionApplyLexemeRewrite(term, rewrite) {
|
||||
if (!term || !rewrite) {
|
||||
return;
|
||||
}
|
||||
const escapedTerm = String(term).replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
||||
this.bijectionInput = this.bijectionInput.replace(new RegExp('\\b' + escapedTerm + '\\b', 'i'), rewrite);
|
||||
if (typeof this.showNotification === 'function') {
|
||||
this.showNotification('Applied rewrite for ' + term, 'success', 'fas fa-pen');
|
||||
}
|
||||
},
|
||||
generateBijectionMapping() {
|
||||
const chars = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789.,!?';
|
||||
const mapping = {};
|
||||
@@ -229,6 +260,16 @@ Now, please translate and respond to this message in alphapr: ${encoded}`;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
getVueWatchers() {
|
||||
return {
|
||||
bijectionInput: function() {
|
||||
if (typeof this.bijectionRefreshLexemeAnalysis === 'function') {
|
||||
this.bijectionRefreshLexemeAnalysis();
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
if (typeof module !== 'undefined' && module.exports) {
|
||||
|
||||
@@ -15,6 +15,7 @@ class MutationTool extends Tool {
|
||||
getVueData() {
|
||||
return {
|
||||
fuzzerInput: '',
|
||||
fuzzerLexemeAnalysis: { totalFindings: 0, findings: [], summary: 'No Latin-root wording findings.' },
|
||||
fuzzerCount: 20,
|
||||
fuzzerSeed: '',
|
||||
fuzzUseRandomMix: true,
|
||||
@@ -30,6 +31,36 @@ class MutationTool extends Tool {
|
||||
|
||||
getVueMethods() {
|
||||
return {
|
||||
fuzzerRefreshLexemeAnalysis: function() {
|
||||
if (typeof window === 'undefined' || !window.LexemeAnalysis || typeof window.LexemeAnalysis.analyze !== 'function') {
|
||||
this.fuzzerLexemeAnalysis = { totalFindings: 0, findings: [], summary: 'Lexeme analysis unavailable.' };
|
||||
return;
|
||||
}
|
||||
this.fuzzerLexemeAnalysis = window.LexemeAnalysis.analyze(this.fuzzerInput);
|
||||
},
|
||||
fuzzerGetLexemeAnalysis: function() {
|
||||
return this.fuzzerLexemeAnalysis || { totalFindings: 0, findings: [], summary: 'No Latin-root wording findings.' };
|
||||
},
|
||||
fuzzerNeutralizeInput: function() {
|
||||
const analysis = this.fuzzerGetLexemeAnalysis();
|
||||
if (!analysis.totalFindings || !window.LexemeAnalysis || typeof window.LexemeAnalysis.neutralizeText !== 'function') {
|
||||
return;
|
||||
}
|
||||
this.fuzzerInput = window.LexemeAnalysis.neutralizeText(this.fuzzerInput, analysis);
|
||||
if (typeof this.showNotification === 'function') {
|
||||
this.showNotification('Applied neutral Latin-root rewrites', 'success', 'fas fa-seedling');
|
||||
}
|
||||
},
|
||||
fuzzerApplyLexemeRewrite: function(term, rewrite) {
|
||||
if (!term || !rewrite) {
|
||||
return;
|
||||
}
|
||||
const escapedTerm = String(term).replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
||||
this.fuzzerInput = this.fuzzerInput.replace(new RegExp('\\b' + escapedTerm + '\\b', 'i'), rewrite);
|
||||
if (typeof this.showNotification === 'function') {
|
||||
this.showNotification('Applied rewrite for ' + term, 'success', 'fas fa-pen');
|
||||
}
|
||||
},
|
||||
seededRandomFactory: function(seedStr) {
|
||||
if (!seedStr) return Math.random;
|
||||
let h = 1779033703 ^ seedStr.length;
|
||||
@@ -106,6 +137,16 @@ class MutationTool extends Tool {
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
getVueWatchers() {
|
||||
return {
|
||||
fuzzerInput: function() {
|
||||
if (typeof this.fuzzerRefreshLexemeAnalysis === 'function') {
|
||||
this.fuzzerRefreshLexemeAnalysis();
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
// Export
|
||||
@@ -116,4 +157,3 @@ if (typeof module !== 'undefined' && module.exports) {
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -21,6 +21,7 @@ class PromptCraftTool extends Tool {
|
||||
pcInput: '',
|
||||
pcOutput: '',
|
||||
pcOutputs: [],
|
||||
pcLexemeAnalysis: { totalFindings: 0, findings: [], summary: 'No Latin-root wording findings.' },
|
||||
pcStrategy: 'rephrase',
|
||||
pcModel: localStorage.getItem('pc-model') || 'nousresearch/hermes-3-llama-3.1-405b',
|
||||
pcTemperature,
|
||||
@@ -78,6 +79,13 @@ class PromptCraftTool extends Tool {
|
||||
}
|
||||
return base;
|
||||
},
|
||||
pcRefreshLexemeAnalysis: function() {
|
||||
if (typeof window === 'undefined' || !window.LexemeAnalysis || typeof window.LexemeAnalysis.analyze !== 'function') {
|
||||
this.pcLexemeAnalysis = { totalFindings: 0, findings: [], summary: 'Lexeme analysis unavailable.' };
|
||||
return;
|
||||
}
|
||||
this.pcLexemeAnalysis = window.LexemeAnalysis.analyze(this.pcInput);
|
||||
},
|
||||
pcRunMutation: async function() {
|
||||
const apiKey = this.pcGetApiKey();
|
||||
if (!apiKey) {
|
||||
@@ -163,6 +171,39 @@ class PromptCraftTool extends Tool {
|
||||
},
|
||||
pcUseAsInput: function(text) {
|
||||
this.pcInput = text;
|
||||
},
|
||||
pcGetLexemeAnalysis: function() {
|
||||
return this.pcLexemeAnalysis || { totalFindings: 0, findings: [], summary: 'No Latin-root wording findings.' };
|
||||
},
|
||||
pcNeutralizeInput: function() {
|
||||
const analysis = this.pcGetLexemeAnalysis();
|
||||
if (!analysis.totalFindings || !window.LexemeAnalysis || typeof window.LexemeAnalysis.neutralizeText !== 'function') {
|
||||
return;
|
||||
}
|
||||
this.pcInput = window.LexemeAnalysis.neutralizeText(this.pcInput, analysis);
|
||||
if (typeof this.showNotification === 'function') {
|
||||
this.showNotification('Applied neutral Latin-root rewrites', 'success', 'fas fa-seedling');
|
||||
}
|
||||
},
|
||||
pcApplyLexemeRewrite: function(term, rewrite) {
|
||||
if (!term || !rewrite) {
|
||||
return;
|
||||
}
|
||||
const escapedTerm = String(term).replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
||||
this.pcInput = this.pcInput.replace(new RegExp('\\b' + escapedTerm + '\\b', 'i'), rewrite);
|
||||
if (typeof this.showNotification === 'function') {
|
||||
this.showNotification('Applied rewrite for ' + term, 'success', 'fas fa-pen');
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
getVueWatchers() {
|
||||
return {
|
||||
pcInput: function() {
|
||||
if (typeof this.pcRefreshLexemeAnalysis === 'function') {
|
||||
this.pcRefreshLexemeAnalysis();
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -61,6 +61,7 @@ class TransformTool extends Tool {
|
||||
|
||||
return {
|
||||
transformInput: 'Hello World',
|
||||
transformLexemeAnalysis: { totalFindings: 0, findings: [], summary: 'No Latin-root wording findings.' },
|
||||
transformOutput: '',
|
||||
activeTransform: null,
|
||||
transforms: transforms,
|
||||
@@ -250,6 +251,36 @@ class TransformTool extends Tool {
|
||||
}
|
||||
return 'text';
|
||||
},
|
||||
transformRefreshLexemeAnalysis: function() {
|
||||
if (typeof window === 'undefined' || !window.LexemeAnalysis || typeof window.LexemeAnalysis.analyze !== 'function') {
|
||||
this.transformLexemeAnalysis = { totalFindings: 0, findings: [], summary: 'Lexeme analysis unavailable.' };
|
||||
return;
|
||||
}
|
||||
this.transformLexemeAnalysis = window.LexemeAnalysis.analyze(this.transformInput);
|
||||
},
|
||||
transformGetLexemeAnalysis: function() {
|
||||
return this.transformLexemeAnalysis || { totalFindings: 0, findings: [], summary: 'No Latin-root wording findings.' };
|
||||
},
|
||||
transformNeutralizeInput: function() {
|
||||
const analysis = this.transformGetLexemeAnalysis();
|
||||
if (!analysis.totalFindings || !window.LexemeAnalysis || typeof window.LexemeAnalysis.neutralizeText !== 'function') {
|
||||
return;
|
||||
}
|
||||
this.transformInput = window.LexemeAnalysis.neutralizeText(this.transformInput, analysis);
|
||||
if (typeof this.showNotification === 'function') {
|
||||
this.showNotification('Applied neutral Latin-root rewrites', 'success', 'fas fa-seedling');
|
||||
}
|
||||
},
|
||||
transformApplyLexemeRewrite: function(term, rewrite) {
|
||||
if (!term || !rewrite) {
|
||||
return;
|
||||
}
|
||||
const escapedTerm = String(term).replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
||||
this.transformInput = this.transformInput.replace(new RegExp('\\b' + escapedTerm + '\\b', 'i'), rewrite);
|
||||
if (typeof this.showNotification === 'function') {
|
||||
this.showNotification('Applied rewrite for ' + term, 'success', 'fas fa-pen');
|
||||
}
|
||||
},
|
||||
openTransformOptions: function(transform, event) {
|
||||
if (event) {
|
||||
event.preventDefault();
|
||||
@@ -623,6 +654,9 @@ class TransformTool extends Tool {
|
||||
getVueWatchers() {
|
||||
return {
|
||||
transformInput() {
|
||||
if (typeof this.transformRefreshLexemeAnalysis === 'function') {
|
||||
this.transformRefreshLexemeAnalysis();
|
||||
}
|
||||
if (this.activeTransform && this.activeTab === 'transforms') {
|
||||
const opts = this.getMergedOptionsForTransform(this.activeTransform.name);
|
||||
this.transformOutput = this.activeTransform.func(this.transformInput, opts);
|
||||
@@ -670,4 +704,3 @@ if (typeof module !== 'undefined' && module.exports) {
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
+3
-1
@@ -13,9 +13,11 @@
|
||||
"start": "serve dist -l 8080",
|
||||
"preview": "npm run build && serve dist -l 8080",
|
||||
"test": "node tests/test_universal.js",
|
||||
"test:lexeme": "node tests/test_lexeme_analysis.js",
|
||||
"test:lexeme-ui": "node tests/test_lexeme_ui_surface.js",
|
||||
"test:universal": "node tests/test_universal.js",
|
||||
"test:steg": "node tests/test_steganography_options.js",
|
||||
"test:all": "npm run test:universal && npm run test:steg",
|
||||
"test:all": "npm run test:universal && npm run test:steg && npm run test:lexeme && npm run test:lexeme-ui",
|
||||
"precommit": "npm run test:all"
|
||||
},
|
||||
"repository": {
|
||||
|
||||
@@ -13,6 +13,48 @@
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div v-if="acGetLexemeAnalysis().totalFindings" class="lexeme-analysis-card">
|
||||
<div class="lexeme-analysis-header">
|
||||
<div>
|
||||
<div class="lexeme-analysis-kicker">Latin-Root Analysis</div>
|
||||
<h4>{{ acGetLexemeAnalysis().summary }}</h4>
|
||||
<p>This input contains affix patterns that skew toward destructive or lethal framing. Use the generated rewrites to neutralize tone before transformation.</p>
|
||||
</div>
|
||||
<button type="button" class="action-button copy lexeme-neutralize-btn" @click="acNeutralizeInput">
|
||||
<i class="fas fa-seedling"></i> Neutralize flagged terms
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="lexeme-analysis-list">
|
||||
<div v-for="finding in acGetLexemeAnalysis().findings" :key="finding.id" class="lexeme-analysis-item">
|
||||
<div class="lexeme-analysis-item-top">
|
||||
<div class="lexeme-analysis-term-group">
|
||||
<code class="lexeme-term">{{ finding.term }}</code>
|
||||
<span class="lexeme-chip">{{ finding.partOfSpeech }}</span>
|
||||
<span class="lexeme-chip">{{ finding.affixes.join(', ') }}</span>
|
||||
<span class="lexeme-chip">confidence {{ Math.round(finding.confidence * 100) }}%</span>
|
||||
</div>
|
||||
<div class="lexeme-analysis-domain">
|
||||
<span v-if="finding.semanticDomain"><strong>Domain:</strong> {{ finding.semanticDomain }}</span>
|
||||
<span v-else><strong>Domain:</strong> unresolved root</span>
|
||||
</div>
|
||||
</div>
|
||||
<p class="lexeme-analysis-rationale">{{ finding.rationale }}</p>
|
||||
<div class="lexeme-analysis-rewrites">
|
||||
<button
|
||||
v-for="rewrite in finding.rewrites"
|
||||
:key="finding.id + '-' + rewrite"
|
||||
type="button"
|
||||
class="lexeme-rewrite-btn"
|
||||
@click="acApplyLexemeRewrite(finding.term, rewrite)"
|
||||
>
|
||||
{{ rewrite }}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="options-grid ac-options">
|
||||
<label>
|
||||
Model
|
||||
|
||||
@@ -16,6 +16,41 @@
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div v-if="bijectionGetLexemeAnalysis().totalFindings" class="lexeme-analysis-card">
|
||||
<div class="lexeme-analysis-header">
|
||||
<div>
|
||||
<div class="lexeme-analysis-kicker">Latin-Root Analysis</div>
|
||||
<h4>{{ bijectionGetLexemeAnalysis().summary }}</h4>
|
||||
<p>This target content contains loaded Latin-root wording. Neutralizing it here propagates through the generated alphapr prompts.</p>
|
||||
</div>
|
||||
<button type="button" class="action-button copy lexeme-neutralize-btn" @click="bijectionNeutralizeInput">
|
||||
<i class="fas fa-seedling"></i> Neutralize flagged terms
|
||||
</button>
|
||||
</div>
|
||||
<div class="lexeme-analysis-list">
|
||||
<div v-for="finding in bijectionGetLexemeAnalysis().findings" :key="finding.id" class="lexeme-analysis-item">
|
||||
<div class="lexeme-analysis-item-top">
|
||||
<div class="lexeme-analysis-term-group">
|
||||
<code class="lexeme-term">{{ finding.term }}</code>
|
||||
<span class="lexeme-chip">{{ finding.partOfSpeech }}</span>
|
||||
<span class="lexeme-chip">{{ finding.family }}</span>
|
||||
<span class="lexeme-chip">confidence {{ Math.round(finding.confidence * 100) }}%</span>
|
||||
</div>
|
||||
<div class="lexeme-analysis-domain">
|
||||
<span v-if="finding.semanticDomain"><strong>Domain:</strong> {{ finding.semanticDomain }}</span>
|
||||
<span v-else><strong>Domain:</strong> unresolved root</span>
|
||||
</div>
|
||||
</div>
|
||||
<p class="lexeme-analysis-rationale">{{ finding.rationale }}</p>
|
||||
<div class="lexeme-analysis-rewrites">
|
||||
<button v-for="rewrite in finding.rewrites" :key="finding.id + '-' + rewrite" type="button" class="lexeme-rewrite-btn" @click="bijectionApplyLexemeRewrite(finding.term, rewrite)">
|
||||
{{ rewrite }}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="options-grid bij-options">
|
||||
<label>
|
||||
Bijection type
|
||||
|
||||
+35
-1
@@ -10,6 +10,40 @@
|
||||
<textarea v-model="fuzzerInput" placeholder="Enter seed text to fuzz..." rows="3"></textarea>
|
||||
</label>
|
||||
</div>
|
||||
<div v-if="fuzzerGetLexemeAnalysis().totalFindings" class="lexeme-analysis-card">
|
||||
<div class="lexeme-analysis-header">
|
||||
<div>
|
||||
<div class="lexeme-analysis-kicker">Latin-Root Analysis</div>
|
||||
<h4>{{ fuzzerGetLexemeAnalysis().summary }}</h4>
|
||||
<p>This seed text contains loaded Latin-root wording. Neutralizing it here affects every generated mutation case.</p>
|
||||
</div>
|
||||
<button type="button" class="action-button copy lexeme-neutralize-btn" @click="fuzzerNeutralizeInput">
|
||||
<i class="fas fa-seedling"></i> Neutralize flagged terms
|
||||
</button>
|
||||
</div>
|
||||
<div class="lexeme-analysis-list">
|
||||
<div v-for="finding in fuzzerGetLexemeAnalysis().findings" :key="finding.id" class="lexeme-analysis-item">
|
||||
<div class="lexeme-analysis-item-top">
|
||||
<div class="lexeme-analysis-term-group">
|
||||
<code class="lexeme-term">{{ finding.term }}</code>
|
||||
<span class="lexeme-chip">{{ finding.partOfSpeech }}</span>
|
||||
<span class="lexeme-chip">{{ finding.family }}</span>
|
||||
<span class="lexeme-chip">confidence {{ Math.round(finding.confidence * 100) }}%</span>
|
||||
</div>
|
||||
<div class="lexeme-analysis-domain">
|
||||
<span v-if="finding.semanticDomain"><strong>Domain:</strong> {{ finding.semanticDomain }}</span>
|
||||
<span v-else><strong>Domain:</strong> unresolved root</span>
|
||||
</div>
|
||||
</div>
|
||||
<p class="lexeme-analysis-rationale">{{ finding.rationale }}</p>
|
||||
<div class="lexeme-analysis-rewrites">
|
||||
<button v-for="rewrite in finding.rewrites" :key="finding.id + '-' + rewrite" type="button" class="lexeme-rewrite-btn" @click="fuzzerApplyLexemeRewrite(finding.term, rewrite)">
|
||||
{{ rewrite }}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="options-grid">
|
||||
<label>
|
||||
Cases
|
||||
@@ -45,4 +79,4 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -12,6 +12,48 @@
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div v-if="pcGetLexemeAnalysis().totalFindings" class="lexeme-analysis-card">
|
||||
<div class="lexeme-analysis-header">
|
||||
<div>
|
||||
<div class="lexeme-analysis-kicker">Latin-Root Analysis</div>
|
||||
<h4>{{ pcGetLexemeAnalysis().summary }}</h4>
|
||||
<p>This input contains affix patterns that skew toward destructive or lethal framing. Use the generated rewrites to neutralize tone before mutation.</p>
|
||||
</div>
|
||||
<button type="button" class="action-button copy lexeme-neutralize-btn" @click="pcNeutralizeInput">
|
||||
<i class="fas fa-seedling"></i> Neutralize flagged terms
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="lexeme-analysis-list">
|
||||
<div v-for="finding in pcGetLexemeAnalysis().findings" :key="finding.id" class="lexeme-analysis-item">
|
||||
<div class="lexeme-analysis-item-top">
|
||||
<div class="lexeme-analysis-term-group">
|
||||
<code class="lexeme-term">{{ finding.term }}</code>
|
||||
<span class="lexeme-chip">{{ finding.partOfSpeech }}</span>
|
||||
<span class="lexeme-chip">{{ finding.affixes.join(', ') }}</span>
|
||||
<span class="lexeme-chip">confidence {{ Math.round(finding.confidence * 100) }}%</span>
|
||||
</div>
|
||||
<div class="lexeme-analysis-domain">
|
||||
<span v-if="finding.semanticDomain"><strong>Domain:</strong> {{ finding.semanticDomain }}</span>
|
||||
<span v-else><strong>Domain:</strong> unresolved root</span>
|
||||
</div>
|
||||
</div>
|
||||
<p class="lexeme-analysis-rationale">{{ finding.rationale }}</p>
|
||||
<div class="lexeme-analysis-rewrites">
|
||||
<button
|
||||
v-for="rewrite in finding.rewrites"
|
||||
:key="finding.id + '-' + rewrite"
|
||||
type="button"
|
||||
class="lexeme-rewrite-btn"
|
||||
@click="pcApplyLexemeRewrite(finding.term, rewrite)"
|
||||
>
|
||||
{{ rewrite }}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="pc-controls">
|
||||
<div class="pc-strategies">
|
||||
<div class="pc-label" id="pc-strategy-label">Strategy</div>
|
||||
|
||||
@@ -21,6 +21,41 @@
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div v-if="transformGetLexemeAnalysis().totalFindings" class="lexeme-analysis-card transform-lexeme-card">
|
||||
<div class="lexeme-analysis-header">
|
||||
<div>
|
||||
<div class="lexeme-analysis-kicker">Latin-Root Analysis</div>
|
||||
<h4>{{ transformGetLexemeAnalysis().summary }}</h4>
|
||||
<p>This shared input feeds transforms and inline translation. Neutralizing flagged wording here affects both paths.</p>
|
||||
</div>
|
||||
<button type="button" class="action-button copy lexeme-neutralize-btn" @click="transformNeutralizeInput">
|
||||
<i class="fas fa-seedling"></i> Neutralize flagged terms
|
||||
</button>
|
||||
</div>
|
||||
<div class="lexeme-analysis-list">
|
||||
<div v-for="finding in transformGetLexemeAnalysis().findings" :key="finding.id" class="lexeme-analysis-item">
|
||||
<div class="lexeme-analysis-item-top">
|
||||
<div class="lexeme-analysis-term-group">
|
||||
<code class="lexeme-term">{{ finding.term }}</code>
|
||||
<span class="lexeme-chip">{{ finding.partOfSpeech }}</span>
|
||||
<span class="lexeme-chip">{{ finding.family }}</span>
|
||||
<span class="lexeme-chip">confidence {{ Math.round(finding.confidence * 100) }}%</span>
|
||||
</div>
|
||||
<div class="lexeme-analysis-domain">
|
||||
<span v-if="finding.semanticDomain"><strong>Domain:</strong> {{ finding.semanticDomain }}</span>
|
||||
<span v-else><strong>Domain:</strong> unresolved root</span>
|
||||
</div>
|
||||
</div>
|
||||
<p class="lexeme-analysis-rationale">{{ finding.rationale }}</p>
|
||||
<div class="lexeme-analysis-rewrites">
|
||||
<button v-for="rewrite in finding.rewrites" :key="finding.id + '-' + rewrite" type="button" class="lexeme-rewrite-btn" @click="transformApplyLexemeRewrite(finding.term, rewrite)">
|
||||
{{ rewrite }}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="transform-section">
|
||||
<div class="transform-category-legend">
|
||||
<div class="legend-title">Categories:</div>
|
||||
@@ -493,4 +528,4 @@
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -0,0 +1,70 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
const assert = require('assert');
|
||||
const path = require('path');
|
||||
|
||||
const lexemeAnalysis = require(path.join(__dirname, '..', 'js', 'core', 'lexemeAnalysis.js'));
|
||||
|
||||
const empty = lexemeAnalysis.analyze('');
|
||||
assert.strictEqual(empty.totalFindings, 0, 'Empty input should yield no findings');
|
||||
|
||||
const nounCase = lexemeAnalysis.analyze('Need benign pesticide framing.');
|
||||
assert.strictEqual(nounCase.totalFindings, 1, 'Known -cide noun should be detected');
|
||||
assert.strictEqual(nounCase.findings[0].family, 'destructive_suffix');
|
||||
assert.strictEqual(nounCase.findings[0].semanticDomain, 'pest');
|
||||
assert.strictEqual(nounCase.findings[0].primaryRewrite, 'pest management');
|
||||
|
||||
const adjectiveCase = lexemeAnalysis.analyze('Looking for non-cidal wording in this prompt.');
|
||||
assert.strictEqual(adjectiveCase.totalFindings, 1, 'Standalone -cidal wording should be detected');
|
||||
assert.strictEqual(adjectiveCase.findings[0].partOfSpeech, 'adjective');
|
||||
assert.ok(
|
||||
adjectiveCase.findings[0].primaryRewrite.includes('mitigation'),
|
||||
'Standalone adjectival form should fall back to mitigation-oriented rewrite'
|
||||
);
|
||||
|
||||
const aliasCase = lexemeAnalysis.analyze('A bactericidal workflow should sound less destructive.');
|
||||
assert.strictEqual(aliasCase.totalFindings, 1, 'Alias-root adjectival case should be detected');
|
||||
assert.strictEqual(aliasCase.findings[0].semanticDomain, 'bacterial');
|
||||
assert.ok(
|
||||
aliasCase.findings[0].rewrites.some((rewrite) => rewrite.includes('bacterial management')),
|
||||
'Alias roots should resolve into domain-specific rewrites'
|
||||
);
|
||||
|
||||
const eradicationCase = lexemeAnalysis.analyze('We should eradicate this issue before release.');
|
||||
assert.strictEqual(eradicationCase.totalFindings, 1, 'Eradication language should be detected');
|
||||
assert.strictEqual(eradicationCase.findings[0].family, 'eradication_language');
|
||||
assert.ok(
|
||||
eradicationCase.findings[0].rewrites.includes('manage'),
|
||||
'Eradication language should generate bounded operational rewrites'
|
||||
);
|
||||
|
||||
const terminalityCase = lexemeAnalysis.analyze('This creates a lethal failure mode.');
|
||||
assert.strictEqual(terminalityCase.totalFindings, 1, 'Terminality language should be detected');
|
||||
assert.strictEqual(terminalityCase.findings[0].family, 'terminality_language');
|
||||
assert.ok(
|
||||
terminalityCase.findings[0].rewrites.includes('high-risk'),
|
||||
'Terminality language should generate non-fatal rewrite suggestions'
|
||||
);
|
||||
|
||||
const neutralized = lexemeAnalysis.neutralizeText(
|
||||
'Pesticide and bactericidal wording should be softened.',
|
||||
lexemeAnalysis.analyze('Pesticide and bactericidal wording should be softened.')
|
||||
);
|
||||
assert.ok(neutralized.includes('Pest Management'), 'Neutralization should preserve leading capitalization');
|
||||
assert.ok(neutralized.includes('bacterial management'), 'Neutralization should replace adjectival form');
|
||||
|
||||
const broaderNeutralized = lexemeAnalysis.neutralizeText(
|
||||
'We should eradicate lethal language.',
|
||||
lexemeAnalysis.analyze('We should eradicate lethal language.')
|
||||
);
|
||||
assert.ok(
|
||||
broaderNeutralized.includes('reduce') ||
|
||||
broaderNeutralized.includes('manage') ||
|
||||
broaderNeutralized.includes('contain') ||
|
||||
broaderNeutralized.includes('address'),
|
||||
'Broader policy families should neutralize loaded verbs'
|
||||
);
|
||||
assert.ok(!broaderNeutralized.includes('eradicate'), 'Loaded verb should be removed from neutralized output');
|
||||
assert.ok(broaderNeutralized.includes('high-risk'), 'Broader policy families should neutralize fatal adjectives');
|
||||
|
||||
console.log('Lexeme analysis tests passed');
|
||||
@@ -0,0 +1,38 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
const assert = require('assert');
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
|
||||
const projectRoot = path.join(__dirname, '..');
|
||||
const promptcraftTemplate = fs.readFileSync(path.join(projectRoot, 'templates', 'promptcraft.html'), 'utf8');
|
||||
const anticlassifierTemplate = fs.readFileSync(path.join(projectRoot, 'templates', 'anticlassifier.html'), 'utf8');
|
||||
const bijectionTemplate = fs.readFileSync(path.join(projectRoot, 'templates', 'bijection.html'), 'utf8');
|
||||
const fuzzerTemplate = fs.readFileSync(path.join(projectRoot, 'templates', 'fuzzer.html'), 'utf8');
|
||||
const transformsTemplate = fs.readFileSync(path.join(projectRoot, 'templates', 'transforms.html'), 'utf8');
|
||||
const promptcraftTool = fs.readFileSync(path.join(projectRoot, 'js', 'tools', 'PromptCraftTool.js'), 'utf8');
|
||||
const anticlassifierTool = fs.readFileSync(path.join(projectRoot, 'js', 'tools', 'AntiClassifierTool.js'), 'utf8');
|
||||
const bijectionTool = fs.readFileSync(path.join(projectRoot, 'js', 'tools', 'BijectionTool.js'), 'utf8');
|
||||
const mutationTool = fs.readFileSync(path.join(projectRoot, 'js', 'tools', 'MutationTool.js'), 'utf8');
|
||||
const transformTool = fs.readFileSync(path.join(projectRoot, 'js', 'tools', 'TransformTool.js'), 'utf8');
|
||||
const indexTemplate = fs.readFileSync(path.join(projectRoot, 'index.template.html'), 'utf8');
|
||||
|
||||
assert.ok(promptcraftTemplate.includes('Latin-Root Analysis'), 'PromptCraft should expose the analysis card');
|
||||
assert.ok(promptcraftTemplate.includes('pcNeutralizeInput'), 'PromptCraft should expose neutralization action');
|
||||
assert.ok(anticlassifierTemplate.includes('Latin-Root Analysis'), 'Anti-Classifier should expose the analysis card');
|
||||
assert.ok(anticlassifierTemplate.includes('acNeutralizeInput'), 'Anti-Classifier should expose neutralization action');
|
||||
assert.ok(bijectionTemplate.includes('Latin-Root Analysis'), 'Bijection should expose the analysis card');
|
||||
assert.ok(bijectionTemplate.includes('bijectionNeutralizeInput'), 'Bijection should expose neutralization action');
|
||||
assert.ok(fuzzerTemplate.includes('Latin-Root Analysis'), 'Mutation Lab should expose the analysis card');
|
||||
assert.ok(fuzzerTemplate.includes('fuzzerNeutralizeInput'), 'Mutation Lab should expose neutralization action');
|
||||
assert.ok(transformsTemplate.includes('Latin-Root Analysis'), 'Transforms should expose the analysis card');
|
||||
assert.ok(transformsTemplate.includes('transformNeutralizeInput'), 'Transforms should expose neutralization action');
|
||||
assert.ok(promptcraftTool.includes('pcGetLexemeAnalysis'), 'PromptCraft tool should bind shared analysis');
|
||||
assert.ok(anticlassifierTool.includes('acGetLexemeAnalysis'), 'Anti-Classifier tool should bind shared analysis');
|
||||
assert.ok(bijectionTool.includes('bijectionGetLexemeAnalysis'), 'Bijection tool should bind shared analysis');
|
||||
assert.ok(mutationTool.includes('fuzzerGetLexemeAnalysis'), 'Mutation tool should bind shared analysis');
|
||||
assert.ok(transformTool.includes('transformGetLexemeAnalysis'), 'Transform tool should bind shared analysis');
|
||||
assert.ok(indexTemplate.includes('js/data/latinAffixPolicies.js'), 'Index template should load affix policy data');
|
||||
assert.ok(indexTemplate.includes('js/core/lexemeAnalysis.js'), 'Index template should load shared lexeme analysis engine');
|
||||
|
||||
console.log('Lexeme UI surface tests passed');
|
||||
Reference in New Issue
Block a user