tokenfactory-pi 0.1.0

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.
@@ -0,0 +1,5 @@
1
+ <component name="InspectionProjectProfileManager">
2
+ <settings>
3
+ <option name="PROJECT_PROFILE" />
4
+ </settings>
5
+ </component>
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 Dmitry Orlov
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,39 @@
1
+ # tokenfactory-pi
2
+
3
+ [Nebius Token Factory](https://tokenfactory.nebius.com/) provider extension for [pi coding agent](https://pi.dev).
4
+
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
+
7
+ ## Setup
8
+
9
+ ```bash
10
+ # Get an API key from https://tokenfactory.nebius.com/
11
+ export NEBIUS_API_KEY=your-key-here
12
+ ```
13
+
14
+ ## Usage
15
+
16
+ ```bash
17
+ # Load the extension
18
+ pi -e tokenfactory-pi
19
+
20
+ # Specify provider explicitly
21
+ pi -e tokenfactory-pi --provider nebius
22
+
23
+ # Pick a specific model
24
+ pi -e tokenfactory-pi --provider nebius --model Qwen/Qwen3-32B
25
+ ```
26
+
27
+ Once running, use `/nebius-models` to list all available models.
28
+
29
+ ## How it works
30
+
31
+ On startup the extension:
32
+
33
+ 1. Reads `NEBIUS_API_KEY` from environment (no-op if missing)
34
+ 2. Fetches `GET /v1/models?verbose=true` from the Token Factory API
35
+ 3. Filters for models with `tools` support and `->text` output modality
36
+ 4. Registers them as the `nebius` provider via `pi.registerProvider()`
37
+
38
+ All models use the `openai-completions` API with
39
+ `compat: { supportsDeveloperRole: false, maxTokensField: "max_tokens" }`.
package/index.ts ADDED
@@ -0,0 +1,146 @@
1
+ /**
2
+ * Nebius Token Factory — pi extension
3
+ *
4
+ * Fetches the current model catalog from the Token Factory API on startup
5
+ * and registers all tool-capable text-generation models as a "nebius" provider.
6
+ *
7
+ * Environment:
8
+ * NEBIUS_API_KEY — required, Token Factory API key
9
+ *
10
+ * Usage:
11
+ * pi -e /path/to/tokenfactory-pi
12
+ * pi -e /path/to/tokenfactory-pi --provider nebius
13
+ * pi -e /path/to/tokenfactory-pi --provider nebius --model Qwen/Qwen3-32B
14
+ */
15
+
16
+ import type { ExtensionAPI } from "@mariozechner/pi-coding-agent";
17
+
18
+ const PROVIDER_NAME = "nebius";
19
+ const BASE_URL = "https://api.tokenfactory.nebius.com/v1";
20
+ const ENV_VAR = "NEBIUS_API_KEY";
21
+
22
+ // ============================================================================
23
+ // Token Factory API types
24
+ // ============================================================================
25
+
26
+ interface TokenFactoryModel {
27
+ id: string;
28
+ name?: string;
29
+ context_length?: number;
30
+ supported_features?: string[];
31
+ architecture?: { modality?: string };
32
+ pricing?: { prompt?: string; completion?: string };
33
+ }
34
+
35
+ interface TokenFactoryResponse {
36
+ data: TokenFactoryModel[];
37
+ }
38
+
39
+ // ============================================================================
40
+ // Helpers
41
+ // ============================================================================
42
+
43
+ function isToolCapableTextModel(m: TokenFactoryModel): boolean {
44
+ const features = m.supported_features || [];
45
+ const modality = m.architecture?.modality || "";
46
+ return features.includes("tools") && modality.includes("->text");
47
+ }
48
+
49
+ function parseInputModalities(modality: string): ("text" | "image")[] {
50
+ const input: ("text" | "image")[] = ["text"];
51
+ if (modality.includes("image")) input.push("image");
52
+ return input;
53
+ }
54
+
55
+ function parseCostPerMillion(raw: string | undefined): number {
56
+ return parseFloat(raw || "0") * 1_000_000;
57
+ }
58
+
59
+ function isReasoningModel(id: string): boolean {
60
+ return /(-R1|-Thinking|QwQ)/.test(id);
61
+ }
62
+
63
+ // ============================================================================
64
+ // Extension entry point
65
+ // ============================================================================
66
+
67
+ export default async function (pi: ExtensionAPI) {
68
+ const apiKey = process.env[ENV_VAR];
69
+ if (!apiKey) {
70
+ return;
71
+ }
72
+
73
+ let response: TokenFactoryResponse;
74
+ try {
75
+ const res = await fetch(`${BASE_URL}/models?verbose=true`, {
76
+ headers: { Authorization: `Bearer ${apiKey}` },
77
+ });
78
+ if (!res.ok) {
79
+ console.warn(`[${PROVIDER_NAME}] API returned ${res.status}: ${res.statusText}`);
80
+ return;
81
+ }
82
+ response = (await res.json()) as TokenFactoryResponse;
83
+ } catch (error) {
84
+ console.warn(`[${PROVIDER_NAME}] Failed to fetch models:`, error);
85
+ return;
86
+ }
87
+
88
+ if (!Array.isArray(response.data)) {
89
+ console.warn(`[${PROVIDER_NAME}] Unexpected API response shape`);
90
+ return;
91
+ }
92
+
93
+ const models = [];
94
+ for (const m of response.data) {
95
+ if (!isToolCapableTextModel(m)) continue;
96
+
97
+ const modality = m.architecture?.modality || "";
98
+
99
+ models.push({
100
+ id: m.id,
101
+ name: m.name || m.id,
102
+ reasoning: isReasoningModel(m.id),
103
+ input: parseInputModalities(modality),
104
+ cost: {
105
+ input: parseCostPerMillion(m.pricing?.prompt),
106
+ output: parseCostPerMillion(m.pricing?.completion),
107
+ cacheRead: 0,
108
+ cacheWrite: 0,
109
+ },
110
+ contextWindow: m.context_length || 131072,
111
+ maxTokens: Math.min(m.context_length || 32768, 32768),
112
+ compat: {
113
+ supportsDeveloperRole: false,
114
+ maxTokensField: "max_tokens" as const,
115
+ },
116
+ });
117
+ }
118
+
119
+ pi.registerProvider(PROVIDER_NAME, {
120
+ baseUrl: BASE_URL,
121
+ apiKey: ENV_VAR,
122
+ api: "openai-completions",
123
+ models,
124
+ });
125
+
126
+ // /nebius-models command to list and select a model
127
+ pi.registerCommand("nebius-models", {
128
+ description: "List available Nebius Token Factory models",
129
+ handler: async (_args, ctx) => {
130
+ if (models.length === 0) {
131
+ ctx.ui.notify("No Nebius models available", "warning");
132
+ return;
133
+ }
134
+ const items = models
135
+ .sort((a, b) => a.id.localeCompare(b.id))
136
+ .map((m) => {
137
+ const tags = [];
138
+ if (m.reasoning) tags.push("reasoning");
139
+ if (m.input.includes("image")) tags.push("vision");
140
+ const suffix = tags.length > 0 ? ` (${tags.join(", ")})` : "";
141
+ return `${m.id}${suffix}`;
142
+ });
143
+ await ctx.ui.select(`Nebius Token Factory — ${models.length} models`, items);
144
+ },
145
+ });
146
+ }
package/package.json ADDED
@@ -0,0 +1,34 @@
1
+ {
2
+ "name": "tokenfactory-pi",
3
+ "version": "0.1.0",
4
+ "description": "Nebius Token Factory provider extension for pi coding agent",
5
+ "main": "index.ts",
6
+ "type": "module",
7
+ "license": "MIT",
8
+ "repository": {
9
+ "type": "git",
10
+ "url": "git+https://github.com/mosquito/tokenfactory-pi.git"
11
+ },
12
+ "keywords": [
13
+ "pi",
14
+ "pi-extension",
15
+ "pi-package",
16
+ "nebius",
17
+ "tokenfactory",
18
+ "llm",
19
+ "coding-agent"
20
+ ],
21
+ "pi": {
22
+ "extensions": [
23
+ "./index.ts"
24
+ ]
25
+ },
26
+ "peerDependencies": {
27
+ "@mariozechner/pi-coding-agent": "*"
28
+ },
29
+ "scripts": {
30
+ "clean": "echo 'nothing to clean'",
31
+ "build": "echo 'nothing to build'",
32
+ "check": "echo 'nothing to check'"
33
+ }
34
+ }