@nanogpt/private-mode 0.2.11 → 0.2.13
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/README.md +4 -2
- package/lib/requestTransforms.js +42 -11
- package/lib/serverErrorNormalization.js +1 -1
- package/lib/statusContract.js +2 -1
- package/models/private-tee.json +28 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -46,7 +46,7 @@ NanoGPT can see account identity, selected private model, selected TEE target me
|
|
|
46
46
|
|
|
47
47
|
NanoGPT's web app can also use these models without running this local proxy. Select an eligible Private Mode model and use the Private Mode control in the model picker. This package is for API clients, CLIs, agents, and other OpenAI-compatible tools.
|
|
48
48
|
|
|
49
|
-
The web
|
|
49
|
+
The web app supports local image attachments for Private Mode vision models (Kimi K3 and Gemma 4 31B). Image bytes are converted to base64 in the browser and included inside the EHBP-encrypted request; they are not uploaded to NanoGPT object storage. Other attachment types, web search, URL-scraped content, project tools, multi-model chat, Context Memory injection, quick replies, and automatic title generation remain disabled for private turns.
|
|
50
50
|
|
|
51
51
|
In the hosted web app, decrypted Private Mode turns remain in local browser history. Cloud conversation sync is blocked for Private Mode chats unless password-based end-to-end sync is enabled; the recoverable default sync mode is not used for those chats.
|
|
52
52
|
|
|
@@ -74,10 +74,12 @@ Supported private model IDs include:
|
|
|
74
74
|
- `private/llama3-3-70b`
|
|
75
75
|
- `private/glm-5-2`
|
|
76
76
|
- `private/glm-5-2:thinking`
|
|
77
|
+
- `private/glm-5-3`
|
|
78
|
+
- `private/glm-5-3-flash`
|
|
77
79
|
- `private/gemma4-31b`
|
|
78
80
|
- `private/gemma4-31b:thinking`
|
|
79
81
|
|
|
80
|
-
Both GLM 5.2 variants are deployed with a 393,216-token total context limit, with prompt and output sharing that window. Kimi K3 Private has a 256,000-token context limit. DeepSeek V4 Flash 0731 Private has a 1,048,576-token total context limit. The model-list extension `context_length` is the combined prompt-and-output window, while `max_output_tokens` is the output ceiling within that same window. These values are not additive.
|
|
82
|
+
Both GLM 5.2 variants are deployed with a 393,216-token total context limit, with prompt and output sharing that window. GLM 5.3 Private and GLM 5.3 Flash Private each have a 1,048,576-token total context limit and a 131,072-token output ceiling. Kimi K3 Private has a 256,000-token context limit. DeepSeek V4 Flash 0731 Private has a 1,048,576-token total context limit. The model-list extension `context_length` is the combined prompt-and-output window, while `max_output_tokens` is the output ceiling within that same window. These values are not additive.
|
|
81
83
|
|
|
82
84
|
Browser requests are locked down by default. The proxy only accepts same-machine clients and rejects browser `Origin` headers that are not explicitly allowed, so a random website or LAN client cannot spend the local `NANOGPT_API_KEY` while the proxy is running. If a local browser app needs to call the proxy directly, allow that exact origin:
|
|
83
85
|
|
package/lib/requestTransforms.js
CHANGED
|
@@ -29,6 +29,7 @@ const KIMI_K3_TEXT_ENCODER = new TextEncoder();
|
|
|
29
29
|
const DEEPSEEK_V4_CONTEXT_WINDOW_TOKENS = 1_048_576;
|
|
30
30
|
const DEEPSEEK_V4_MAX_COMPLETION_TOKENS = 1_048_576;
|
|
31
31
|
const DEEPSEEK_V4_CONTEXT_SAFETY_MARGIN_TOKENS = 10;
|
|
32
|
+
const GLM_53_MAX_COMPLETION_TOKENS = 131_072;
|
|
32
33
|
const PRIVATE_TINFOIL_CHAT_COMPLETION_BODY_FIELDS = new Set([
|
|
33
34
|
'chat_template_kwargs',
|
|
34
35
|
'frequency_penalty',
|
|
@@ -243,6 +244,14 @@ function normalizeDeepSeekV4ReasoningEffort(value) {
|
|
|
243
244
|
return 'high';
|
|
244
245
|
}
|
|
245
246
|
|
|
247
|
+
function normalizeGlm53ReasoningEffort(value) {
|
|
248
|
+
const normalized = typeof value === 'string' ? value.trim().toLowerCase() : '';
|
|
249
|
+
if (normalized === 'max' || normalized === 'xhigh') return 'max';
|
|
250
|
+
if (normalized === 'low' || normalized === 'minimal') return 'low';
|
|
251
|
+
if (normalized === 'high' || normalized === 'medium') return 'high';
|
|
252
|
+
return 'max';
|
|
253
|
+
}
|
|
254
|
+
|
|
246
255
|
function getPrivateModeModelSignals(body, model) {
|
|
247
256
|
return [
|
|
248
257
|
body.model,
|
|
@@ -419,6 +428,10 @@ function isPrivateModeKimiK3Model(model) {
|
|
|
419
428
|
});
|
|
420
429
|
}
|
|
421
430
|
|
|
431
|
+
function isPrivateModeGlm53Model(model) {
|
|
432
|
+
return model.thinkingMode === 'glm-5.3';
|
|
433
|
+
}
|
|
434
|
+
|
|
422
435
|
function getPrivateModeFunctionToolName(tool) {
|
|
423
436
|
if (!isPlainObject(tool) || tool.type !== 'function' || !isPlainObject(tool.function)) {
|
|
424
437
|
return undefined;
|
|
@@ -513,7 +526,7 @@ export function normalizePrivateModelReasoningControls(body, model) {
|
|
|
513
526
|
|
|
514
527
|
export function shouldSuppressPrivateModelReasoning(body, model) {
|
|
515
528
|
const isKimiK3 = isPrivateModeKimiK3Model(model);
|
|
516
|
-
if (!isKimiK3 && model.thinkingMode !== 'deepseek-v4') return false;
|
|
529
|
+
if (!isKimiK3 && model.thinkingMode !== 'deepseek-v4' && model.thinkingMode !== 'glm-5.3') return false;
|
|
517
530
|
const reasoningVisibilityOptOut = body.reasoningOptOut === true ||
|
|
518
531
|
body.exposeReasoning === false ||
|
|
519
532
|
body.materializeReasoning === false ||
|
|
@@ -528,9 +541,6 @@ export function shouldSuppressPrivateModelReasoning(body, model) {
|
|
|
528
541
|
|
|
529
542
|
function resolvePrivateModeKimiK3ReasoningEffort(body) {
|
|
530
543
|
const candidates = [
|
|
531
|
-
body.reasoningOptOut === true ? false : undefined,
|
|
532
|
-
body.exposeReasoning === false ? false : undefined,
|
|
533
|
-
body.materializeReasoning === false ? false : undefined,
|
|
534
544
|
body.enable_thinking,
|
|
535
545
|
body.thinking,
|
|
536
546
|
body.reasoning_effort,
|
|
@@ -544,8 +554,7 @@ function resolvePrivateModeKimiK3ReasoningEffort(body) {
|
|
|
544
554
|
const normalizedEffort = typeof explicitEffort === 'string'
|
|
545
555
|
? explicitEffort.trim().toLowerCase()
|
|
546
556
|
: undefined;
|
|
547
|
-
|
|
548
|
-
return excludesReasoning ? normalizedEffort ?? 'low' : normalizedEffort ?? 'max';
|
|
557
|
+
return normalizedEffort ?? 'max';
|
|
549
558
|
}
|
|
550
559
|
|
|
551
560
|
function applyPrivateModeKimiK3RequestParams(body, model) {
|
|
@@ -618,6 +627,20 @@ function clampPrivateModeDeepSeekV4Output(body) {
|
|
|
618
627
|
);
|
|
619
628
|
}
|
|
620
629
|
|
|
630
|
+
function clampPrivateModeGlm53Output(body) {
|
|
631
|
+
const requested = typeof body.max_tokens === 'number' && Number.isFinite(body.max_tokens)
|
|
632
|
+
? body.max_tokens
|
|
633
|
+
: undefined;
|
|
634
|
+
if (requested === undefined) return;
|
|
635
|
+
const promptTokens = estimatePrivateModeKimiK3PromptTokens(body);
|
|
636
|
+
const remainingContext = Math.max(1, DEEPSEEK_V4_CONTEXT_WINDOW_TOKENS - promptTokens - DEEPSEEK_V4_CONTEXT_SAFETY_MARGIN_TOKENS);
|
|
637
|
+
body.max_tokens = Math.min(
|
|
638
|
+
requested < 0 ? GLM_53_MAX_COMPLETION_TOKENS : Math.max(1, Math.floor(requested)),
|
|
639
|
+
GLM_53_MAX_COMPLETION_TOKENS,
|
|
640
|
+
remainingContext,
|
|
641
|
+
);
|
|
642
|
+
}
|
|
643
|
+
|
|
621
644
|
export function createPrivateModeReasoningContentSuppressor() {
|
|
622
645
|
const openTags = ['<think>', '<thinking>', '<previous_reasoning>', '◁think▷'];
|
|
623
646
|
const closeTags = ['</think>', '</thinking>', '</previous_reasoning>', '◁/think▷'];
|
|
@@ -1011,10 +1034,12 @@ function normalizeStreamOptions(body) {
|
|
|
1011
1034
|
}
|
|
1012
1035
|
|
|
1013
1036
|
export function applyPrivateModelRequestMutations(body, model) {
|
|
1014
|
-
if (isPrivateModeKimiK3Model(model)) {
|
|
1037
|
+
if (isPrivateModeKimiK3Model(model) || isPrivateModeGlm53Model(model)) {
|
|
1015
1038
|
normalizePrivateModelReasoningControls(body, model);
|
|
1016
1039
|
normalizePrivateModeKimiK3ReasoningFromMessages(body);
|
|
1017
|
-
|
|
1040
|
+
if (isPrivateModeKimiK3Model(model)) {
|
|
1041
|
+
normalizePrivateModeKimiK3DynamicToolMessages(body);
|
|
1042
|
+
}
|
|
1018
1043
|
} else {
|
|
1019
1044
|
stripPrivateModeReasoningFromMessages(body);
|
|
1020
1045
|
}
|
|
@@ -1026,6 +1051,8 @@ export function applyPrivateModelRequestMutations(body, model) {
|
|
|
1026
1051
|
applyPrivateModeKimiK3RequestParams(body, model);
|
|
1027
1052
|
} else if (model.thinkingMode === 'deepseek-v4') {
|
|
1028
1053
|
clampPrivateModeDeepSeekV4Output(body);
|
|
1054
|
+
} else if (model.thinkingMode === 'glm-5.3') {
|
|
1055
|
+
clampPrivateModeGlm53Output(body);
|
|
1029
1056
|
}
|
|
1030
1057
|
|
|
1031
1058
|
if (model.thinkingMode === 'gemma') {
|
|
@@ -1037,9 +1064,12 @@ export function applyPrivateModelRequestMutations(body, model) {
|
|
|
1037
1064
|
delete body.reasoning_effort;
|
|
1038
1065
|
} else if (
|
|
1039
1066
|
model.thinkingMode === 'glm-5.2' ||
|
|
1067
|
+
model.thinkingMode === 'glm-5.3' ||
|
|
1040
1068
|
model.thinkingMode === 'deepseek-v4'
|
|
1041
1069
|
) {
|
|
1042
|
-
const thinkingEnabled =
|
|
1070
|
+
const thinkingEnabled = model.thinkingMode === 'glm-5.3'
|
|
1071
|
+
? true
|
|
1072
|
+
: shouldEnableThinking(body, model);
|
|
1043
1073
|
const requestedReasoningEffort = body.reasoning_effort
|
|
1044
1074
|
?? (isPlainObject(body.reasoning) ? body.reasoning.effort : undefined);
|
|
1045
1075
|
body.chat_template_kwargs = {
|
|
@@ -1048,8 +1078,9 @@ export function applyPrivateModelRequestMutations(body, model) {
|
|
|
1048
1078
|
};
|
|
1049
1079
|
|
|
1050
1080
|
if (thinkingEnabled) {
|
|
1051
|
-
body.chat_template_kwargs.reasoning_effort =
|
|
1052
|
-
|
|
1081
|
+
body.chat_template_kwargs.reasoning_effort = model.thinkingMode === 'glm-5.3'
|
|
1082
|
+
? normalizeGlm53ReasoningEffort(requestedReasoningEffort)
|
|
1083
|
+
: normalizeDeepSeekV4ReasoningEffort(requestedReasoningEffort);
|
|
1053
1084
|
} else {
|
|
1054
1085
|
delete body.chat_template_kwargs.reasoning_effort;
|
|
1055
1086
|
}
|
|
@@ -6,7 +6,7 @@ const EHBP_RESPONSE_NONCE_HEADER = 'ehbp-response-nonce';
|
|
|
6
6
|
const PRIVATE_MODE_REFUND_NOTICE = 'Any reserved balance will be released or refunded.';
|
|
7
7
|
const MAX_UPSTREAM_ERROR_BODY_BYTES = 16 * 1024;
|
|
8
8
|
const MAX_UPSTREAM_ERROR_MESSAGE_CHARS = 1_000;
|
|
9
|
-
const HIDDEN_PROVIDER_NAME_PATTERN = /\b(?:openrouter|spoke\s*ai|aihubmix|
|
|
9
|
+
const HIDDEN_PROVIDER_NAME_PATTERN = /\b(?:openrouter|spoke\s*ai|aihubmix|mimas|comet|azure|digital\s*ocean|axionic|whale\s*ai|langfork)\b/gi;
|
|
10
10
|
|
|
11
11
|
export function privateModeProviderFailureMessage(status) {
|
|
12
12
|
if (status === 429) {
|
package/lib/statusContract.js
CHANGED
|
@@ -2,6 +2,7 @@ import { createHash } from 'node:crypto';
|
|
|
2
2
|
|
|
3
3
|
export const PRIVATE_MODE_FRONTEND_SUPPORTED_FEATURES = Object.freeze([
|
|
4
4
|
'text_chat',
|
|
5
|
+
'vision_image_inputs',
|
|
5
6
|
'streaming',
|
|
6
7
|
'conversation_history',
|
|
7
8
|
'model_settings',
|
|
@@ -9,7 +10,7 @@ export const PRIVATE_MODE_FRONTEND_SUPPORTED_FEATURES = Object.freeze([
|
|
|
9
10
|
]);
|
|
10
11
|
|
|
11
12
|
export const PRIVATE_MODE_FRONTEND_DISABLED_FEATURES = Object.freeze([
|
|
12
|
-
'
|
|
13
|
+
'non_image_attachments',
|
|
13
14
|
'web_search',
|
|
14
15
|
'url_scraped_content',
|
|
15
16
|
'project_chats',
|
package/models/private-tee.json
CHANGED
|
@@ -76,6 +76,34 @@
|
|
|
76
76
|
"ownedBy": "nanogpt-private-mode",
|
|
77
77
|
"aliases": ["private/glm-5.2:thinking", "TEE/glm-5-2:thinking", "TEE/glm-5.2:thinking"]
|
|
78
78
|
},
|
|
79
|
+
{
|
|
80
|
+
"id": "private/glm-5-3",
|
|
81
|
+
"name": "GLM 5.3 Private",
|
|
82
|
+
"upstreamModel": "glm-5-3",
|
|
83
|
+
"billingModel": "TEE/glm-5.3",
|
|
84
|
+
"providerPricingModel": "TEE/glm-5.3",
|
|
85
|
+
"teeTargetModel": "glm-5-3",
|
|
86
|
+
"maxInputTokens": 1048576,
|
|
87
|
+
"maxOutputTokens": 131072,
|
|
88
|
+
"thinkingMode": "glm-5.3",
|
|
89
|
+
"created": 1788391929,
|
|
90
|
+
"ownedBy": "nanogpt-private-mode",
|
|
91
|
+
"aliases": ["private/glm-5.3", "TEE/glm-5-3", "TEE/glm-5.3"]
|
|
92
|
+
},
|
|
93
|
+
{
|
|
94
|
+
"id": "private/glm-5-3-flash",
|
|
95
|
+
"name": "GLM 5.3 Flash Private",
|
|
96
|
+
"upstreamModel": "glm-5-3-flash",
|
|
97
|
+
"billingModel": "TEE/glm-5.3-flash",
|
|
98
|
+
"providerPricingModel": "TEE/glm-5.3-flash",
|
|
99
|
+
"teeTargetModel": "glm-5-3-flash",
|
|
100
|
+
"maxInputTokens": 1048576,
|
|
101
|
+
"maxOutputTokens": 131072,
|
|
102
|
+
"thinkingMode": "glm-5.3",
|
|
103
|
+
"created": 1788307200,
|
|
104
|
+
"ownedBy": "nanogpt-private-mode",
|
|
105
|
+
"aliases": ["private/glm-5.3-flash", "TEE/glm-5-3-flash", "TEE/glm-5.3-flash"]
|
|
106
|
+
},
|
|
79
107
|
{
|
|
80
108
|
"id": "private/gemma4-31b",
|
|
81
109
|
"name": "Gemma 4 31B Private",
|