pi-nvidia-nim 1.1.21 → 1.1.23
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 +17 -12
- package/index.ts +101 -84
- package/package.json +9 -4
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# pi-nvidia-nim
|
|
2
2
|
|
|
3
|
-
NVIDIA NIM API provider extension for [pi coding agent](https://github.com/earendil-works/pi-mono) - access 100+ models from [build.nvidia.com](https://build.nvidia.com) including DeepSeek V4 Flash/Pro, DeepSeek V3.2, Kimi K2.6,
|
|
3
|
+
NVIDIA NIM API provider extension for [pi coding agent](https://github.com/earendil-works/pi-mono) - access 100+ models from [build.nvidia.com](https://build.nvidia.com) including Inkling, MiniMax M3, DeepSeek V4 Flash/Pro, DeepSeek V3.2, Kimi K2.6, GLM-5, GLM-4.7, Qwen3, Llama 4, and many more.
|
|
4
4
|
|
|
5
5
|
https://github.com/user-attachments/assets/f44773e4-9bf8-4bb5-a9c0-d5938030701c
|
|
6
6
|
|
|
@@ -74,35 +74,40 @@ NVIDIA NIM models use a non-standard `chat_template_kwargs` parameter to enable
|
|
|
74
74
|
|
|
75
75
|
When you change the thinking level in pi (`Shift+Tab` to cycle), the extension:
|
|
76
76
|
|
|
77
|
-
1. **Maps thinking levels** to values each NIM model accepts.
|
|
77
|
+
1. **Maps thinking levels** to values each NIM model accepts. MiniMax M3 uses its native `disabled`/`adaptive`/`enabled` modes; Inkling and DeepSeek V4 map extended levels to `max`.
|
|
78
78
|
2. **Injects `chat_template_kwargs`** per model to actually enable thinking:
|
|
79
|
+
- Inkling: `{ reasoning_effort: "none" | "minimal" | "low" | "medium" | "high" | "max" }`
|
|
80
|
+
- MiniMax M3: `{ thinking_mode: "disabled" | "adaptive" | "enabled" }`
|
|
79
81
|
- DeepSeek V4: `{ thinking: true, reasoning_effort: "high" | "max" }`
|
|
80
82
|
- DeepSeek V3.x, R1 distills: `{ thinking: true }`
|
|
81
83
|
- GLM-5, GLM-4.7: `{ enable_thinking: true, clear_thinking: false }`
|
|
82
84
|
- Kimi K2.6, K2-thinking: `{ thinking: true }`
|
|
83
85
|
- Qwen3, QwQ: `{ enable_thinking: true }`
|
|
84
|
-
3. **Explicitly
|
|
86
|
+
3. **Explicitly selects the lowest/off setting** when thinking is "off" for configurable models. MiniMax M3 uses `disabled`; Inkling uses `none` as a conditioning hint rather than a guarantee of zero reasoning tokens.
|
|
85
87
|
4. **Uses `system` role** instead of `developer` for all NIM models - the `developer` role combined with `chat_template_kwargs` causes 500 errors on NIM.
|
|
86
88
|
|
|
87
89
|
### Supported thinking levels
|
|
88
90
|
|
|
89
91
|
| pi Level | NIM Mapping | Effect |
|
|
90
92
|
|----------|-------------|--------|
|
|
91
|
-
| off |
|
|
92
|
-
| minimal |
|
|
93
|
-
| low | low, or high for DeepSeek V4 | Thinking enabled |
|
|
94
|
-
| medium | medium, or high for DeepSeek V4 | Thinking enabled |
|
|
95
|
-
| high | high | Thinking enabled |
|
|
96
|
-
| xhigh |
|
|
93
|
+
| off | MiniMax M3 `disabled`; Inkling `none`; explicit disable elsewhere | No/lowest reasoning effort |
|
|
94
|
+
| minimal | MiniMax M3 `adaptive`; Inkling `minimal`; low/high where required | Thinking enabled when useful |
|
|
95
|
+
| low | MiniMax M3 `adaptive`; low, or high for DeepSeek V4 | Thinking enabled when useful |
|
|
96
|
+
| medium | MiniMax M3 `adaptive`; medium, or high for DeepSeek V4 | Thinking enabled when useful |
|
|
97
|
+
| high | MiniMax M3 `enabled`; high elsewhere | Thinking enabled |
|
|
98
|
+
| xhigh | MiniMax M3 `enabled`; max for Inkling/DeepSeek V4 | Extended thinking |
|
|
99
|
+
| max | MiniMax M3 `enabled`; max for Inkling/DeepSeek V4 | Maximum supported thinking |
|
|
97
100
|
|
|
98
101
|
## Available Models
|
|
99
102
|
|
|
100
|
-
The extension ships with curated metadata for
|
|
103
|
+
The extension ships with curated metadata for 44 featured models. At startup, it also queries the NVIDIA NIM API to discover additional models automatically.
|
|
101
104
|
|
|
102
105
|
### Featured Models
|
|
103
106
|
|
|
104
107
|
| Model | Reasoning | Vision | Context |
|
|
105
108
|
|-------|-----------|--------|---------|
|
|
109
|
+
| `thinkingmachines/inkling` | ✅ | ✅ | 1M |
|
|
110
|
+
| `minimaxai/minimax-m3` | ✅ | ✅ | 1M |
|
|
106
111
|
| `deepseek-ai/deepseek-v4-flash` | ✅ | | 1M |
|
|
107
112
|
| `deepseek-ai/deepseek-v4-pro` | ✅ | | 1M |
|
|
108
113
|
| `deepseek-ai/deepseek-v3.2` | ✅ | | 128K |
|
|
@@ -138,7 +143,7 @@ This extension uses `pi.registerProvider()` to register NVIDIA NIM as a custom p
|
|
|
138
143
|
The custom streamer:
|
|
139
144
|
1. Intercepts the request payload via `onPayload` callback
|
|
140
145
|
2. Injects `chat_template_kwargs` for models that need it to enable thinking
|
|
141
|
-
3. Maps unsupported thinking levels to NIM-compatible values
|
|
146
|
+
3. Maps unsupported thinking levels to NIM-compatible values while preserving MiniMax M3's native modes and Inkling's native effort presets
|
|
142
147
|
4. Suppresses `reasoning_effort` for models that don't respond to it (e.g., DeepSeek without kwargs)
|
|
143
148
|
5. Uses the standard OpenAI SSE streaming format - pi already parses `reasoning_content` and `reasoning` fields from streaming deltas
|
|
144
149
|
|
|
@@ -153,7 +158,7 @@ The only configuration needed is either the `NVIDIA_NIM_API_KEY` or `NVIDIA_API_
|
|
|
153
158
|
- If a model isn't in the curated list, it gets a conservative 32K context window and 8K max output tokens
|
|
154
159
|
- The extension filters out embedding, reward, safety, and other non-chat models automatically
|
|
155
160
|
- Rate limits on free preview keys are relatively strict; you may encounter 429 errors during heavy usage
|
|
156
|
-
- MiniMax models use `<think>` tags inline in content
|
|
161
|
+
- MiniMax M3 streams thinking through `reasoning_content`; older MiniMax models may use `<think>` tags inline in content
|
|
157
162
|
|
|
158
163
|
## License
|
|
159
164
|
|
package/index.ts
CHANGED
|
@@ -34,17 +34,16 @@
|
|
|
34
34
|
* thinking settings.
|
|
35
35
|
*/
|
|
36
36
|
|
|
37
|
-
import { existsSync, readFileSync } from "node:fs";
|
|
38
|
-
import { join } from "node:path";
|
|
39
37
|
import type {
|
|
40
38
|
Api,
|
|
41
39
|
AssistantMessageEventStream,
|
|
42
40
|
Context,
|
|
43
41
|
Model,
|
|
44
42
|
SimpleStreamOptions,
|
|
43
|
+
ThinkingLevelMap,
|
|
45
44
|
} from "@earendil-works/pi-ai";
|
|
46
|
-
import { streamSimpleOpenAICompletions } from "@earendil-works/pi-ai";
|
|
47
|
-
import {
|
|
45
|
+
import { streamSimpleOpenAICompletions } from "@earendil-works/pi-ai/compat";
|
|
46
|
+
import type { ExtensionAPI, ExtensionContext } from "@earendil-works/pi-coding-agent";
|
|
48
47
|
|
|
49
48
|
// =============================================================================
|
|
50
49
|
// Constants
|
|
@@ -55,6 +54,8 @@ const NVIDIA_NIM_API_KEY_ENV = "NVIDIA_NIM_API_KEY";
|
|
|
55
54
|
const NVIDIA_API_KEY_ENV = "NVIDIA_API_KEY";
|
|
56
55
|
const NVIDIA_API_KEY_ENV_NAMES = [NVIDIA_NIM_API_KEY_ENV, NVIDIA_API_KEY_ENV] as const;
|
|
57
56
|
const PROVIDER_NAME = "nvidia-nim";
|
|
57
|
+
const INKLING_MODEL_ID = "thinkingmachines/inkling";
|
|
58
|
+
const MINIMAX_M3_MODEL_ID = "minimaxai/minimax-m3";
|
|
58
59
|
|
|
59
60
|
// =============================================================================
|
|
60
61
|
// Per-model thinking configuration
|
|
@@ -185,7 +186,11 @@ const THINKING_CONFIGS: Record<string, ThinkingConfig> = {
|
|
|
185
186
|
// Reasoning models and their capabilities
|
|
186
187
|
// =============================================================================
|
|
187
188
|
|
|
188
|
-
const REASONING_MODELS = new Set(
|
|
189
|
+
const REASONING_MODELS = new Set([
|
|
190
|
+
...Object.keys(THINKING_CONFIGS),
|
|
191
|
+
INKLING_MODEL_ID,
|
|
192
|
+
MINIMAX_M3_MODEL_ID,
|
|
193
|
+
]);
|
|
189
194
|
|
|
190
195
|
// Models known to support image/vision input
|
|
191
196
|
const VISION_MODELS = new Set([
|
|
@@ -197,6 +202,8 @@ const VISION_MODELS = new Set([
|
|
|
197
202
|
"nvidia/llama-3.1-nemotron-nano-vl-8b-v1",
|
|
198
203
|
"nvidia/nemotron-nano-12b-v2-vl",
|
|
199
204
|
"nvidia/cosmos-reason2-8b",
|
|
205
|
+
INKLING_MODEL_ID,
|
|
206
|
+
MINIMAX_M3_MODEL_ID,
|
|
200
207
|
]);
|
|
201
208
|
|
|
202
209
|
// Embedding / non-chat models to skip
|
|
@@ -269,6 +276,7 @@ const CONTEXT_WINDOWS: Record<string, number> = {
|
|
|
269
276
|
"minimaxai/minimax-m2": 1048576,
|
|
270
277
|
"minimaxai/minimax-m2.1": 1048576,
|
|
271
278
|
"minimaxai/minimax-m2.7": 204800,
|
|
279
|
+
[MINIMAX_M3_MODEL_ID]: 1_048_576,
|
|
272
280
|
// Meta Llama
|
|
273
281
|
"meta/llama-3.1-405b-instruct": 131072,
|
|
274
282
|
"meta/llama-3.1-70b-instruct": 131072,
|
|
@@ -339,6 +347,8 @@ const CONTEXT_WINDOWS: Record<string, number> = {
|
|
|
339
347
|
"nvidia/llama-3.3-nemotron-super-49b-v1.5": 131072,
|
|
340
348
|
"nvidia/nemotron-4-340b-instruct": 4096,
|
|
341
349
|
"nvidia/nvidia-nemotron-nano-9b-v2": 131072,
|
|
350
|
+
// Thinking Machines
|
|
351
|
+
[INKLING_MODEL_ID]: 1_048_576,
|
|
342
352
|
// OpenAI open-source
|
|
343
353
|
"openai/gpt-oss-120b": 131072,
|
|
344
354
|
"openai/gpt-oss-20b": 131072,
|
|
@@ -396,6 +406,7 @@ const MAX_TOKENS: Record<string, number> = {
|
|
|
396
406
|
"minimaxai/minimax-m2": 8192,
|
|
397
407
|
"minimaxai/minimax-m2.1": 8192,
|
|
398
408
|
"minimaxai/minimax-m2.7": 8192,
|
|
409
|
+
[MINIMAX_M3_MODEL_ID]: 16_384,
|
|
399
410
|
"meta/llama-4-maverick-17b-128e-instruct": 16384,
|
|
400
411
|
"meta/llama-4-scout-17b-16e-instruct": 16384,
|
|
401
412
|
"z-ai/glm4.7": 16384,
|
|
@@ -406,6 +417,7 @@ const MAX_TOKENS: Record<string, number> = {
|
|
|
406
417
|
"openai/gpt-oss-20b": 16384,
|
|
407
418
|
"mistralai/mistral-large-3-675b-instruct-2512": 16384,
|
|
408
419
|
"mistralai/devstral-2-123b-instruct-2512": 32768,
|
|
420
|
+
[INKLING_MODEL_ID]: 16_384,
|
|
409
421
|
};
|
|
410
422
|
|
|
411
423
|
// =============================================================================
|
|
@@ -423,6 +435,7 @@ const FEATURED_MODELS = [
|
|
|
423
435
|
"moonshotai/kimi-k2-thinking",
|
|
424
436
|
"moonshotai/kimi-k2-instruct",
|
|
425
437
|
"moonshotai/kimi-k2-instruct-0905",
|
|
438
|
+
MINIMAX_M3_MODEL_ID,
|
|
426
439
|
"minimaxai/minimax-m2.1",
|
|
427
440
|
"minimaxai/minimax-m2",
|
|
428
441
|
"minimaxai/minimax-m2.7",
|
|
@@ -432,6 +445,7 @@ const FEATURED_MODELS = [
|
|
|
432
445
|
"openai/gpt-oss-20b",
|
|
433
446
|
"stepfun-ai/step-3.5-flash",
|
|
434
447
|
"bytedance/seed-oss-36b-instruct",
|
|
448
|
+
INKLING_MODEL_ID,
|
|
435
449
|
// Qwen
|
|
436
450
|
"qwen/qwen3-coder-480b-a35b-instruct",
|
|
437
451
|
"qwen/qwen3-235b-a22b",
|
|
@@ -479,14 +493,6 @@ const FEATURED_MODELS = [
|
|
|
479
493
|
* 4. Uses onPayload callback to mutate request params before they're sent
|
|
480
494
|
*/
|
|
481
495
|
type NimApiKeyEnvName = (typeof NVIDIA_API_KEY_ENV_NAMES)[number];
|
|
482
|
-
type AuthStorageLike = {
|
|
483
|
-
get?: (provider: string) => unknown;
|
|
484
|
-
};
|
|
485
|
-
|
|
486
|
-
interface NimApiKeyCredential {
|
|
487
|
-
type: "api_key";
|
|
488
|
-
key: string;
|
|
489
|
-
}
|
|
490
496
|
|
|
491
497
|
function getNimApiKeyEnv(): NimApiKeyEnvName | undefined {
|
|
492
498
|
return NVIDIA_API_KEY_ENV_NAMES.find((envName) => !!process.env[envName]);
|
|
@@ -498,59 +504,27 @@ function getNimApiKey(): string | undefined {
|
|
|
498
504
|
return apiKey?.trim() || undefined;
|
|
499
505
|
}
|
|
500
506
|
|
|
501
|
-
function
|
|
502
|
-
return
|
|
503
|
-
}
|
|
504
|
-
|
|
505
|
-
function isNimApiKeyEnvValue(value: string): boolean {
|
|
506
|
-
return NVIDIA_API_KEY_ENV_NAMES.some((envName) => process.env[envName]?.trim() === value);
|
|
507
|
+
function getNimProviderApiKeyConfig(): string {
|
|
508
|
+
return `$${getNimApiKeyEnv() ?? NVIDIA_NIM_API_KEY_ENV}`;
|
|
507
509
|
}
|
|
508
510
|
|
|
509
|
-
function
|
|
510
|
-
return (
|
|
511
|
-
typeof credential === "object" &&
|
|
512
|
-
credential !== null &&
|
|
513
|
-
(credential as { type?: unknown }).type === "api_key" &&
|
|
514
|
-
typeof (credential as { key?: unknown }).key === "string"
|
|
515
|
-
);
|
|
516
|
-
}
|
|
517
|
-
|
|
518
|
-
function readStoredNimApiKeyConfig(): string | undefined {
|
|
519
|
-
try {
|
|
520
|
-
const authPath = join(getAgentDir(), "auth.json");
|
|
521
|
-
if (!existsSync(authPath)) return undefined;
|
|
522
|
-
|
|
523
|
-
const data = JSON.parse(readFileSync(authPath, "utf-8")) as Record<string, unknown>;
|
|
524
|
-
const credential = data[PROVIDER_NAME];
|
|
525
|
-
return isNimApiKeyCredential(credential) ? credential.key : undefined;
|
|
526
|
-
} catch {
|
|
527
|
-
return undefined;
|
|
528
|
-
}
|
|
511
|
+
function isNimApiKeyEnvName(value: string): value is NimApiKeyEnvName {
|
|
512
|
+
return NVIDIA_API_KEY_ENV_NAMES.includes(value as NimApiKeyEnvName);
|
|
529
513
|
}
|
|
530
514
|
|
|
531
|
-
function
|
|
532
|
-
|
|
533
|
-
try {
|
|
534
|
-
const credential = authStorage.get?.(PROVIDER_NAME);
|
|
535
|
-
return isNimApiKeyCredential(credential) ? credential.key : undefined;
|
|
536
|
-
} catch {
|
|
537
|
-
return undefined;
|
|
538
|
-
}
|
|
539
|
-
}
|
|
540
|
-
|
|
541
|
-
return readStoredNimApiKeyConfig();
|
|
515
|
+
function isNimApiKeyEnvReference(value: string): boolean {
|
|
516
|
+
return value.startsWith("$") && isNimApiKeyEnvName(value.slice(1));
|
|
542
517
|
}
|
|
543
518
|
|
|
544
|
-
function
|
|
545
|
-
return
|
|
519
|
+
function isNimApiKeyEnvPlaceholder(value: string): boolean {
|
|
520
|
+
return isNimApiKeyEnvName(value) || isNimApiKeyEnvReference(value);
|
|
546
521
|
}
|
|
547
522
|
|
|
548
|
-
function
|
|
549
|
-
|
|
550
|
-
if (!configuredApiKey || configuredApiKey.startsWith("!")) return undefined;
|
|
523
|
+
function resolveNimApiKeyEnvReference(value: string): string | undefined {
|
|
524
|
+
if (!isNimApiKeyEnvReference(value)) return undefined;
|
|
551
525
|
|
|
552
|
-
const envValue = process.env[
|
|
553
|
-
return envValue ||
|
|
526
|
+
const envValue = process.env[value.slice(1)]?.trim();
|
|
527
|
+
return envValue || undefined;
|
|
554
528
|
}
|
|
555
529
|
|
|
556
530
|
function normalizeResolvedNimApiKey(apiKey: string | undefined): string | undefined {
|
|
@@ -564,21 +538,19 @@ function normalizeResolvedNimApiKey(apiKey: string | undefined): string | undefi
|
|
|
564
538
|
return trimmed;
|
|
565
539
|
}
|
|
566
540
|
|
|
567
|
-
function resolveNimApiKey(apiKey: string | undefined
|
|
541
|
+
function resolveNimApiKey(apiKey: string | undefined): string | undefined {
|
|
568
542
|
const resolvedApiKey = normalizeResolvedNimApiKey(apiKey);
|
|
569
|
-
const hasStoredCommandCredential = hasStoredNimCommandCredential(authStorage);
|
|
570
543
|
|
|
571
|
-
if (
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
) {
|
|
575
|
-
throw new Error("NVIDIA NIM API key command resolved to an empty value.");
|
|
576
|
-
}
|
|
544
|
+
if (resolvedApiKey) {
|
|
545
|
+
const envReferenceApiKey = resolveNimApiKeyEnvReference(resolvedApiKey);
|
|
546
|
+
if (envReferenceApiKey) return envReferenceApiKey;
|
|
577
547
|
|
|
578
|
-
|
|
579
|
-
|
|
548
|
+
if (/^[A-Za-z_][A-Za-z0-9_]*$/.test(resolvedApiKey) && Object.hasOwn(process.env, resolvedApiKey)) {
|
|
549
|
+
return normalizeResolvedNimApiKey(process.env[resolvedApiKey]);
|
|
550
|
+
}
|
|
580
551
|
|
|
581
|
-
|
|
552
|
+
if (!isNimApiKeyEnvPlaceholder(resolvedApiKey)) return resolvedApiKey;
|
|
553
|
+
}
|
|
582
554
|
|
|
583
555
|
return getNimApiKey();
|
|
584
556
|
}
|
|
@@ -615,6 +587,20 @@ function buildThinkingKwargs(
|
|
|
615
587
|
return kwargs;
|
|
616
588
|
}
|
|
617
589
|
|
|
590
|
+
function buildNimRequestHeaders(headers: SimpleStreamOptions["headers"], apiKey: string): Record<string, string> {
|
|
591
|
+
const resolvedHeaders: Record<string, string> = {};
|
|
592
|
+
|
|
593
|
+
for (const [key, value] of Object.entries(headers ?? {})) {
|
|
594
|
+
if (key.toLowerCase() === "authorization" || value === null) continue;
|
|
595
|
+
resolvedHeaders[key] = value;
|
|
596
|
+
}
|
|
597
|
+
|
|
598
|
+
return {
|
|
599
|
+
...resolvedHeaders,
|
|
600
|
+
Authorization: `Bearer ${apiKey}`,
|
|
601
|
+
};
|
|
602
|
+
}
|
|
603
|
+
|
|
618
604
|
function nimStreamSimple(
|
|
619
605
|
model: Model<Api>,
|
|
620
606
|
context: Context,
|
|
@@ -632,9 +618,9 @@ function nimStreamSimple(
|
|
|
632
618
|
const reasoning = options?.reasoning;
|
|
633
619
|
const isThinkingEnabled = !!reasoning;
|
|
634
620
|
|
|
635
|
-
//
|
|
636
|
-
//
|
|
637
|
-
const mappedReasoning = mapNimTopLevelReasoning(reasoning);
|
|
621
|
+
// Custom NIM thinking configs use the provider-wide effort mapping. Models that
|
|
622
|
+
// declare native chat-template compatibility keep their own named levels.
|
|
623
|
+
const mappedReasoning = thinkingConfig ? mapNimTopLevelReasoning(reasoning) : reasoning;
|
|
638
624
|
|
|
639
625
|
// For models that have a thinking config: we handle thinking via chat_template_kwargs.
|
|
640
626
|
// Suppress reasoning_effort (set reasoning to undefined) unless the model explicitly
|
|
@@ -654,6 +640,7 @@ function nimStreamSimple(
|
|
|
654
640
|
...options,
|
|
655
641
|
reasoning: effectiveReasoning,
|
|
656
642
|
apiKey: nimApiKey,
|
|
643
|
+
headers: buildNimRequestHeaders(options?.headers, nimApiKey),
|
|
657
644
|
onPayload: (params: unknown) => {
|
|
658
645
|
const p = params as Record<string, unknown>;
|
|
659
646
|
|
|
@@ -705,6 +692,7 @@ interface NimModelEntry {
|
|
|
705
692
|
id: string;
|
|
706
693
|
name: string;
|
|
707
694
|
reasoning: boolean;
|
|
695
|
+
thinkingLevelMap?: ThinkingLevelMap;
|
|
708
696
|
input: ("text" | "image")[];
|
|
709
697
|
contextWindow: number;
|
|
710
698
|
maxTokens: number;
|
|
@@ -750,6 +738,35 @@ function buildModelEntry(modelId: string): NimModelEntry | null {
|
|
|
750
738
|
maxTokensField: "max_tokens",
|
|
751
739
|
};
|
|
752
740
|
|
|
741
|
+
// Inkling conditions reasoning through its chat template rather than a top-level
|
|
742
|
+
// reasoning_effort field. NVIDIA NIM accepts the same named effort presets as
|
|
743
|
+
// Inkling's tokenizer; map pi's extended levels to Inkling's 0.99 preset.
|
|
744
|
+
if (modelId === INKLING_MODEL_ID) {
|
|
745
|
+
entry.thinkingLevelMap = { off: "none", xhigh: "max", max: "max" };
|
|
746
|
+
entry.compat.thinkingFormat = "chat-template";
|
|
747
|
+
entry.compat.chatTemplateKwargs = {
|
|
748
|
+
reasoning_effort: { $var: "thinking.effort" },
|
|
749
|
+
};
|
|
750
|
+
}
|
|
751
|
+
|
|
752
|
+
// MiniMax M3 exposes discrete disabled/adaptive/enabled reasoning modes through
|
|
753
|
+
// chat_template_kwargs.thinking_mode. Map pi's effort scale to the closest mode.
|
|
754
|
+
if (modelId === MINIMAX_M3_MODEL_ID) {
|
|
755
|
+
entry.thinkingLevelMap = {
|
|
756
|
+
off: "disabled",
|
|
757
|
+
minimal: "adaptive",
|
|
758
|
+
low: "adaptive",
|
|
759
|
+
medium: "adaptive",
|
|
760
|
+
high: "enabled",
|
|
761
|
+
xhigh: "enabled",
|
|
762
|
+
max: "enabled",
|
|
763
|
+
};
|
|
764
|
+
entry.compat.thinkingFormat = "chat-template";
|
|
765
|
+
entry.compat.chatTemplateKwargs = {
|
|
766
|
+
thinking_mode: { $var: "thinking.effort" },
|
|
767
|
+
};
|
|
768
|
+
}
|
|
769
|
+
|
|
753
770
|
// Mistral models on NIM need extra compat flags
|
|
754
771
|
if (modelId.startsWith("mistralai/")) {
|
|
755
772
|
entry.compat.requiresToolResultName = true;
|
|
@@ -781,14 +798,17 @@ function sanitizeNimLogMessage(message: string): string {
|
|
|
781
798
|
return message.replace(/nvapi-[A-Za-z0-9._-]+/g, "nvapi-[REDACTED]");
|
|
782
799
|
}
|
|
783
800
|
|
|
784
|
-
function notifyNimDiscoveryCredentialWarning(ctx:
|
|
785
|
-
ctx
|
|
801
|
+
function notifyNimDiscoveryCredentialWarning(ctx: ExtensionContext): void {
|
|
802
|
+
ctx.ui.notify(NIM_DISCOVERY_CREDENTIAL_WARNING, "warning");
|
|
786
803
|
}
|
|
787
804
|
|
|
788
|
-
async function resolveNimDiscoveryApiKey(ctx:
|
|
805
|
+
async function resolveNimDiscoveryApiKey(ctx: ExtensionContext): Promise<string | undefined> {
|
|
789
806
|
try {
|
|
790
|
-
const apiKey = await ctx
|
|
791
|
-
|
|
807
|
+
const apiKey = await ctx.modelRegistry.getApiKeyForProvider(PROVIDER_NAME);
|
|
808
|
+
if (apiKey === undefined && ctx.modelRegistry.getProviderAuthStatus(PROVIDER_NAME).configured) {
|
|
809
|
+
throw new Error("NVIDIA NIM configured credential resolved to an empty value.");
|
|
810
|
+
}
|
|
811
|
+
return resolveNimApiKey(apiKey);
|
|
792
812
|
} catch (error) {
|
|
793
813
|
const message = error instanceof Error ? error.message : String(error);
|
|
794
814
|
console.warn(`pi-nvidia-nim: ${sanitizeNimLogMessage(message)}`);
|
|
@@ -834,7 +854,7 @@ async function fetchNimModels(apiKey: string): Promise<NimModelFetchResult> {
|
|
|
834
854
|
// =============================================================================
|
|
835
855
|
|
|
836
856
|
export default function (pi: ExtensionAPI) {
|
|
837
|
-
const providerApiKeyConfig =
|
|
857
|
+
const providerApiKeyConfig = getNimProviderApiKeyConfig();
|
|
838
858
|
|
|
839
859
|
// Always register the curated model list. The request path resolves credentials
|
|
840
860
|
// through pi first (CLI override, auth.json, shell command), then falls back to
|
|
@@ -863,7 +883,7 @@ export default function (pi: ExtensionAPI) {
|
|
|
863
883
|
});
|
|
864
884
|
|
|
865
885
|
// On session start, discover additional models from the API
|
|
866
|
-
pi.on("session_start", async (_event
|
|
886
|
+
pi.on("session_start", async (_event, ctx) => {
|
|
867
887
|
const apiKey = await resolveNimDiscoveryApiKey(ctx);
|
|
868
888
|
if (!apiKey) return;
|
|
869
889
|
|
|
@@ -889,15 +909,12 @@ export default function (pi: ExtensionAPI) {
|
|
|
889
909
|
}
|
|
890
910
|
}
|
|
891
911
|
|
|
892
|
-
//
|
|
893
|
-
// NOTE: must use ctx.modelRegistry.registerProvider() here, not pi.registerProvider().
|
|
894
|
-
// pi.registerProvider() only queues registrations for the initial extension load.
|
|
895
|
-
// From event handlers/commands, we need to call the registry directly.
|
|
912
|
+
// Runtime provider registrations now apply immediately through the extension API.
|
|
896
913
|
if (newModelsAdded > 0) {
|
|
897
914
|
const allModels = Array.from(modelMap.values());
|
|
898
|
-
|
|
915
|
+
pi.registerProvider(PROVIDER_NAME, {
|
|
899
916
|
baseUrl: NVIDIA_NIM_BASE_URL,
|
|
900
|
-
apiKey:
|
|
917
|
+
apiKey: getNimProviderApiKeyConfig(),
|
|
901
918
|
api: "openai-completions",
|
|
902
919
|
authHeader: true,
|
|
903
920
|
models: allModels,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pi-nvidia-nim",
|
|
3
|
-
"version": "1.1.
|
|
3
|
+
"version": "1.1.23",
|
|
4
4
|
"description": "NVIDIA NIM API provider extension for pi coding agent — access 100+ models from build.nvidia.com",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"files": [
|
|
@@ -23,9 +23,14 @@
|
|
|
23
23
|
"test": "node --test test/*.test.mjs"
|
|
24
24
|
},
|
|
25
25
|
"devDependencies": {
|
|
26
|
-
"@earendil-works/pi-ai": "^0.
|
|
27
|
-
"@earendil-works/pi-coding-agent": "^0.
|
|
28
|
-
"@types/node": "^
|
|
26
|
+
"@earendil-works/pi-ai": "^0.80.10",
|
|
27
|
+
"@earendil-works/pi-coding-agent": "^0.80.10",
|
|
28
|
+
"@types/node": "^24.0.0",
|
|
29
|
+
"typescript": "^6.0.3"
|
|
30
|
+
},
|
|
31
|
+
"peerDependencies": {
|
|
32
|
+
"@earendil-works/pi-ai": "*",
|
|
33
|
+
"@earendil-works/pi-coding-agent": "*"
|
|
29
34
|
},
|
|
30
35
|
"repository": {
|
|
31
36
|
"type": "git",
|