@nanogpt/private-mode 0.2.13 → 0.2.14

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 CHANGED
@@ -81,6 +81,23 @@ Supported private model IDs include:
81
81
 
82
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.
83
83
 
84
+ ### Thinking without leaving Private Mode
85
+
86
+ Keep the same private model ID when enabling thinking. For example:
87
+
88
+ ```json
89
+ {
90
+ "model": "private/deepseek-v4-flash",
91
+ "messages": [{ "role": "user", "content": "What is 17 times 19?" }],
92
+ "reasoning_effort": "high",
93
+ "stream": true
94
+ }
95
+ ```
96
+
97
+ DeepSeek V4 Flash uses the 0731 release and supports optional thinking. GLM 5.2 and Gemma 4 also support optional thinking. Use `thinking: true` or `enable_thinking: true` to enable it; `thinking: false` disables it for these optional-thinking models. Kimi K3, GLM 5.3, and GLM 5.3 Flash always generate reasoning. GLM 5.3 models accept `low`, `high`, or `max` effort. `reasoning: { "exclude": true }` controls response visibility and does not disable thinking.
98
+
99
+ Non-streaming reasoning requests may take more than five minutes. The CLI allows the encrypted request to wait for the hosted route's response, with a 30-minute transport limit. The hosted route stops non-streaming generation after at most 29 minutes to leave time before its platform deadline. Use `stream: true` for earlier output, and configure your API client's timeout to allow long generations. Upgrade the local proxy as well as the hosted service to receive both timeout fixes.
100
+
84
101
  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:
85
102
 
