tokenfactory-pi 0.2.2 → 0.2.5

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
@@ -4,6 +4,20 @@
4
4
 
5
5
  Fetches the current model catalog from the Token Factory API on startup and registers all tool-capable models. No changes to pi-mono required.
6
6
 
7
+ ## Prerequisites
8
+
9
+ ```bash
10
+ # Install pi coding agent globally (required)
11
+ npm install -g @mariozechner/pi-coding-agent
12
+ ```
13
+
14
+ ## Installation
15
+
16
+ ```bash
17
+ # Install the extension using pi's built-in package manager
18
+ pi install npm:tokenfactory-pi
19
+ ```
20
+
7
21
  ## Setup
8
22
 
9
23
  ```bash
@@ -14,17 +28,23 @@ export NEBIUS_API_KEY=your-key-here
14
28
  ## Usage
15
29
 
16
30
  ```bash
17
- # Load the extension
18
- pi -e tokenfactory-pi
31
+ # List available models to verify installation
32
+ pi --list-models | grep nebius
33
+ ```
19
34
 
20
- # Specify provider explicitly
21
- pi -e tokenfactory-pi --provider nebius
35
+ Once running in interactive mode, use `/nebius-models` to list all available models.
22
36
 
23
- # Pick a specific model
24
- pi -e tokenfactory-pi --provider nebius --model Qwen/Qwen3-32B
25
- ```
37
+ ## Development
26
38
 
27
- Once running, use `/nebius-models` to list all available models.
39
+ For local development:
40
+ ```bash
41
+ # Build the TypeScript
42
+ npm run build
43
+
44
+ # Test locally from the project directory
45
+ cd path/to/tokenfactory-pi
46
+ pi -e . --provider nebius
47
+ ```
28
48
 
29
49
  ## How it works
30
50
 
