localpi 0.6.0 → 0.6.1
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,7 +4,7 @@
|
|
|
4
4
|
<img src="assets/cover.svg" alt="localpi: an unopinionated Pi distribution that makes it easy to test and work with small local models on constrained systems" width="880">
|
|
5
5
|
</p>
|
|
6
6
|
|
|
7
|
-
Localpi is an unopinionated Pi distribution that makes it easy to test and work with small local models on constrained systems.
|
|
7
|
+
Localpi is an unopinionated [Pi](https://pi.dev) distribution that makes it easy to test and work with small local models on constrained systems.
|
|
8
8
|
|
|
9
9
|
By default, Localpi discovers available local providers, lets you choose when more than one model is loaded, points Pi at the selected model, and writes Pi config for the other discovered models so `/model` can switch among them during the session.
|
|
10
10
|
|
|
@@ -216,9 +216,10 @@ Approval still works: the adapter forwards Pi's extension dialogs to the ACP cli
|
|
|
216
216
|
asks before a tool call runs. The adapter does not pass `--no-extensions`, so Pi extension discovery
|
|
217
217
|
stays on.
|
|
218
218
|
|
|
219
|
-
Pi has no ACP mode of its own. ACP support comes from
|
|
220
|
-
|
|
221
|
-
different adapter
|
|
219
|
+
Pi has no ACP mode of its own. ACP support comes from
|
|
220
|
+
[`pi-acp`](https://github.com/svkozak/pi-acp) (MIT), which `package.json` pins to an exact version,
|
|
221
|
+
and localpi never vendors its source. Set `LOCALPI_ACP_ADAPTER` to a path to run a different adapter
|
|
222
|
+
build.
|
|
222
223
|
|
|
223
224
|
## Continue On Truncation
|
|
224
225
|
|
|
@@ -669,6 +670,20 @@ Model capability profiles can fill in metadata that OpenAI-compatible servers do
|
|
|
669
670
|
|
|
670
671
|
`LOCALPAGER_AGENT_PROFILE`, `LOCALPAGER_AGENT_REASONING`, and `LOCALPAGER_AGENT_THINKING_FORMAT` are accepted as aliases so LocalPager Agent can pass the same profile metadata through to localpi.
|
|
671
672
|
|
|
673
|
+
## Related Projects
|
|
674
|
+
|
|
675
|
+
- [Pi](https://pi.dev) is the coding agent this distribution launches. Pi owns model detection, the
|
|
676
|
+
tool loop, streaming, slash commands, and the session. Its source is
|
|
677
|
+
[earendil-works/pi](https://github.com/earendil-works/pi).
|
|
678
|
+
- [pi-factory](https://github.com/osolmaz/pi-factory) builds the Pi launch plan, the launch
|
|
679
|
+
environment, and the runtime config a session uses.
|
|
680
|
+
- [pi-demo-mode](https://github.com/osolmaz/pi-demo-mode) is the shared extension behind demo mode.
|
|
681
|
+
- [pi-acp](https://github.com/svkozak/pi-acp) is the adapter that ACP mode pins, for the
|
|
682
|
+
[Agent Client Protocol](https://agentclientprotocol.com). Localpi keeps no copy of its source.
|
|
683
|
+
- Inference stacks: [llama.cpp](https://github.com/ggml-org/llama.cpp) and its `llama-server`,
|
|
684
|
+
[LM Studio](https://lmstudio.ai), [vLLM](https://github.com/vllm-project/vllm),
|
|
685
|
+
[SGLang](https://github.com/sgl-project/sglang), and [Ollama](https://ollama.com).
|
|
686
|
+
|
|
672
687
|
## Development
|
|
673
688
|
|
|
674
689
|
```bash
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
export function startupModelSelectorExtensionSource(options) {
|
|
2
2
|
const startupModelsSource = JSON.stringify(options.models);
|
|
3
|
-
return `import type { ExtensionAPI,
|
|
3
|
+
return `import type { ExtensionAPI, ModelRegistry, ModelRuntime } from "@earendil-works/pi-coding-agent";
|
|
4
4
|
import { ModelSelectorComponent } from "@earendil-works/pi-coding-agent";
|
|
5
5
|
|
|
6
6
|
type SelectedModel = Parameters<ExtensionAPI["setModel"]>[0];
|
|
@@ -22,20 +22,16 @@ export default function localpiStartupModelSelector(pi: ExtensionAPI): void {
|
|
|
22
22
|
const scopedModels = selectableModels.map((model) => ({ model }));
|
|
23
23
|
|
|
24
24
|
opened = true;
|
|
25
|
-
const selected = await ctx.ui.custom<SelectedModel | undefined>((tui, _theme, _keybindings, done) =>
|
|
26
|
-
|
|
27
|
-
setDefaultModelAndProvider: () => {}
|
|
28
|
-
} as unknown as SettingsManager;
|
|
29
|
-
return new ModelSelectorComponent(
|
|
25
|
+
const selected = await ctx.ui.custom<SelectedModel | undefined>((tui, _theme, _keybindings, done) =>
|
|
26
|
+
new ModelSelectorComponent(
|
|
30
27
|
tui,
|
|
31
28
|
ctx.model,
|
|
32
|
-
|
|
33
|
-
startupModelRegistry(ctx.modelRegistry) as typeof ctx.modelRegistry,
|
|
29
|
+
startupModelRuntime(ctx.modelRegistry),
|
|
34
30
|
scopedModels,
|
|
35
|
-
(model) => done(model),
|
|
31
|
+
(model: SelectedModel) => done(model),
|
|
36
32
|
() => done(undefined)
|
|
37
|
-
)
|
|
38
|
-
|
|
33
|
+
)
|
|
34
|
+
);
|
|
39
35
|
|
|
40
36
|
if (selected === undefined) {
|
|
41
37
|
return;
|
|
@@ -54,21 +50,20 @@ function startupAvailableModels(registry: {
|
|
|
54
50
|
return registry.getAvailable().filter((model) => startupModelKeys.has(modelKey(model)));
|
|
55
51
|
}
|
|
56
52
|
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
refresh: () => registry.refresh(),
|
|
65
|
-
getError: () => registry.getError(),
|
|
66
|
-
getAvailable: () => startupAvailableModels(registry),
|
|
67
|
-
find: (provider, modelId) => {
|
|
53
|
+
// The selector reads a ModelRuntime, which extensions cannot reach. It only calls
|
|
54
|
+
// getAvailableSnapshot, getModel, getError, and refresh, so this view answers
|
|
55
|
+
// those from the registry and hides every model outside the startup list.
|
|
56
|
+
function startupModelRuntime(registry: ModelRegistry): ModelRuntime {
|
|
57
|
+
const view = {
|
|
58
|
+
getAvailableSnapshot: () => startupAvailableModels(registry),
|
|
59
|
+
getModel: (provider: string, modelId: string) => {
|
|
68
60
|
const model = registry.find(provider, modelId);
|
|
69
61
|
return model !== undefined && startupModelKeys.has(modelKey(model)) ? model : undefined;
|
|
70
|
-
}
|
|
62
|
+
},
|
|
63
|
+
getError: () => registry.getError(),
|
|
64
|
+
refresh: (options?: Parameters<ModelRegistry["refresh"]>[0]) => registry.refresh(options)
|
|
71
65
|
};
|
|
66
|
+
return view as unknown as ModelRuntime;
|
|
72
67
|
}
|
|
73
68
|
|
|
74
69
|
function modelKey(model: { readonly provider: string; readonly id: string }): string {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "localpi",
|
|
3
|
-
"version": "0.6.
|
|
3
|
+
"version": "0.6.1",
|
|
4
4
|
"description": "Swiss army knife for running Pi with local inference engines.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -41,7 +41,7 @@
|
|
|
41
41
|
"check": "npm run format && npm run lint && npm run typecheck && npm test && npm run build"
|
|
42
42
|
},
|
|
43
43
|
"devDependencies": {
|
|
44
|
-
"@earendil-works/pi-coding-agent": "^0.87.
|
|
44
|
+
"@earendil-works/pi-coding-agent": "^0.87.1",
|
|
45
45
|
"@eslint/js": "^9.0.0",
|
|
46
46
|
"@stryker-mutator/core": "^9.6.1",
|
|
47
47
|
"@stryker-mutator/typescript-checker": "^9.6.1",
|