tokenfactory-pi 0.2.4 → 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/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,6 +1,6 @@
1
1
  {
2
2
  "name": "tokenfactory-pi",
3
- "version": "0.2.4",
3
+ "version": "0.2.5",
4
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",