pi-nebius 0.3.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.
- package/CHANGELOG.md +49 -0
- package/CONTRIBUTING.md +39 -0
- package/LICENSE +21 -0
- package/README.md +239 -0
- package/SECURITY.md +47 -0
- package/benchmarks/add-api-endpoint/benchmark.yaml +13 -0
- package/benchmarks/add-api-endpoint/fixture/app.mjs +6 -0
- package/benchmarks/add-api-endpoint/fixture/app.test.mjs +8 -0
- package/benchmarks/add-api-endpoint/fixture/package.json +8 -0
- package/benchmarks/add-api-endpoint/validation/check.test.mjs +37 -0
- package/benchmarks/fix-auth-bug/benchmark.yaml +14 -0
- package/benchmarks/fix-auth-bug/fixture/auth.mjs +4 -0
- package/benchmarks/fix-auth-bug/fixture/auth.test.mjs +26 -0
- package/benchmarks/fix-auth-bug/fixture/package.json +8 -0
- package/benchmarks/fix-auth-bug/validation/check.test.mjs +25 -0
- package/benchmarks/multi-file-feature/benchmark.yaml +15 -0
- package/benchmarks/multi-file-feature/fixture/package.json +8 -0
- package/benchmarks/multi-file-feature/fixture/routes.mjs +11 -0
- package/benchmarks/multi-file-feature/fixture/routes.test.mjs +14 -0
- package/benchmarks/multi-file-feature/fixture/serialize.mjs +3 -0
- package/benchmarks/multi-file-feature/fixture/store.mjs +10 -0
- package/benchmarks/multi-file-feature/validation/check.test.mjs +52 -0
- package/benchmarks/refactor-module/benchmark.yaml +12 -0
- package/benchmarks/refactor-module/fixture/invoice.mjs +8 -0
- package/benchmarks/refactor-module/fixture/invoice.test.mjs +9 -0
- package/benchmarks/refactor-module/fixture/package.json +8 -0
- package/benchmarks/refactor-module/validation/check.test.mjs +36 -0
- package/dist/benchmark/cli.js +112 -0
- package/dist/benchmark/command.js +194 -0
- package/dist/benchmark/definition.js +109 -0
- package/dist/benchmark/host-worker.js +14 -0
- package/dist/benchmark/instrumentation.js +296 -0
- package/dist/benchmark/metrics.js +78 -0
- package/dist/benchmark/process.js +122 -0
- package/dist/benchmark/project.js +70 -0
- package/dist/benchmark/report.js +94 -0
- package/dist/benchmark/runner.js +376 -0
- package/dist/benchmark/types.js +1 -0
- package/dist/benchmark/worker.js +134 -0
- package/dist/benchmark/workspace.js +55 -0
- package/dist/discovery.js +154 -0
- package/dist/errors.js +32 -0
- package/dist/index.js +86 -0
- package/dist/model-settings-command.js +130 -0
- package/dist/model-settings.js +101 -0
- package/dist/models.js +62 -0
- package/dist/provider.js +48 -0
- package/docs/benchmark-research.md +35 -0
- package/docs/benchmarking.md +253 -0
- package/docs/security-review.md +49 -0
- package/docs/validation.md +51 -0
- package/examples/models.json +31 -0
- package/package.json +74 -0
- package/src/benchmark/cli.ts +118 -0
- package/src/benchmark/command.ts +218 -0
- package/src/benchmark/definition.ts +112 -0
- package/src/benchmark/host-worker.ts +14 -0
- package/src/benchmark/instrumentation.ts +298 -0
- package/src/benchmark/metrics.ts +101 -0
- package/src/benchmark/process.ts +120 -0
- package/src/benchmark/project.ts +71 -0
- package/src/benchmark/report.ts +111 -0
- package/src/benchmark/runner.ts +458 -0
- package/src/benchmark/types.ts +180 -0
- package/src/benchmark/worker.ts +150 -0
- package/src/benchmark/workspace.ts +64 -0
- package/src/discovery.ts +176 -0
- package/src/errors.ts +32 -0
- package/src/index.ts +96 -0
- package/src/model-settings-command.ts +151 -0
- package/src/model-settings.ts +129 -0
- package/src/models.ts +73 -0
- package/src/provider.ts +63 -0
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
Versions use Semantic Versioning. Entries describe changes included in the named version;
|
|
4
|
+
a version is released only when its matching Git tag and GitHub release are published.
|
|
5
|
+
|
|
6
|
+
## [0.3.0](https://github.com/PeterHdd/pi-nebius/compare/v0.2.1...v0.3.0) (2026-09-17)
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
### Features
|
|
10
|
+
|
|
11
|
+
* add persistent per-model Nebius settings ([d1b3114](https://github.com/PeterHdd/pi-nebius/commit/d1b31146abf17d54aa43d57f5c9080072cea78e6))
|
|
12
|
+
* add persistent per-model Nebius settings ([b31dce1](https://github.com/PeterHdd/pi-nebius/commit/b31dce1087e41e41482a56634f3dc5f57213bae4))
|
|
13
|
+
|
|
14
|
+
## [0.2.1](https://github.com/PeterHdd/pi-nebius/compare/v0.2.0...v0.2.1) (2026-09-17)
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
### Bug Fixes
|
|
18
|
+
|
|
19
|
+
* clarify auth benchmark requirements and expand visible test ([557271b](https://github.com/PeterHdd/pi-nebius/commit/557271b74b30e457cdeca7b3e3d491e84903eca4))
|
|
20
|
+
* clarify auth benchmark requirements and expand visible test ([3d86156](https://github.com/PeterHdd/pi-nebius/commit/3d86156f41677cdd2672fe6b5196e44d2830b5c6))
|
|
21
|
+
|
|
22
|
+
## [0.2.0](https://github.com/PeterHdd/pi-nebius/compare/v0.1.0...v0.2.0) (2026-09-17)
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
### Features
|
|
26
|
+
|
|
27
|
+
* add in-Pi benchmarks for custom prompts and bundled tasks ([615c800](https://github.com/PeterHdd/pi-nebius/commit/615c800975288242530a362ee7fd954e1ae08502))
|
|
28
|
+
* add Nebius provider and coding benchmarks for Pi ([a770213](https://github.com/PeterHdd/pi-nebius/commit/a7702139910c11afec35235fea785f1b805ad478))
|
|
29
|
+
* improve benchmark reporting and automate releases ([fbbca35](https://github.com/PeterHdd/pi-nebius/commit/fbbca35ecf46156616b74a4707d09f2fd85dbe8b))
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
### Bug Fixes
|
|
33
|
+
|
|
34
|
+
* **ci:** stabilize benchmark tests and target main branch only ([7ffc127](https://github.com/PeterHdd/pi-nebius/commit/7ffc127e90defa063fb6c66352385b2c9a3cfff1))
|
|
35
|
+
* support Pi Git installs without dev dependencies ([18ec13b](https://github.com/PeterHdd/pi-nebius/commit/18ec13b6afba14d3948409e89a358aa4eae0eab4))
|
|
36
|
+
|
|
37
|
+
## [0.1.0]
|
|
38
|
+
|
|
39
|
+
Development baseline before automated GitHub releases.
|
|
40
|
+
|
|
41
|
+
- Native Pi provider with authenticated Nebius model discovery and a private metadata cache.
|
|
42
|
+
- Agentic benchmark CLI with deterministic validation, isolated runs, request/tool traces,
|
|
43
|
+
cumulative usage and four coding fixtures.
|
|
44
|
+
- Credential-free integration tests using real Pi sessions and mocked provider responses.
|
|
45
|
+
- GitHub CI, dependency updates, secret scanning, workflow validation, and automated changelogs and GitHub releases.
|
|
46
|
+
- Filesystem checks reject overlapping validator paths and symlinked fixture roots/output aliases.
|
|
47
|
+
|
|
48
|
+
Live Nebius acceptance remains outstanding. Supported baseline: Pi 0.85.1, Node 22.19+,
|
|
49
|
+
macOS/Linux; benchmark execution is sequential.
|
package/CONTRIBUTING.md
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
# Contributing
|
|
2
|
+
|
|
3
|
+
Use Node 22.19+ on macOS or Linux. Pi development dependencies are locked to the
|
|
4
|
+
tested release; retain the lockfile and update both Pi packages together.
|
|
5
|
+
|
|
6
|
+
```bash
|
|
7
|
+
npm ci
|
|
8
|
+
npm run check
|
|
9
|
+
npm run version:check
|
|
10
|
+
npm run security:check
|
|
11
|
+
npm run package:check
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
`check` runs TypeScript, Biome, the build, and credential-free tests. Security checks
|
|
15
|
+
download checksum-pinned official tools; package checks install a temporary tarball
|
|
16
|
+
with the tested Pi peers. Both require network access. Tests need loopback sockets
|
|
17
|
+
and permission to launch/stop subprocesses. Do not provide live API keys to CI.
|
|
18
|
+
|
|
19
|
+
Keep changes focused and add regression tests for behavior changes. Use a descriptive
|
|
20
|
+
commit message; no special commit syntax or automatic version inference is required.
|
|
21
|
+
Update the changelog for user-visible changes. Report whether a test used mocks or
|
|
22
|
+
live Nebius; a scripted tool loop is not a hosted-model benchmark.
|
|
23
|
+
|
|
24
|
+
Never commit credentials, `.env` files, private fixtures, `node_modules`, or generated
|
|
25
|
+
benchmark results. The secret scanner scans Git-visible files and history, so a
|
|
26
|
+
credential committed and later deleted must still be revoked and removed from history.
|
|
27
|
+
|
|
28
|
+
Release Please generates changelog/version updates in a release PR; merging it publishes
|
|
29
|
+
the GitHub release after CI passes. Changes to workflow permissions,
|
|
30
|
+
tool checksum pins, dependency lockfiles, and authentication deserve particular review.
|
|
31
|
+
|
|
32
|
+
PR descriptions are maintained by the **PR description** workflow. Write meaningful
|
|
33
|
+
commit subjects and optional bullet points in commit bodies: these populate Changes.
|
|
34
|
+
Validation reports actual CI job/step outcomes for the latest PR commit and updates
|
|
35
|
+
when CI runs. Add context or live-testing evidence under Notes, outside the
|
|
36
|
+
`auto-pr` markers. The workflow preserves that text and leaves Release Please PRs
|
|
37
|
+
alone. It uses the built-in GitHub token; no additional secret or AI service is needed.
|
|
38
|
+
It becomes active after the workflow reaches the default branch. For existing PRs
|
|
39
|
+
without markers, it appends its generated section without deleting your description.
|
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 pi-nebius contributors
|
|
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,239 @@
|
|
|
1
|
+
# pi-nebius
|
|
2
|
+
|
|
3
|
+
Nebius Token Factory models in Pi's normal model picker, discovered using your API key.
|
|
4
|
+
|
|
5
|
+
This package also includes **benchmarks inside Pi**: run `/nebius-benchmark` to enter your own task prompt and compare models on copies of your current project. Results appear in Pi. A standalone CLI also supports scripted tasks and deterministic validation. See [Benchmarking](docs/benchmarking.md) for the CLI, result schema, metrics, and four ready-to-run fixtures.
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm ci
|
|
9
|
+
npm run build
|
|
10
|
+
npm run benchmark -- --benchmark benchmarks/fix-auth-bug \
|
|
11
|
+
--models 'EXACT_MODEL_ID_A,EXACT_MODEL_ID_B' --runs 3 \
|
|
12
|
+
--output benchmark-results/auth-comparison
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
Results appear directly in the terminal: each run prints its pass/fail status, followed by a comparison table of success counts, mean input/output tokens, median task duration, observed time to first token (TTFT), end-to-end output throughput, turns, and tool calls. Detailed JSON reports, traces, and workspaces are also saved under `benchmark-results/`; `--output` chooses the directory.
|
|
16
|
+
|
|
17
|
+
Set `NEBIUS_API_KEY` first. `npm run benchmark:demo` exercises the real Pi runner with scripted model responses, without credentials.
|
|
18
|
+
|
|
19
|
+
```text
|
|
20
|
+
Pi → pi-nebius → https://api.tokenfactory.nebius.com/v1 → model
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
Requires **Node.js 22.19+ and Pi 0.85.1** (the tested version). This targets the current `@earendil-works` Pi packages, not older `@mariozechner` releases. Pi's package instructions require wildcard peer dependencies; that is not a claim that every Pi version is compatible.
|
|
24
|
+
|
|
25
|
+
## Installation
|
|
26
|
+
|
|
27
|
+
With Pi already installed:
|
|
28
|
+
|
|
29
|
+
```bash
|
|
30
|
+
pi install npm:pi-nebius
|
|
31
|
+
export NEBIUS_API_KEY="your-api-key"
|
|
32
|
+
pi
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
Run `/model`, search for `nebius`, and select a model.
|
|
36
|
+
|
|
37
|
+
From this checkout:
|
|
38
|
+
|
|
39
|
+
```bash
|
|
40
|
+
cd /path/to/pi-nebius
|
|
41
|
+
npm ci
|
|
42
|
+
pi install /absolute/path/to/pi-nebius
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
Or try it without adding it to Pi's settings:
|
|
46
|
+
|
|
47
|
+
```bash
|
|
48
|
+
pi -e /absolute/path/to/pi-nebius
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
The provider uses Pi's `pi.extensions` manifest and loads TypeScript directly. Pi installs Git packages with `npm install --omit=dev`; the extension needs no build step. To use the benchmark CLI from a source checkout, run `npm ci` and `npm run build` to generate `dist/`. YAML is the benchmark definition parser; neither component needs a separate server.
|
|
52
|
+
|
|
53
|
+
GitHub installation is also supported:
|
|
54
|
+
|
|
55
|
+
```bash
|
|
56
|
+
pi install git:github.com/PeterHdd/pi-nebius
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
Git installs require no compiler or development dependencies. Benchmark development uses a separate source checkout with development dependencies installed. Append a published tag (for example, `@v0.3.0`) to pin a Git installation; install a newer tag explicitly to upgrade.
|
|
60
|
+
|
|
61
|
+
See [CONTRIBUTING.md](CONTRIBUTING.md), [CHANGELOG.md](CHANGELOG.md), and [SECURITY.md](SECURITY.md).
|
|
62
|
+
|
|
63
|
+
## Authentication and usage
|
|
64
|
+
|
|
65
|
+
```bash
|
|
66
|
+
export NEBIUS_API_KEY="your-key"
|
|
67
|
+
pi
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
Inside Pi, run `/model` and search for `nebius`. Choose an exact model from the discovered list. Provider ID: `nebius`; display name: `Nebius Token Factory`. Nebius IDs such as `vendor/model` remain unchanged; Pi identifies the pair as `nebius/vendor/model`.
|
|
71
|
+
|
|
72
|
+
You can also inspect the catalog and select a model explicitly:
|
|
73
|
+
|
|
74
|
+
```bash
|
|
75
|
+
pi --list-models nebius
|
|
76
|
+
pi --provider nebius --model 'EXACT_ID_FROM_THE_LIST'
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
`/nebius-refresh` forces discovery again and updates Pi's registered models. If a selected model was removed, use `/model` to choose a current one.
|
|
80
|
+
|
|
81
|
+
The extension reads `NEBIUS_API_KEY` at startup for discovery and resolves it through Pi's native authentication interface for inference. It has no `/login` flow and never writes the key to a file. Set the variable in the shell that launches Pi; changing a parent shell's environment cannot change an already-running process. Restart Pi after changing it. Pi's explicit CLI/runtime authentication overrides remain Pi features, but discovery specifically requires `NEBIUS_API_KEY`.
|
|
82
|
+
|
|
83
|
+
## Benchmark inside Pi
|
|
84
|
+
|
|
85
|
+
After installing the extension, run this inside Pi:
|
|
86
|
+
|
|
87
|
+
```text
|
|
88
|
+
/nebius-benchmark
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
An editor asks for your own task prompt. The benchmark uses your selected Nebius model and a copy of the project directory where Pi is running. To compare models, supply their exact IDs:
|
|
92
|
+
|
|
93
|
+
```text
|
|
94
|
+
/nebius-benchmark --models zai-org/GLM-5.3,moonshotai/Kimi-K2.6 --runs 3
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
This runs the same prompt three times per model, sequentially, starting from the same project snapshot each time. It shows progress and a comparison of input/output tokens, task duration, observed TTFT, output throughput, turns, and tools directly in Pi. Custom prompts report **correctness not checked**; completion does not prove the task was solved.
|
|
98
|
+
|
|
99
|
+
The snapshot includes current working files, including uncommitted changes, and respects Git ignores. It excludes `.git`, `.pi`, dependency/build folders, prior benchmark results, `.env` files, and `.pem`/`.key` files. Dependencies are not preinstalled; include setup instructions in your task if needed. Links and special files are rejected. Snapshots are limited to 10,000 files / 50 MiB. Run Pi from the project directory you want to benchmark.
|
|
100
|
+
|
|
101
|
+
Runs use paid inference. Tools have normal host permissions: file copies are not an OS sandbox. Reports and each run's resulting files are retained under `benchmark-results/` in your project.
|
|
102
|
+
|
|
103
|
+
```text
|
|
104
|
+
/nebius-benchmark cancel
|
|
105
|
+
/nebius-benchmark help
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
For an optional bundled coding task with automatic correctness tests:
|
|
109
|
+
|
|
110
|
+
```text
|
|
111
|
+
/nebius-benchmark --task fix-auth-bug --runs 3
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
Other bundled tasks: `add-api-endpoint`, `refactor-module`, and `multi-file-feature`. No build step or separate terminal is needed for these Pi commands.
|
|
115
|
+
|
|
116
|
+
## Dynamic model discovery
|
|
117
|
+
|
|
118
|
+
The async extension factory calls authenticated `GET /v1/models?verbose=true` before Pi finishes startup. This is the documented verbose variant of `/v1/models`, supplying metadata in addition to IDs. There is **no bundled model catalog**.
|
|
119
|
+
|
|
120
|
+
| Nebius field | Pi mapping |
|
|
121
|
+
| --- | --- |
|
|
122
|
+
| `id` | Exact, case-sensitive `model.id` |
|
|
123
|
+
| `name` | Display name; defaults to ID |
|
|
124
|
+
| `context_length` | `contextWindow`; defaults to 32,768 when absent/invalid |
|
|
125
|
+
| `architecture.modality` | Text/image inputs; known non-text-output models are excluded |
|
|
126
|
+
| `status` | Only `active` or unspecified models are included |
|
|
127
|
+
| `supported_sampling_parameters` contains `reasoning_effort` | Enables reasoning and Pi's standard reasoning-effort control |
|
|
128
|
+
| `supported_features` contains `reasoning` | Marks reasoning support without assuming an effort control |
|
|
129
|
+
| No documented maximum output length | `maxTokens`: 4,096, capped at a quarter of the context window |
|
|
130
|
+
|
|
131
|
+
Basic responses containing only IDs are supported. Missing capabilities default to text-only with no explicit reasoning control. Without modality metadata, the endpoint may include models unsuitable for chat; discovery cannot prove tool support. Pick a tool-capable model and run the integration test. Duplicate IDs are deduplicated; malformed lists are rejected rather than silently replacing a good cache with partial data.
|
|
132
|
+
|
|
133
|
+
### Cache and offline fallback
|
|
134
|
+
|
|
135
|
+
The cache is under `getAgentDir()/cache/pi-nebius/` (normally `~/.pi/agent/cache/pi-nebius/`). Pi's `PI_CODING_AGENT_DIR` override is respected. Files contain normalized metadata and a timestamp, never the API key. Names use a SHA-256 fingerprint of the endpoint and key to avoid sharing one account's catalog with another key.
|
|
136
|
+
|
|
137
|
+
- Fresh metadata is used for 24 hours without a discovery request.
|
|
138
|
+
- Stale/missing metadata triggers one request, with an 8-second timeout and a 4 MiB response limit.
|
|
139
|
+
- Transient failures use stale metadata and display a warning. No cache means no discovered models; Pi continues running.
|
|
140
|
+
- A 401/403 invalidates this key's cache. A fresh cache does not validate credentials; inference can still reject a revoked key.
|
|
141
|
+
- Successful refresh replaces the catalog, including an authoritative empty list. Failed refresh never updates the cache timestamp.
|
|
142
|
+
- Cache writes are atomic and best-effort, with private directory/file permissions. Concurrent Pi processes may each perform discovery; there is no background service or cross-process lock.
|
|
143
|
+
|
|
144
|
+
Pi now has a shared native model store, but its general provider-level cache is not scoped by the discovery key. This small extension-owned cache makes key isolation explicit and guarantees discovery before model selection. It does not participate in Pi's general remote-catalog refresh; use `/nebius-refresh` or restart Pi.
|
|
145
|
+
|
|
146
|
+
### Per-model settings
|
|
147
|
+
|
|
148
|
+
Run `/nebius-model` to choose a discovered model, or `/nebius-model MODEL_ID` to open it directly.
|
|
149
|
+
The menu saves temperature, reasoning effort, and maximum output tokens separately for each model.
|
|
150
|
+
Blank numeric values or **Inherit** restore defaults; **Reset all overrides** clears that model's settings.
|
|
151
|
+
Advanced settings let you override the context window and reasoning capability metadata. These do not
|
|
152
|
+
change the server's capabilities. Use verified limits; the default output limit remains 4,096 tokens
|
|
153
|
+
when the catalog does not provide a documented output limit.
|
|
154
|
+
|
|
155
|
+
Temperature and reasoning-effort overrides are sent only when discovery advertises support.
|
|
156
|
+
An advertised parameter does not guarantee every value is supported by every model; use its documented values.
|
|
157
|
+
Saved request values take precedence over Pi's generated values (including `/thinking` for reasoning effort).
|
|
158
|
+
Changes apply to new requests immediately and survive restarts in
|
|
159
|
+
`~/.pi/agent/pi-nebius/model-settings.json` (respecting `PI_CODING_AGENT_DIR`).
|
|
160
|
+
`/nebius-refresh` preserves saved overrides. External file edits require `/reload`.
|
|
161
|
+
|
|
162
|
+
Both benchmark entry points use saved settings. In-Pi benchmarks snapshot them when the run starts,
|
|
163
|
+
so changing the menu does not change an active comparison. Each run's `effectiveSettings` includes
|
|
164
|
+
saved overrides, model limits, and `requestParameters`: the outgoing temperature, reasoning effort,
|
|
165
|
+
output limit, and top-p where present, without prompt content. Omitted values mean the request did not
|
|
166
|
+
specify them; server defaults are unknown. These parameters are in `results.json`, not the summary table.
|
|
167
|
+
|
|
168
|
+
### Metadata overrides and user-defined fallback models
|
|
169
|
+
|
|
170
|
+
Merge the `nebius` provider entry from [examples/models.json](examples/models.json) into `~/.pi/agent/models.json`, replacing the placeholder ID and limits with verified values. Do not overwrite unrelated providers. The extension must remain installed.
|
|
171
|
+
|
|
172
|
+
Use `modelOverrides` to adjust discovered models and `models` to add explicit models that remain available when discovery is offline. Native providers are composed beneath these settings by Pi. No `apiKey` field is needed in this configuration. Explicitly configured models remain until you remove them, even if the server no longer lists them.
|
|
173
|
+
|
|
174
|
+
|
|
175
|
+
For reasoning models, verify the model's Nebius-specific behavior before overriding `reasoning`, `thinkingLevelMap`, or `compat`. Pi supports model-specific template controls, but this extension does not guess them from model names. Always-on reasoning may still appear in Pi even when explicit effort selection is unavailable.
|
|
176
|
+
|
|
177
|
+
## Architecture and compatibility
|
|
178
|
+
|
|
179
|
+
`src/index.ts` is Pi's async extension factory. It registers a native provider with `pi.registerProvider(provider)`, adds `/nebius-refresh`, and supplies provider-specific error guidance through `message_end`.
|
|
180
|
+
|
|
181
|
+
`src/provider.ts` uses Pi's `createProvider()` and its exported `openAICompletionsApi()` adapter. Pi owns request/message serialization, SSE parsing, multi-turn conversation state, function calls/results, usage accounting, abort handling, retries, and the tool loop. A small fetch decorator retains retry/rate-limit/request-ID headers on HTTP errors; successful response streams pass through unchanged.
|
|
182
|
+
|
|
183
|
+
Compatibility settings select `system` messages and `max_tokens`, request streamed usage, and disable unsupported assumptions about `store`, strict tools, grammar tools, and reasoning effort. These are model-level properties and can be overridden through Pi.
|
|
184
|
+
|
|
185
|
+
|
|
186
|
+
## Testing
|
|
187
|
+
|
|
188
|
+
```bash
|
|
189
|
+
npm ci
|
|
190
|
+
npm run typecheck
|
|
191
|
+
npm run lint
|
|
192
|
+
npm test
|
|
193
|
+
```
|
|
194
|
+
|
|
195
|
+
Tests cover model parsing/mapping, discovery errors, missing credentials, cache behavior, native authentication, and Pi's actual extension loader. Adapter tests use Pi itself with mocked HTTP responses. The CLI integration test starts a local HTTP server, loads this package in the real Pi CLI, checks `--list-models`, executes a streamed `write` call, checks the generated file, and verifies that the next request contains the tool result. It also checks cache reuse and `models.json` composition. These tests do not require or use a real Nebius key.
|
|
196
|
+
|
|
197
|
+
### Manual live integration test
|
|
198
|
+
|
|
199
|
+
In a scratch directory, start Pi, select a tool-capable Nebius model with `/model`, then ask:
|
|
200
|
+
|
|
201
|
+
> Create a file called nebius-test.txt containing 'Hello from Nebius'.
|
|
202
|
+
|
|
203
|
+
Verify that Pi shows a successful write-tool execution, the file contains the expected text, and the model responds after receiving the tool result. Check Token Factory's usage view for the request. Ordinary text claiming the file was created is not sufficient.
|
|
204
|
+
|
|
205
|
+
An opt-in automated equivalent runs a real Pi CLI in a temporary directory, limits its tools to `write`, requires an actual tool event and final response, checks exact file contents, and cleans up afterward:
|
|
206
|
+
|
|
207
|
+
```bash
|
|
208
|
+
export NEBIUS_API_KEY="your-key"
|
|
209
|
+
export NEBIUS_MODEL="EXACT_TOOL_CAPABLE_MODEL_ID"
|
|
210
|
+
npm run test:live
|
|
211
|
+
```
|
|
212
|
+
|
|
213
|
+
This makes paid inference requests and has a three-minute timeout. It is not part of `npm test`. See [docs/validation.md](docs/validation.md) for what has actually been run.
|
|
214
|
+
|
|
215
|
+
## Troubleshooting
|
|
216
|
+
|
|
217
|
+
| Problem | Action |
|
|
218
|
+
| --- | --- |
|
|
219
|
+
| Missing API key | Run `export NEBIUS_API_KEY="..."` in the launching shell and restart Pi. |
|
|
220
|
+
| 401/403 | Token Factory rejected the key or project access. Check the key, permissions, and project; then restart Pi. |
|
|
221
|
+
| Model unavailable / 404 | Run `/nebius-refresh`, then `/model`; remove obsolete explicit entries from `models.json`. |
|
|
222
|
+
| 429 | Respect the preserved `retry-after` / rate-limit details. Pi handles its normal retry policy; discovery itself does not retry automatically. |
|
|
223
|
+
| 5xx | Provider service failure. Retry later; discovery can use stale cached metadata. |
|
|
224
|
+
| Connectivity / timeout | Check access to `api.tokenfactory.nebius.com` and Pi's network/proxy settings. Discovery times out after eight seconds. |
|
|
225
|
+
| No models without a cache | Add an explicit verified model via the example configuration, or retry discovery once connectivity returns. |
|
|
226
|
+
| Model talks but never uses tools | Verify that the chosen model supports functions on Nebius; run the live test or select another model. Discovery alone cannot certify this. |
|
|
227
|
+
| Context/output limit errors | Set verified `contextWindow` and `maxTokens` in `modelOverrides`. Defaults are conservative assumptions, not guaranteed endpoint limits. |
|
|
228
|
+
|
|
229
|
+
## Current limitations
|
|
230
|
+
|
|
231
|
+
- Live Nebius inference has not been verified in the development environment because no API key was available. Mocked Pi integration tests prove adapter wiring and the tool loop, not every hosted model's capabilities.
|
|
232
|
+
- Metadata availability and capability vocabulary vary. Reasoning/vision detection is conservative; model-specific thinking templates require explicit configuration and testing.
|
|
233
|
+
- Maximum output limits are defaults; the verbose schema does not expose a documented equivalent of Pi's `maxTokens`.
|
|
234
|
+
- Pi's adapter handles `stop`, `length`, and `tool_calls` finish reasons. Its generic stream options do not expose custom stop sequences; advanced extensions can use Pi's `onPayload` hook if needed. No new stop-sequence API is invented here.
|
|
235
|
+
- The normal picker/listing integration is tested through Pi's CLI and registry. An interactive terminal screenshot/UI test was not performed.
|
|
236
|
+
|
|
237
|
+
## License
|
|
238
|
+
|
|
239
|
+
MIT; see [LICENSE](LICENSE).
|
package/SECURITY.md
ADDED
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
# Security policy
|
|
2
|
+
|
|
3
|
+
This project accepts security fixes for the latest tagged 0.x release and
|
|
4
|
+
the default branch. Older versions receive no separate backports.
|
|
5
|
+
|
|
6
|
+
## Reporting
|
|
7
|
+
|
|
8
|
+
When this repository is hosted on GitHub, use **Security → Report a vulnerability**
|
|
9
|
+
if private vulnerability reporting is enabled. Do not put API keys, private code,
|
|
10
|
+
or exploitable details into a public issue. If private reporting is unavailable,
|
|
11
|
+
open an issue requesting a private contact without disclosing the vulnerability.
|
|
12
|
+
There is no dedicated security mailbox or response-time guarantee yet.
|
|
13
|
+
|
|
14
|
+
## Trust boundaries
|
|
15
|
+
|
|
16
|
+
- Pi extensions and built-in tools execute with your host user's access. The benchmark's
|
|
17
|
+
file copies and separate processes are **not an OS sandbox**. Run untrusted tasks in
|
|
18
|
+
a disposable environment. Agents may access paths outside the workspace.
|
|
19
|
+
- Benchmark definitions, setup commands, and validators are executable code: review them.
|
|
20
|
+
Restored validators prevent ordinary accidental replacement, not malicious host access.
|
|
21
|
+
- The provider reads `NEBIUS_API_KEY`; its cache stores model metadata and a key fingerprint,
|
|
22
|
+
not the key. The normal interactive Pi process inherits your shell environment.
|
|
23
|
+
- The benchmark sends its key over private parent/child IPC. Tool and validation subprocesses
|
|
24
|
+
receive an allowlisted environment without that key. Traces redact the known key and bearer
|
|
25
|
+
strings; arbitrary secrets in user fixtures or generated files cannot be reliably identified.
|
|
26
|
+
- Network inference sends task context and tool results to Nebius. Follow your own data policy.
|
|
27
|
+
- Cache/filesystem checks assume the same host user is trusted. They do not defend against
|
|
28
|
+
an attacker concurrently replacing filesystem entries or controlling the parent process.
|
|
29
|
+
|
|
30
|
+
## Repository controls
|
|
31
|
+
|
|
32
|
+
Test/security CI uses read-only tokens and SHA-pinned Actions, no live credentials, and ordinary
|
|
33
|
+
`pull_request` triggers. After successful main-branch CI, Release Please updates a
|
|
34
|
+
release PR or publishes the merged release. Its dedicated repository-scoped token
|
|
35
|
+
is confined to the release workflow, which checks out no code and executes no
|
|
36
|
+
repository build scripts. npm publication is disabled with `private: true`.
|
|
37
|
+
|
|
38
|
+
`npm run security:check` verifies pinned official Gitleaks/actionlint binary checksums,
|
|
39
|
+
scans Git-visible first-party files and available commit history with redacted output,
|
|
40
|
+
and validates workflows. It downloads executable tools from GitHub. `npm audit` checks
|
|
41
|
+
known dependency advisories separately. Neither scan proves the absence of vulnerabilities.
|
|
42
|
+
|
|
43
|
+
See [the review record](docs/security-review.md) for findings and residual limitations.
|
|
44
|
+
|
|
45
|
+
The PR-description workflow uses `pull_request_target` and `workflow_run` only to
|
|
46
|
+
read GitHub metadata and update the PR body with a scoped write token. It never
|
|
47
|
+
checks out PR code, installs dependencies, or consumes workflow artifacts.
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
schemaVersion: 1
|
|
2
|
+
name: add-api-endpoint
|
|
3
|
+
task: |
|
|
4
|
+
Implement GET /api/sum in app.mjs, preserving GET /health. Accept query parameters
|
|
5
|
+
a and b as finite numbers and return JSON {sum: a+b} with status 200. Missing,
|
|
6
|
+
blank, nonnumeric, infinite inputs or non-finite sums must return 400. Other
|
|
7
|
+
methods on /api/sum return 405. Unknown routes return 404. Keep the existing
|
|
8
|
+
Fetch Request/Response interface and do not change tests.
|
|
9
|
+
validation:
|
|
10
|
+
- command: node
|
|
11
|
+
args: [--test, "{validation}/check.test.mjs"]
|
|
12
|
+
timeout: 120
|
|
13
|
+
validationTimeout: 15
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import assert from "node:assert/strict";
|
|
2
|
+
import { test } from "node:test";
|
|
3
|
+
import { handle } from "./app.mjs";
|
|
4
|
+
|
|
5
|
+
test("health", async () =>
|
|
6
|
+
assert.deepEqual(await handle(new Request("http://local/health")).json(), { ok: true }));
|
|
7
|
+
test("sum endpoint", async () =>
|
|
8
|
+
assert.deepEqual(await handle(new Request("http://local/api/sum?a=2&b=3")).json(), { sum: 5 }));
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import assert from "node:assert/strict";
|
|
2
|
+
import { resolve } from "node:path";
|
|
3
|
+
import { test } from "node:test";
|
|
4
|
+
import { pathToFileURL } from "node:url";
|
|
5
|
+
|
|
6
|
+
const { handle } = await import(pathToFileURL(resolve("app.mjs")));
|
|
7
|
+
test("HTTP endpoint contract", async () => {
|
|
8
|
+
assert.deepEqual(await handle(new Request("http://local/health")).json(), { ok: true });
|
|
9
|
+
for (const [a, b] of [
|
|
10
|
+
[2, 3],
|
|
11
|
+
[-4, 1.5],
|
|
12
|
+
[0, 0],
|
|
13
|
+
[100, -100],
|
|
14
|
+
]) {
|
|
15
|
+
const response = await handle(new Request(`http://local/api/sum?a=${a}&b=${b}`));
|
|
16
|
+
assert.equal(response.status, 200);
|
|
17
|
+
assert.match(response.headers.get("content-type"), /application\/json/);
|
|
18
|
+
assert.deepEqual(await response.json(), { sum: a + b });
|
|
19
|
+
}
|
|
20
|
+
for (const query of [
|
|
21
|
+
"",
|
|
22
|
+
"a=1",
|
|
23
|
+
"b=2",
|
|
24
|
+
"a=&b=2",
|
|
25
|
+
"a=%20&b=2",
|
|
26
|
+
"a=no&b=2",
|
|
27
|
+
"a=Infinity&b=1",
|
|
28
|
+
"a=1e308&b=1e308",
|
|
29
|
+
]) {
|
|
30
|
+
assert.equal((await handle(new Request(`http://local/api/sum?${query}`))).status, 400);
|
|
31
|
+
}
|
|
32
|
+
assert.equal(
|
|
33
|
+
(await handle(new Request("http://local/api/sum?a=1&b=2", { method: "POST" }))).status,
|
|
34
|
+
405,
|
|
35
|
+
);
|
|
36
|
+
assert.equal((await handle(new Request("http://local/missing"))).status, 404);
|
|
37
|
+
});
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
schemaVersion: 1
|
|
2
|
+
name: fix-auth-bug
|
|
3
|
+
task: |
|
|
4
|
+
Fix refresh-token expiry handling in auth.mjs. A token is valid strictly before
|
|
5
|
+
its expiresAt timestamp (Unix seconds); nowMs is in milliseconds. Both expiresAt
|
|
6
|
+
and nowMs must be finite numbers: reject NaN, Infinity, -Infinity, missing values,
|
|
7
|
+
and strings without coercing them to numbers. Reject revoked and missing tokens.
|
|
8
|
+
Run the existing tests and ensure both timestamp inputs meet these requirements.
|
|
9
|
+
Do not change the tests.
|
|
10
|
+
validation:
|
|
11
|
+
- command: node
|
|
12
|
+
args: [--test, "{validation}/check.test.mjs"]
|
|
13
|
+
timeout: 120
|
|
14
|
+
validationTimeout: 15
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import assert from "node:assert/strict";
|
|
2
|
+
import { test } from "node:test";
|
|
3
|
+
import { canRefresh } from "./auth.mjs";
|
|
4
|
+
|
|
5
|
+
test("unexpired refresh token", () => {
|
|
6
|
+
assert.equal(canRefresh({ expiresAt: 2000, revoked: false }, 1_999_000), true);
|
|
7
|
+
});
|
|
8
|
+
test("expiry boundary", () => {
|
|
9
|
+
assert.equal(canRefresh({ expiresAt: 2000, revoked: false }, 2_000_000), false);
|
|
10
|
+
});
|
|
11
|
+
|
|
12
|
+
test("expired, revoked, and missing tokens", () => {
|
|
13
|
+
assert.equal(canRefresh({ expiresAt: 2000 }, 2_000_001), false);
|
|
14
|
+
assert.equal(canRefresh({ expiresAt: 2000, revoked: true }, 1000), false);
|
|
15
|
+
assert.equal(canRefresh(null, 1000), false);
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
for (const invalid of [NaN, Infinity, -Infinity, undefined, "2000"]) {
|
|
19
|
+
test(`rejects invalid expiresAt: ${String(invalid)}`, () => {
|
|
20
|
+
assert.equal(canRefresh({ expiresAt: invalid }, 1000), false);
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
test(`rejects invalid nowMs: ${String(invalid)}`, () => {
|
|
24
|
+
assert.equal(canRefresh({ expiresAt: 2000 }, invalid), false);
|
|
25
|
+
});
|
|
26
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import assert from "node:assert/strict";
|
|
2
|
+
import { resolve } from "node:path";
|
|
3
|
+
import { test } from "node:test";
|
|
4
|
+
import { pathToFileURL } from "node:url";
|
|
5
|
+
|
|
6
|
+
const { canRefresh } = await import(pathToFileURL(resolve("auth.mjs")));
|
|
7
|
+
test("refresh contract across boundaries and invalid inputs", () => {
|
|
8
|
+
assert.equal(canRefresh({ expiresAt: 2000 }, 1_999_999), true);
|
|
9
|
+
assert.equal(canRefresh({ expiresAt: 2000 }, 2_000_000), false);
|
|
10
|
+
assert.equal(canRefresh({ expiresAt: 2000 }, 2_000_001), false);
|
|
11
|
+
assert.equal(canRefresh({ expiresAt: 2000, revoked: true }, 1000), false);
|
|
12
|
+
assert.equal(canRefresh(null, 1000), false);
|
|
13
|
+
for (const bad of [NaN, Infinity, -Infinity, undefined, "2000"]) {
|
|
14
|
+
assert.equal(
|
|
15
|
+
canRefresh({ expiresAt: bad }, 1000),
|
|
16
|
+
false,
|
|
17
|
+
`expiresAt must be a finite number; received ${String(bad)}`,
|
|
18
|
+
);
|
|
19
|
+
assert.equal(
|
|
20
|
+
canRefresh({ expiresAt: 2000 }, bad),
|
|
21
|
+
false,
|
|
22
|
+
`nowMs must be a finite number; received ${String(bad)}`,
|
|
23
|
+
);
|
|
24
|
+
}
|
|
25
|
+
});
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
schemaVersion: 1
|
|
2
|
+
name: multi-file-feature
|
|
3
|
+
task: |
|
|
4
|
+
Add task completion across store.mjs, routes.mjs and serialize.mjs. New tasks
|
|
5
|
+
start with completed:false. Export setCompleted(id, completed) from store.mjs;
|
|
6
|
+
update the stored task and return it, or return null for an unknown ID.
|
|
7
|
+
Expose PATCH /tasks/:id accepting JSON {completed:boolean}, returning the
|
|
8
|
+
updated serialized task with status 200, 400 for invalid JSON/nonboolean values,
|
|
9
|
+
or 404 for missing tasks. Serialization must include completed. Preserve GET /tasks
|
|
10
|
+
and POST /tasks. Completion must persist into subsequent GET results.
|
|
11
|
+
validation:
|
|
12
|
+
- command: node
|
|
13
|
+
args: [--test, "{validation}/check.test.mjs"]
|
|
14
|
+
timeout: 180
|
|
15
|
+
validationTimeout: 15
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { serialize } from "./serialize.mjs";
|
|
2
|
+
import { create, list } from "./store.mjs";
|
|
3
|
+
export async function handle(request) {
|
|
4
|
+
const path = new URL(request.url).pathname;
|
|
5
|
+
if (path === "/tasks" && request.method === "GET") return Response.json(list().map(serialize));
|
|
6
|
+
if (path === "/tasks" && request.method === "POST") {
|
|
7
|
+
const { title } = await request.json();
|
|
8
|
+
return Response.json(serialize(create(title)), { status: 201 });
|
|
9
|
+
}
|
|
10
|
+
return Response.json({ error: "Not found" }, { status: 404 });
|
|
11
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import assert from "node:assert/strict";
|
|
2
|
+
import { test } from "node:test";
|
|
3
|
+
import { handle } from "./routes.mjs";
|
|
4
|
+
|
|
5
|
+
test("created task starts incomplete", async () => {
|
|
6
|
+
const response = await handle(
|
|
7
|
+
new Request("http://local/tasks", {
|
|
8
|
+
method: "POST",
|
|
9
|
+
body: JSON.stringify({ title: "A task" }),
|
|
10
|
+
}),
|
|
11
|
+
);
|
|
12
|
+
assert.equal(response.status, 201);
|
|
13
|
+
assert.equal((await response.json()).completed, false);
|
|
14
|
+
});
|