86
103
  ```bash
@@ -4,6 +4,7 @@ import { parseArgs } from 'node:util';
4
4
  import { PRIVATE_MODE_PROXY_VERSION } from '../lib/packageInfo.js';
5
5
  import { startPrivateModeProxy } from '../lib/server.js';
6
6
  import { verifyPrivateModeReceiptCli } from '../lib/verifyReceipt.js';
7
+ import { installPrivateModeTransportTimeouts } from '../lib/transportTimeouts.js';
7
8
 
8
9
  const { values, positionals } = parseArgs({
9
10
  options: {
@@ -69,6 +70,7 @@ if (!apiKey) {
69
70
  process.exit(1);
70
71
  }
71
72
 
73
+ installPrivateModeTransportTimeouts(String(values['api-base']));
72
74
  startPrivateModeProxy({
73
75
  apiBase: String(values['api-base']),
74
76
  apiKey,
@@ -271,8 +271,11 @@ function shouldEnableThinking(body, model) {
271
271
  ));
272
272
 
273
273
  return explicitThinking
274
+ ?? isThinkingEnabled(body.enable_thinking)
274
275
  ?? explicitReasoning
275
276
  ?? (body.reasoning_effort !== undefined ? reasoningEffort !== 'none' : undefined)
277
+ ?? isThinkingEnabled(body.chat_template_kwargs?.thinking)
278
+ ?? isThinkingEnabled(body.chat_template_kwargs?.enable_thinking)
276
279
  ?? hasThinkingSuffix;
277
280
  }
278
281
 
@@ -526,7 +529,6 @@ export function normalizePrivateModelReasoningControls(body, model) {
526
529
 
527
530
  export function shouldSuppressPrivateModelReasoning(body, model) {
528
531
  const isKimiK3 = isPrivateModeKimiK3Model(model);
529
- if (!isKimiK3 && model.thinkingMode !== 'deepseek-v4' && model.thinkingMode !== 'glm-5.3') return false;
530
532
  const reasoningVisibilityOptOut = body.reasoningOptOut === true ||
531
533
  body.exposeReasoning === false ||
532
534
  body.materializeReasoning === false ||
@@ -1071,22 +1073,26 @@ export function applyPrivateModelRequestMutations(body, model) {
1071
1073
  ? true
1072
1074
  : shouldEnableThinking(body, model);
1073
1075
  const requestedReasoningEffort = body.reasoning_effort
1074
- ?? (isPlainObject(body.reasoning) ? body.reasoning.effort : undefined);
1076
+ ?? (isPlainObject(body.reasoning) ? body.reasoning.effort : undefined)
1077
+ ?? body.chat_template_kwargs?.reasoning_effort;
1075
1078
  body.chat_template_kwargs = {
1076
1079
  ...mergeChatTemplateKwargs(body),
1077
1080
  thinking: thinkingEnabled,
1078
1081
  };
1079
1082
 
1080
- if (thinkingEnabled) {
1081
- body.chat_template_kwargs.reasoning_effort = model.thinkingMode === 'glm-5.3'
1082
- ? normalizeGlm53ReasoningEffort(requestedReasoningEffort)
1083
- : normalizeDeepSeekV4ReasoningEffort(requestedReasoningEffort);
1083
+ if (model.thinkingMode === 'glm-5.3') {
1084
+ // Match the native GLM API and the browser client: low/high/max belongs
1085
+ // at the top level, including on tool turns.
1086
+ body.reasoning_effort = normalizeGlm53ReasoningEffort(requestedReasoningEffort);
1087
+ delete body.chat_template_kwargs.reasoning_effort;
1088
+ } else if (thinkingEnabled) {
1089
+ body.chat_template_kwargs.reasoning_effort = normalizeDeepSeekV4ReasoningEffort(requestedReasoningEffort);
1084
1090
  } else {
1085
1091
  delete body.chat_template_kwargs.reasoning_effort;
1086
1092
  }
1087
1093
 
1088
1094
  delete body.thinking;
1089
- delete body.reasoning_effort;
1095
+ if (model.thinkingMode !== 'glm-5.3') delete body.reasoning_effort;
1090
1096
  }
1091
1097
 
1092
1098
  stripUnsupportedPrivateTinfoilFields(body);
@@ -0,0 +1,28 @@
1
+ import { Agent, setGlobalDispatcher } from 'undici';
2
+ import { PRIVATE_MODE_STREAM_ROUTE_MAX_DURATION_MS } from './constants.js';
3
+
4
+ export class PrivateModeDispatcher extends Agent {
5
+ constructor(apiBase) {
6
+ super();
7
+ this.chatUrl = new URL(`${apiBase.replace(/\/+$/, '')}/api/v1/private/tinfoil/v1/chat/completions`);
8
+ }
9
+
10
+ dispatch(options, handler) {
11
+ // EHBP calls native fetch after encryption, without retaining a custom
12
+ // dispatcher. Scope the CLI's override to its configured encrypted endpoint.
13
+ // Override dispatch options, not Agent defaults: fetch supplies its own
14
+ // five-minute timeout values on every dispatch.
15
+ if (String(options.origin) === this.chatUrl.origin && options.path === this.chatUrl.pathname) {
16
+ options = {
17
+ ...options,
18
+ headersTimeout: PRIVATE_MODE_STREAM_ROUTE_MAX_DURATION_MS,
19
+ bodyTimeout: PRIVATE_MODE_STREAM_ROUTE_MAX_DURATION_MS,
20
+ };
21
+ }
22
+ return super.dispatch(options, handler);
23
+ }
24
+ }
25
+
26
+ export function installPrivateModeTransportTimeouts(apiBase) {
27
+ setGlobalDispatcher(new PrivateModeDispatcher(apiBase));
28
+ }
@@ -97,6 +97,9 @@
97
97
  "billingModel": "TEE/glm-5.3-flash",
98
98
  "providerPricingModel": "TEE/glm-5.3-flash",
99
99
  "teeTargetModel": "glm-5-3-flash",
100
+ "teeTargetAliases": [
101
+ "glm5-3-flash"
102
+ ],
100
103
  "maxInputTokens": 1048576,
101
104
  "maxOutputTokens": 131072,
102
105
  "thinkingMode": "glm-5.3",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nanogpt/private-mode",
3
- "version": "0.2.13",
3
+ "version": "0.2.14",
4
4
  "description": "OpenAI-compatible localhost proxy for NanoGPT Private Mode.",
5
5
  "type": "module",
6
6
  "publishConfig": {
@@ -23,6 +23,7 @@
23
23
  "lib/serverErrorNormalization.js",
24
24
  "lib/sseResponsePump.js",
25
25
  "lib/statusContract.js",
26
+ "lib/transportTimeouts.js",
26
27
  "lib/verifyReceipt.js",
27
28
  "models",
28
29
  "README.md"
@@ -32,7 +33,8 @@
32
33
  },
33
34
  "dependencies": {
34
35
  "ai": "6.0.220",
35
- "tinfoil": "1.1.12"
36
+ "tinfoil": "1.1.12",
37
+ "undici": "6.28.0"
36
38
  },
37
39
  "engines": {
38
40
  "node": ">=22"