nothumanallowed 14.1.26 → 14.1.28

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "nothumanallowed",
3
- "version": "14.1.26",
3
+ "version": "14.1.28",
4
4
  "description": "NotHumanAllowed — 38 AI agents, 80 tools, Studio (visual agentic workflows). Email, calendar, browser automation, screen capture, canvas, cron/heartbeat, Alexandria E2E messaging, GitHub, Notion, Slack, voice chat, free AI (Liara), 28 languages. Zero-dependency CLI.",
5
5
  "type": "module",
6
6
  "bin": {
package/src/constants.mjs CHANGED
@@ -5,7 +5,7 @@ import { fileURLToPath } from 'url';
5
5
  const __filename = fileURLToPath(import.meta.url);
6
6
  const __dirname = path.dirname(__filename);
7
7
 
8
- export const VERSION = '14.1.26';
8
+ export const VERSION = '14.1.28';
9
9
  export const BASE_URL = 'https://nothumanallowed.com/cli';
10
10
  export const API_BASE = 'https://nothumanallowed.com/api/v1';
11
11
 
@@ -5,6 +5,63 @@
5
5
  * Supports: Anthropic, OpenAI, Gemini, DeepSeek, Grok, Mistral, Cohere.
6
6
  */
7
7
 
8
+ // ── Qwen3 BPE artifact repair ─────────────────────────────────────────────
9
+ //
10
+ // Qwen3-32B running on vLLM produces text where Italian/English function words
11
+ // (articles, prepositions, conjunctions) are fused to adjacent content words
12
+ // due to BPE tokenizer subword merging. Examples:
13
+ // "lacorrelazione" → "la correlazione"
14
+ // "ilprezzodell'oro" → "il prezzo dell'oro"
15
+ // "deidati" → "dei dati"
16
+ // "nonesiste" → "non esiste"
17
+ //
18
+ // Strategy: insert a space before any known function word that immediately
19
+ // follows a lowercase letter (i.e. is fused mid-word). We run multiple
20
+ // passes because chains like "dellacorrelazione" need two separations.
21
+ // Table rows (lines starting with |) are intentionally skipped to avoid
22
+ // corrupting cell separators.
23
+
24
+ const IT_FUNCTION_WORDS = [
25
+ // Definite articles
26
+ 'il','lo','la','i','gli','le',
27
+ // Indefinite articles
28
+ 'un','uno','una',
29
+ // Prepositions + contractions
30
+ 'di','del','dello','della','dei','degli','delle',
31
+ 'a','al','allo','alla','ai','agli','alle',
32
+ 'da','dal','dallo','dalla','dai','dagli','dalle',
33
+ 'in','nel','nello','nella','nei','negli','nelle',
34
+ 'su','sul','sullo','sulla','sui','sugli','sulle',
35
+ 'con','col','per','tra','fra',
36
+ // Common conjunctions / adverbs that fuse
37
+ 'che','non','ma','se','anche','come','dove','quando',
38
+ 'perché','mentre','quindi','però','sia','né',
39
+ 'questo','questa','questi','queste','quello','quella',
40
+ 'e','o','è',
41
+ ];
42
+
43
+ // Build a single regex that matches any of these words preceded by a lowercase letter
44
+ const BPE_SPLIT_RE = new RegExp(
45
+ '([a-zàèéìòù])(' + IT_FUNCTION_WORDS.map((w) => w.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')).join('|') + ')(?=[A-Za-zàèéìòù])',
46
+ 'g',
47
+ );
48
+
49
+ function fixQwen3BPE(text) {
50
+ // Process line by line — skip table rows and code blocks
51
+ let inCode = false;
52
+ return text.split('\n').map((line) => {
53
+ if (line.trimStart().startsWith('```')) { inCode = !inCode; return line; }
54
+ if (inCode) return line;
55
+ if (line.trimStart().startsWith('|')) return line; // table row — untouched
56
+ // Three passes to handle chains
57
+ let out = line;
58
+ out = out.replace(BPE_SPLIT_RE, '$1 $2');
59
+ out = out.replace(BPE_SPLIT_RE, '$1 $2');
60
+ out = out.replace(BPE_SPLIT_RE, '$1 $2');
61
+ return out;
62
+ }).join('\n');
63
+ }
64
+
8
65
  // ── Providers ──────────────────────────────────────────────────────────────
9
66
 
10
67
  export async function callAnthropic(apiKey, model, systemPrompt, userMessage, stream = false, opts = {}) {
@@ -574,6 +631,13 @@ export async function callLLMStream(config, systemPrompt, userMessage, onToken,
574
631
  let fullNhaText = nhaJson.choices?.[0]?.message?.content || '';
575
632
  // Strip <think>...</think> blocks
576
633
  fullNhaText = fullNhaText.replace(/<think>[\s\S]*?<\/think>/g, '').trim();
634
+ // Fix Qwen3 BPE tokenizer artifacts: words joined without spaces.
635
+ // Qwen3 streaming via vLLM occasionally produces subword tokens that arrive
636
+ // concatenated — e.g. "lacorrelazione" instead of "la correlazione",
637
+ // "ilprezzodell'oro" instead of "il prezzo dell'oro".
638
+ // We fix this by inserting spaces before Italian/English articles and
639
+ // prepositions that appear fused at word boundaries.
640
+ fullNhaText = fixQwen3BPE(fullNhaText);
577
641
  if (onToken) onToken(fullNhaText);
578
642
  return fullNhaText;
579
643
  }