nothumanallowed 13.2.39 → 13.2.40
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 +1 -1
- package/src/constants.mjs +1 -1
- package/src/services/llm.mjs +21 -4
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "nothumanallowed",
|
|
3
|
-
"version": "13.2.
|
|
3
|
+
"version": "13.2.40",
|
|
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 = '13.2.
|
|
8
|
+
export const VERSION = '13.2.40';
|
|
9
9
|
export const BASE_URL = 'https://nothumanallowed.com/cli';
|
|
10
10
|
export const API_BASE = 'https://nothumanallowed.com/api/v1';
|
|
11
11
|
|
package/src/services/llm.mjs
CHANGED
|
@@ -624,6 +624,25 @@ function getProviderHeaders(provider, apiKey) {
|
|
|
624
624
|
}
|
|
625
625
|
|
|
626
626
|
/** Parse a complete SSE text body (already read via res.text()) and call onToken per token. */
|
|
627
|
+
/**
|
|
628
|
+
* Qwen3 sometimes emits entire paragraphs as a single token with no spaces/newlines.
|
|
629
|
+
* This restores markdown structure: newlines before headings, list items, numbered lists.
|
|
630
|
+
* Only applied to non-HTML content (inside HTML tags is left untouched).
|
|
631
|
+
*/
|
|
632
|
+
function fixQwen3Markdown(text) {
|
|
633
|
+
// Don't touch HTML content
|
|
634
|
+
if (/<[a-zA-Z]/.test(text) && text.includes('</')) return text;
|
|
635
|
+
return text
|
|
636
|
+
// newline before markdown headings (##, ###, etc.) not at start
|
|
637
|
+
.replace(/([^\n])(#{1,6}\s)/g, '$1\n$2')
|
|
638
|
+
// newline before list items (- or * at word boundary) not at start
|
|
639
|
+
.replace(/([^\n])(\n?[-*]\s)/g, '$1\n$2')
|
|
640
|
+
// newline before numbered list items (1. 2. etc.) not at start
|
|
641
|
+
.replace(/([^\n])(\n?\d+\.\s)/g, '$1\n$2')
|
|
642
|
+
// newline before --- separators
|
|
643
|
+
.replace(/([^\n])(---)/g, '$1\n$2');
|
|
644
|
+
}
|
|
645
|
+
|
|
627
646
|
function parseSSEText(text, format, onToken) {
|
|
628
647
|
let fullText = '';
|
|
629
648
|
let thinkBuf = '';
|
|
@@ -661,9 +680,8 @@ function parseSSEText(text, format, onToken) {
|
|
|
661
680
|
}
|
|
662
681
|
}
|
|
663
682
|
if (out) {
|
|
664
|
-
// Qwen3 emits tokens without spaces between words — insert space when needed
|
|
665
|
-
// But NOT inside HTML tags (e.g. < div class = "foo" > would get broken)
|
|
666
683
|
const insideTag = fullText.lastIndexOf('<') > fullText.lastIndexOf('>');
|
|
684
|
+
if (!insideTag) out = fixQwen3Markdown(out);
|
|
667
685
|
if (fullText && out && !insideTag && !/[\s\n]$/.test(fullText) && !/^[\s\n.,;:!?)\]}'">]/.test(out)) {
|
|
668
686
|
out = ' ' + out;
|
|
669
687
|
}
|
|
@@ -730,9 +748,8 @@ async function streamSSEWithCallback(res, format, onToken) {
|
|
|
730
748
|
}
|
|
731
749
|
}
|
|
732
750
|
if (out) {
|
|
733
|
-
// Qwen3 emits tokens without spaces between words — insert space when needed
|
|
734
|
-
// But NOT inside HTML tags
|
|
735
751
|
const insideTag2 = fullText.lastIndexOf('<') > fullText.lastIndexOf('>');
|
|
752
|
+
if (!insideTag2) out = fixQwen3Markdown(out);
|
|
736
753
|
if (fullText && out && !insideTag2 && !/[\s\n]$/.test(fullText) && !/^[\s\n.,;:!?)\]}'">]/.test(out)) {
|
|
737
754
|
out = ' ' + out;
|
|
738
755
|
}
|