package/dist/index.d.ts CHANGED
@@ -1,11 +1,11 @@
1
1
  /**
2
- * Nebius Token Factory pi extension
2
+ * Nebius Token Factory - pi extension
3
3
  *
4
4
  * Fetches the current model catalog from the Token Factory API on startup
5
5
  * and registers all tool-capable text-generation models as a "nebius" provider.
6
6
  *
7
7
  * Environment:
8
- * NEBIUS_API_KEY required, Token Factory API key
8
+ * NEBIUS_API_KEY - required, Token Factory API key
9
9
  *
10
10
  * Usage:
11
11
  * pi -e /path/to/tokenfactory-pi
package/dist/index.js CHANGED
@@ -1,11 +1,11 @@
1
1
  /**
2
- * Nebius Token Factory pi extension
2
+ * Nebius Token Factory - pi extension
3
3
  *
4
4
  * Fetches the current model catalog from the Token Factory API on startup
5
5
  * and registers all tool-capable text-generation models as a "nebius" provider.
6
6
  *
7
7
  * Environment:
8
- * NEBIUS_API_KEY required, Token Factory API key
8
+ * NEBIUS_API_KEY - required, Token Factory API key
9
9
  *
10
10
  * Usage:
11
11
  * pi -e /path/to/tokenfactory-pi
@@ -18,10 +18,9 @@ const ENV_VAR = "NEBIUS_API_KEY";
18
18
  // ============================================================================
19
19
  // Helpers
20
20
  // ============================================================================
21
- function isToolCapableTextModel(m) {
22
- const features = m.supported_features || [];
21
+ function isTextModel(m) {
23
22
  const modality = m.architecture?.modality || "";
24
- return features.includes("tools") && modality.includes("->text");
23
+ return modality.includes("->text");
25
24
  }
26
25
  function parseInputModalities(modality) {
27
26
  const input = ["text"];
@@ -30,7 +29,8 @@ function parseInputModalities(modality) {
30
29
  return input;
31
30
  }
32
31
  function parseCostPerMillion(raw) {
33
- return parseFloat(raw || "0") * 1_000_000;
32
+ const parsed = parseFloat(raw || "0");
33
+ return isNaN(parsed) ? 0 : parsed * 1_000_000;
34
34
  }
35
35
  function isReasoningModel(id) {
36
36
  return /(-R1|-Thinking|QwQ)/.test(id);
@@ -50,6 +50,7 @@ export default async function (pi) {
50
50
  });
51
51
  if (!res.ok) {
52
52
  console.warn(`[${PROVIDER_NAME}] API returned ${res.status}: ${res.statusText}`);
53
+ console.warn(`[${PROVIDER_NAME}] Response headers:`, [...res.headers.entries()]);
53
54
  return;
54
55
  }
55
56
  response = (await res.json());
@@ -59,18 +60,34 @@ export default async function (pi) {
59
60
  return;
60
61
  }
61
62
  if (!Array.isArray(response.data)) {
62
- console.warn(`[${PROVIDER_NAME}] Unexpected API response shape`);
63
+ console.warn(`[${PROVIDER_NAME}] Unexpected API response shape. Expected array, got:`, typeof response.data);
64
+ console.warn(`[${PROVIDER_NAME}] Response data:`, response);
63
65
  return;
64
66
  }
65
67
  const models = [];
66
68
  for (const m of response.data) {
67
- if (!isToolCapableTextModel(m))
69
+ // Skip models without valid IDs
70
+ if (!m.id || m.id.trim() === "") {
71
+ console.log(`[${PROVIDER_NAME}] Skipping model with empty ID:`, m);
68
72
  continue;
73
+ }
74
+ if (!isTextModel(m)) {
75
+ continue;
76
+ }
69
77
  const modality = m.architecture?.modality || "";
78
+ // Validate and set defaults for critical fields
79
+ const contextLength = m.context_length && m.context_length > 0 ? m.context_length : 131072;
80
+ const modelName = m.name || m.id || "unknown-model";
81
+ const modelId = m.id || modelName;
82
+ // Skip models with zero context length even after defaults
83
+ if (contextLength <= 0) {
84
+ console.log(`[${PROVIDER_NAME}] Skipping model with invalid context length:`, m.id, m.context_length);
85
+ continue;
86
+ }
70
87
  models.push({
71
- id: m.id,
72
- name: m.name || m.id,
73
- reasoning: isReasoningModel(m.id),
88
+ id: modelId,
89
+ name: modelName,
90
+ reasoning: isReasoningModel(modelId),
74
91
  input: parseInputModalities(modality),
75
92
  cost: {
76
93
  input: parseCostPerMillion(m.pricing?.prompt),
@@ -78,8 +95,8 @@ export default async function (pi) {
78
95
  cacheRead: 0,
79
96
  cacheWrite: 0,
80
97
  },
81
- contextWindow: m.context_length || 131072,
82
- maxTokens: Math.min(m.context_length || 32768, 32768),
98
+ contextWindow: contextLength,
99
+ maxTokens: Math.min(contextLength, 32768),
83
100
  compat: {
84
101
  supportsDeveloperRole: false,
85
102
  maxTokensField: "max_tokens",
@@ -101,7 +118,7 @@ export default async function (pi) {
101
118
  return;
102
119
  }
103
120
  const items = models
104
- .sort((a, b) => a.id.localeCompare(b.id))
121
+ .sort((a, b) => b.cost.output - a.cost.output)
105
122
  .map((m) => {
106
123
  const tags = [];
107
124
  if (m.reasoning)
@@ -111,7 +128,33 @@ export default async function (pi) {
111
128
  const suffix = tags.length > 0 ? ` (${tags.join(", ")})` : "";
112
129
  return `${m.id}${suffix}`;
113
130
  });
114
- await ctx.ui.select(`Nebius Token Factory ${models.length} models`, items);
131
+ // Show the selection dialog and capture the result
132
+ const selectedItem = await ctx.ui.select(`Nebius Token Factory - ${models.length} models`, items);
133
+ // If user cancelled (selectedItem is undefined), do nothing
134
+ if (!selectedItem) {
135
+ ctx.ui.notify("Model selection cancelled", "warning");
136
+ return;
137
+ }
138
+ // Extract the model ID from the selection (remove the suffix)
139
+ const selectedId = selectedItem.split(" (")[0];
140
+ const selectedModel = models.find(m => m.id === selectedId);
141
+ if (!selectedModel) {
142
+ ctx.ui.notify(`Model not found: ${selectedId}`, "error");
143
+ return;
144
+ }
145
+ // Find the model in the registry and switch to it
146
+ const model = ctx.modelRegistry.find(PROVIDER_NAME, selectedModel.id);
147
+ if (!model) {
148
+ ctx.ui.notify(`Model not found in registry: ${PROVIDER_NAME}/${selectedModel.id}`, "error");
149
+ return;
150
+ }
151
+ const success = await pi.setModel(model);
152
+ if (success) {
153
+ ctx.ui.notify(`Switched to model: ${model.provider}/${model.id}`, "info");
154
+ }
155
+ else {
156
+ ctx.ui.notify(`Failed to switch to model: ${model.provider}/${model.id}. Check API key?`, "error");
157
+ }
115
158
  },
116
159
  });
117
160
  }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "tokenfactory-pi",
3
- "version": "0.2.2",
4
- "description": "Nebius Token Factory provider extension for pi coding agent",
3
+ "version": "0.2.5",
4
+ "description": "Nebius Token Factory provider extension for pi coding agent. Requires `npm install -g @mariozechner/pi-coding-agent`. Install with `pi install npm:tokenfactory-pi`",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
7
7
  "type": "module",