pi-llama-cpp 0.1.2 → 0.2.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 +31 -12
- package/package.json +14 -2
- package/src/constants.ts +6 -1
- package/src/enums/{actions.ts → action.ts} +3 -1
- package/src/enums/mode.ts +5 -0
- package/src/enums/status.ts +1 -0
- package/src/events.ts +1 -1
- package/src/handlers.ts +116 -34
- package/{index.ts → src/index.ts} +7 -6
- package/src/interfaces/IAuthFile.ts +6 -4
- package/src/interfaces/IRouterModel.ts +9 -1
- package/src/models/baseModel.ts +24 -1
- package/src/models/routerModel.ts +15 -6
- package/src/models/singleModel.ts +7 -2
- package/src/tools/resolver.ts +16 -9
- package/src/tools/retriever.ts +3 -3
- package/tsconfig.json +13 -0
package/README.md
CHANGED
|
@@ -5,12 +5,22 @@ A [Pi Coding Agent](https://pi.dev/) extension that integrates with a running [l
|
|
|
5
5
|
## Features
|
|
6
6
|
|
|
7
7
|
- **Auto-detect models** — discovers all models available on your running llama.cpp server
|
|
8
|
-
- **Live status indicators** — see which models are loaded, loading, failed, or unloaded with color-coded icons
|
|
8
|
+
- **Live status indicators** — see which models are loaded, loading, failed, sleeping, or unloaded with color-coded icons
|
|
9
9
|
- **Load / unload / switch** — manage models directly from the Pi command palette
|
|
10
10
|
- **Multi-model router support** — works with both single-model and multi-model llama.cpp server configurations
|
|
11
|
-
- **Image
|
|
11
|
+
- **Image capabilities detection** — detects multimodal models automatically
|
|
12
12
|
- **Flexible URL resolution** — configures the server URL via project config, environment variable, or global settings
|
|
13
13
|
|
|
14
|
+
### Status Indicators
|
|
15
|
+
|
|
16
|
+
| Icon | Status | Description |
|
|
17
|
+
|------|--------|-------------|
|
|
18
|
+
| 🟢 | Loaded | Model is active and ready to use |
|
|
19
|
+
| 🟡 | Loading | Model is currently being loaded |
|
|
20
|
+
| 🔴 | Failed | Model failed to load |
|
|
21
|
+
| 🔵 | Sleeping | Model is loaded but inactive (router mode) |
|
|
22
|
+
| ⚪ | Unloaded | Model is not loaded on the server |
|
|
23
|
+
|
|
14
24
|
## Installation
|
|
15
25
|
|
|
16
26
|
This package is a Pi extension. Install it with
|
|
@@ -51,15 +61,15 @@ The extension resolves the llama.cpp server URL using the following priority ord
|
|
|
51
61
|
|
|
52
62
|
### API Key
|
|
53
63
|
|
|
54
|
-
If your llama.cpp server requires authentication, use `/login` in Pi, select the "API key" option, and choose the `
|
|
64
|
+
If your llama.cpp server requires authentication, use `/login` in Pi, select the "API key" option, and choose the `Llama.cpp` provider from the list.
|
|
55
65
|
|
|
56
|
-
Alternatively, configure the API key in `~/.pi/agent/auth.json`:
|
|
66
|
+
Alternatively, configure the API key in `~/.pi/agent/auth.json` using the provider ID `llama-server`:
|
|
57
67
|
|
|
58
68
|
```json
|
|
59
69
|
{
|
|
60
70
|
"llama-server": {
|
|
61
|
-
"type": "
|
|
62
|
-
"key": "your-api-key-here"
|
|
71
|
+
"type": "api_key",
|
|
72
|
+
"key": "<your-api-key-here>"
|
|
63
73
|
}
|
|
64
74
|
}
|
|
65
75
|
```
|
|
@@ -68,15 +78,17 @@ Alternatively, configure the API key in `~/.pi/agent/auth.json`:
|
|
|
68
78
|
|
|
69
79
|
### Prerequisites
|
|
70
80
|
|
|
71
|
-
Make sure your llama.cpp server is running with the appropriate flags.
|
|
81
|
+
Make sure your llama.cpp server is running with the appropriate flags.
|
|
82
|
+
|
|
83
|
+
- For multi-model support (model router), start the server with:
|
|
72
84
|
|
|
73
85
|
```bash
|
|
74
|
-
llama-server --models-preset path/to/presets.ini
|
|
86
|
+
llama-server --models-preset path/to/presets.ini ...
|
|
75
87
|
```
|
|
76
88
|
|
|
77
|
-
|
|
89
|
+
The extension reads the context size from the preset file using the `ctx-size` and/or `fit-ctx` keys.
|
|
78
90
|
|
|
79
|
-
For single-model mode,
|
|
91
|
+
- For single-model mode, start the server with:
|
|
80
92
|
|
|
81
93
|
```bash
|
|
82
94
|
llama-server --model path/to/model.gguf --ctx-size 128000 ...
|
|
@@ -86,7 +98,9 @@ llama-server --model path/to/model.gguf --ctx-size 128000 ...
|
|
|
86
98
|
|
|
87
99
|
| Command | Description |
|
|
88
100
|
| --------- | ------------------------------------------------------------------------------------------ |
|
|
89
|
-
| `/models` | Browse
|
|
101
|
+
| `/models` | Browse your models with live status. Select a model to load, switch, or unload it. |
|
|
102
|
+
|
|
103
|
+
> **Note:** When the llama.cpp server is unreachable, `/models` is still available but displays an error notification with the configured server URL.
|
|
90
104
|
|
|
91
105
|
### Model Actions
|
|
92
106
|
|
|
@@ -95,10 +109,15 @@ When browsing models via the `/models` command, you can:
|
|
|
95
109
|
- **Load & switch** — Load an unloaded model and switch to it
|
|
96
110
|
- **Switch model** — Switch to a model that is already loaded
|
|
97
111
|
- **Unload** — Unload a loaded model to free memory
|
|
112
|
+
- **Retry** — Retry loading a failed model
|
|
113
|
+
- **Info** — View model details (ID, capabilities, context size)
|
|
114
|
+
- **Cancel** — Cancel the current operation
|
|
115
|
+
|
|
116
|
+
> **Note:** In single-model mode, only **Info** and **Cancel** are available, since there is only one model loaded on the server.
|
|
98
117
|
|
|
99
118
|
### Model Selection Event
|
|
100
119
|
|
|
101
|
-
When Pi switches models (
|
|
120
|
+
When Pi switches models (via `model_select`), the extension automatically loads the selected model on the llama.cpp server. This keeps the server in sync with the active model in Pi.
|
|
102
121
|
|
|
103
122
|
### Model Configuration
|
|
104
123
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pi-llama-cpp",
|
|
3
|
-
"version": "0.1
|
|
3
|
+
"version": "0.2.1",
|
|
4
4
|
"description": "Pi extension for llama.cpp integration. Supports both router and single modes",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"pi",
|
|
@@ -20,7 +20,19 @@
|
|
|
20
20
|
},
|
|
21
21
|
"pi": {
|
|
22
22
|
"extensions": [
|
|
23
|
-
"./index.ts"
|
|
23
|
+
"./src/index.ts"
|
|
24
24
|
]
|
|
25
|
+
},
|
|
26
|
+
"prettier": {
|
|
27
|
+
"plugins": [
|
|
28
|
+
"prettier-plugin-organize-imports"
|
|
29
|
+
]
|
|
30
|
+
},
|
|
31
|
+
"peerDependencies": {
|
|
32
|
+
"@mariozechner/pi-coding-agent": "*"
|
|
33
|
+
},
|
|
34
|
+
"devDependencies": {
|
|
35
|
+
"@types/node": "^25.6.0",
|
|
36
|
+
"prettier-plugin-organize-imports": "^4.3.0"
|
|
25
37
|
}
|
|
26
38
|
}
|
package/src/constants.ts
CHANGED
|
@@ -1,7 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* This provider's id
|
|
3
|
+
*/
|
|
4
|
+
export const PROVIDER_ID = "llama-server";
|
|
5
|
+
|
|
1
6
|
/**
|
|
2
7
|
* This provider's name
|
|
3
8
|
*/
|
|
4
|
-
export const PROVIDER_NAME = "
|
|
9
|
+
export const PROVIDER_NAME = "Llama.cpp";
|
|
5
10
|
|
|
6
11
|
/**
|
|
7
12
|
* The default URL if the resolver couldn't find it
|
package/src/enums/status.ts
CHANGED
package/src/events.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { ExtensionContext } from "@mariozechner/pi-coding-agent";
|
|
2
2
|
import { PROVIDER_NAME } from "./constants";
|
|
3
|
-
import { listModels } from "./tools/retriever";
|
|
4
3
|
import { ModelSelectEvent } from "./interfaces/IModelSelectEvent";
|
|
4
|
+
import { listModels } from "./tools/retriever";
|
|
5
5
|
|
|
6
6
|
/**
|
|
7
7
|
* Reacts to a new model event triggered by Pi
|
package/src/handlers.ts
CHANGED
|
@@ -2,50 +2,116 @@ import type {
|
|
|
2
2
|
ExtensionAPI,
|
|
3
3
|
ExtensionCommandContext,
|
|
4
4
|
} from "@mariozechner/pi-coding-agent";
|
|
5
|
+
import { PROVIDER_ID, PROVIDER_NAME } from "./constants";
|
|
6
|
+
import { Action } from "./enums/action";
|
|
7
|
+
import { Mode } from "./enums/mode";
|
|
5
8
|
import { Status } from "./enums/status";
|
|
6
9
|
import { BaseModel } from "./models/baseModel";
|
|
7
|
-
import { Actions } from "./enums/actions";
|
|
8
|
-
import { PROVIDER_NAME } from "./constants";
|
|
9
10
|
|
|
10
11
|
/**
|
|
11
|
-
*
|
|
12
|
+
* Select a model from the list. Returns null if user cancels.
|
|
13
|
+
*
|
|
12
14
|
* @param ctx Pi context
|
|
13
|
-
* @
|
|
15
|
+
* @param models A list of models
|
|
16
|
+
* @returns The selected model
|
|
14
17
|
*/
|
|
15
|
-
const
|
|
18
|
+
const selectModel = async (
|
|
16
19
|
ctx: ExtensionCommandContext,
|
|
17
20
|
models: BaseModel[],
|
|
18
|
-
): Promise<
|
|
19
|
-
// Setup the labels
|
|
21
|
+
): Promise<BaseModel | null> => {
|
|
20
22
|
const labels = await Promise.all(models.map((m) => m.getLabel()));
|
|
21
|
-
|
|
22
|
-
// Detect the selected model
|
|
23
23
|
const choice = await ctx.ui.select(`${PROVIDER_NAME} models:`, labels);
|
|
24
24
|
if (!choice) return null;
|
|
25
|
-
|
|
26
25
|
const idx = labels.indexOf(choice);
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
26
|
+
return models[idx];
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Get available actions for a model based on its mode and status.
|
|
31
|
+
*
|
|
32
|
+
* @param model The selected model
|
|
33
|
+
* @returns
|
|
34
|
+
*/
|
|
35
|
+
const getActionsForModel = async (model: BaseModel): Promise<Array<Action>> => {
|
|
36
|
+
const routerModeActions: Record<Status, Array<Action>> = {
|
|
37
|
+
[Status.LOADED]: [Action.SWITCH, Action.UNLOAD, Action.INFO, Action.CANCEL],
|
|
38
|
+
[Status.LOADING]: [Action.CANCEL],
|
|
39
|
+
[Status.FAILED]: [Action.RETRY, Action.CANCEL],
|
|
40
|
+
[Status.SLEEPING]: [Action.UNLOAD, Action.INFO, Action.CANCEL],
|
|
41
|
+
[Status.UNLOADED]: [Action.LOAD, Action.CANCEL],
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
const singleModeActions: Record<Status, Array<Action>> = {
|
|
45
|
+
[Status.LOADED]: [Action.INFO, Action.CANCEL],
|
|
46
|
+
[Status.LOADING]: [Action.CANCEL],
|
|
47
|
+
[Status.FAILED]: [Action.CANCEL],
|
|
48
|
+
[Status.SLEEPING]: [Action.CANCEL],
|
|
49
|
+
[Status.UNLOADED]: [Action.CANCEL],
|
|
35
50
|
};
|
|
36
51
|
|
|
52
|
+
const allActions =
|
|
53
|
+
model.mode === Mode.ROUTER ? routerModeActions : singleModeActions;
|
|
54
|
+
|
|
37
55
|
const status = await model.getStatus();
|
|
38
|
-
|
|
56
|
+
return allActions[status];
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Selects an action for a model.
|
|
61
|
+
*
|
|
62
|
+
* @param ctx Pi context
|
|
63
|
+
* @param model The selected model
|
|
64
|
+
* @param actions Possible actions to execute
|
|
65
|
+
* @returns The action, or null if user cancels
|
|
66
|
+
*/
|
|
67
|
+
const selectAction = async (
|
|
68
|
+
ctx: ExtensionCommandContext,
|
|
69
|
+
model: BaseModel,
|
|
70
|
+
actions: Array<Action>,
|
|
71
|
+
): Promise<Action | null> => {
|
|
72
|
+
const labels = actions.map((a) => String(a));
|
|
73
|
+
const choice = await ctx.ui.select(`${model.name}`, labels);
|
|
74
|
+
if (!choice) return null;
|
|
75
|
+
|
|
76
|
+
const idx = labels.indexOf(choice);
|
|
77
|
+
return actions[idx];
|
|
78
|
+
};
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Handles the menu for model selection
|
|
82
|
+
* Loops: select model → select action → handle action.
|
|
83
|
+
*
|
|
84
|
+
* Escape on actions menu goes back to model selection.
|
|
85
|
+
* Escape on model selection exits.
|
|
86
|
+
*
|
|
87
|
+
* @param ctx Pi context
|
|
88
|
+
* @returns The action and model, if detected
|
|
89
|
+
*/
|
|
90
|
+
const modelSelectionHandler = async (
|
|
91
|
+
ctx: ExtensionCommandContext,
|
|
92
|
+
models: BaseModel[],
|
|
93
|
+
): Promise<{ action: Action; model: BaseModel } | null> => {
|
|
94
|
+
while (true) {
|
|
95
|
+
// Select the model
|
|
96
|
+
const model = await selectModel(ctx, models);
|
|
97
|
+
if (!model) return null;
|
|
39
98
|
|
|
40
|
-
|
|
41
|
-
|
|
99
|
+
// Select the action
|
|
100
|
+
const actions = await getActionsForModel(model);
|
|
101
|
+
const action = await selectAction(ctx, model, actions);
|
|
102
|
+
if (action === null) {
|
|
103
|
+
// Escape key pressed => back to model selection
|
|
104
|
+
continue;
|
|
105
|
+
}
|
|
42
106
|
|
|
43
|
-
|
|
44
|
-
|
|
107
|
+
// Return the selected action and model
|
|
108
|
+
return { action, model };
|
|
109
|
+
}
|
|
45
110
|
};
|
|
46
111
|
|
|
47
112
|
/**
|
|
48
113
|
* Handles the /models command
|
|
114
|
+
*
|
|
49
115
|
* @param ctx The context used by Pi
|
|
50
116
|
* @param pi The Pi extension
|
|
51
117
|
*/
|
|
@@ -60,25 +126,40 @@ export const modelsCommandHandler = async (
|
|
|
60
126
|
// Detect the model
|
|
61
127
|
const { action, model } = event;
|
|
62
128
|
|
|
63
|
-
//
|
|
64
|
-
if (action ===
|
|
129
|
+
// Action: Cancel
|
|
130
|
+
if (!action || action === Action.CANCEL) return;
|
|
131
|
+
|
|
132
|
+
// Action: Info
|
|
133
|
+
if (action === Action.INFO) {
|
|
134
|
+
const info = await model.getInfo();
|
|
135
|
+
ctx.ui.notify(`${info}`, "info");
|
|
136
|
+
return;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
// Action: Unload
|
|
140
|
+
if (action === Action.UNLOAD) {
|
|
65
141
|
await model.unload();
|
|
66
|
-
ctx.ui.notify(`Unloaded ${model.
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
if (status === Status.LOADED) return;
|
|
142
|
+
ctx.ui.notify(`Unloaded ${model.name}`, "info");
|
|
143
|
+
return;
|
|
144
|
+
}
|
|
70
145
|
|
|
71
|
-
|
|
146
|
+
// Actions: Load/Switch/Retry
|
|
147
|
+
const loadActions = [Action.LOAD, Action.SWITCH, Action.RETRY];
|
|
148
|
+
if (loadActions.includes(action)) {
|
|
149
|
+
ctx.ui.notify(`Loading ${model.name}...`, "info");
|
|
72
150
|
|
|
73
|
-
// Load the model without blocking the UI
|
|
74
151
|
const onSuccess = async () => {
|
|
75
|
-
const piModel = ctx.modelRegistry.find(
|
|
152
|
+
const piModel = ctx.modelRegistry.find(PROVIDER_ID, model.id);
|
|
76
153
|
if (!piModel) {
|
|
77
|
-
throw new Error(`Cannot find model ${model.
|
|
154
|
+
throw new Error(`Cannot find model ${model.name} in pi registry`);
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
if ((await model.getStatus()) === Status.FAILED) {
|
|
158
|
+
throw new Error("Failed to load model");
|
|
78
159
|
}
|
|
79
160
|
|
|
80
161
|
await pi.setModel(piModel);
|
|
81
|
-
ctx.ui.notify(`Model ${model.
|
|
162
|
+
ctx.ui.notify(`Model ${model.name} ready`, "info");
|
|
82
163
|
};
|
|
83
164
|
|
|
84
165
|
const onFailure = (err: any) => {
|
|
@@ -86,6 +167,7 @@ export const modelsCommandHandler = async (
|
|
|
86
167
|
ctx.ui.notify(message, "error");
|
|
87
168
|
};
|
|
88
169
|
|
|
170
|
+
// Load the model without blocking the UI
|
|
89
171
|
model.load().then(onSuccess).catch(onFailure);
|
|
90
172
|
}
|
|
91
173
|
};
|
|
@@ -2,11 +2,11 @@ import type {
|
|
|
2
2
|
ExtensionAPI,
|
|
3
3
|
ExtensionCommandContext,
|
|
4
4
|
} from "@mariozechner/pi-coding-agent";
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
9
|
-
import {
|
|
5
|
+
import { PROVIDER_ID, PROVIDER_NAME } from "./constants";
|
|
6
|
+
import { onModelSelect } from "./events";
|
|
7
|
+
import { modelsCommandHandler } from "./handlers";
|
|
8
|
+
import { resolveApiKey, resolveUrl } from "./tools/resolver";
|
|
9
|
+
import { isServerReady, listModels } from "./tools/retriever";
|
|
10
10
|
|
|
11
11
|
export default async function (pi: ExtensionAPI) {
|
|
12
12
|
// Command registration
|
|
@@ -36,7 +36,8 @@ export default async function (pi: ExtensionAPI) {
|
|
|
36
36
|
});
|
|
37
37
|
|
|
38
38
|
// Provider registration
|
|
39
|
-
pi.registerProvider(
|
|
39
|
+
pi.registerProvider(PROVIDER_ID, {
|
|
40
|
+
name: PROVIDER_NAME,
|
|
40
41
|
baseUrl: `${url}/v1`,
|
|
41
42
|
api: "openai-completions",
|
|
42
43
|
apiKey: await resolveApiKey(),
|
|
@@ -1,3 +1,11 @@
|
|
|
1
|
+
interface IRouterModelStatus {
|
|
2
|
+
value: string;
|
|
3
|
+
args: string[];
|
|
4
|
+
preset: string;
|
|
5
|
+
exit_code?: number;
|
|
6
|
+
failed?: boolean;
|
|
7
|
+
}
|
|
8
|
+
|
|
1
9
|
export interface IRouterModel {
|
|
2
10
|
id: string;
|
|
3
11
|
aliases?: string[];
|
|
@@ -5,5 +13,5 @@ export interface IRouterModel {
|
|
|
5
13
|
object: string;
|
|
6
14
|
owned_by: string;
|
|
7
15
|
created: number;
|
|
8
|
-
status:
|
|
16
|
+
status: IRouterModelStatus;
|
|
9
17
|
}
|
package/src/models/baseModel.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { ProviderModelConfig } from "@mariozechner/pi-coding-agent";
|
|
2
2
|
import { MAX_TOKENS, POLLING_INTERVAL, POLLING_TIMEOUT } from "../constants";
|
|
3
|
+
import { Mode } from "../enums/mode";
|
|
3
4
|
import { Status } from "../enums/status";
|
|
4
5
|
import { rpc } from "../tools/retriever";
|
|
5
6
|
|
|
@@ -8,6 +9,7 @@ export abstract class BaseModel {
|
|
|
8
9
|
loaded: Status.LOADED,
|
|
9
10
|
loading: Status.LOADING,
|
|
10
11
|
failed: Status.FAILED,
|
|
12
|
+
sleeping: Status.SLEEPING,
|
|
11
13
|
unloaded: Status.UNLOADED,
|
|
12
14
|
};
|
|
13
15
|
|
|
@@ -15,9 +17,12 @@ export abstract class BaseModel {
|
|
|
15
17
|
[Status.LOADED]: "🟢",
|
|
16
18
|
[Status.LOADING]: "🟡",
|
|
17
19
|
[Status.FAILED]: "🔴",
|
|
20
|
+
[Status.SLEEPING]: "🔵",
|
|
18
21
|
[Status.UNLOADED]: "⚪",
|
|
19
22
|
};
|
|
20
23
|
|
|
24
|
+
abstract get mode(): Mode;
|
|
25
|
+
|
|
21
26
|
abstract get id(): string;
|
|
22
27
|
|
|
23
28
|
abstract get name(): string;
|
|
@@ -43,13 +48,31 @@ export abstract class BaseModel {
|
|
|
43
48
|
abstract getContextSize(): Promise<number>;
|
|
44
49
|
|
|
45
50
|
/**
|
|
46
|
-
*
|
|
51
|
+
* Sets up a label for the model selection screen
|
|
52
|
+
* @returns A label structured as "<icon> <name>"
|
|
47
53
|
*/
|
|
48
54
|
async getLabel(): Promise<string> {
|
|
49
55
|
const status = await this.getStatus();
|
|
50
56
|
return `${this.labelIcons[status]} ${this.name}`;
|
|
51
57
|
}
|
|
52
58
|
|
|
59
|
+
/**
|
|
60
|
+
* Returns a human-readable information about the model
|
|
61
|
+
* @returns A string with the model information
|
|
62
|
+
*/
|
|
63
|
+
async getInfo(): Promise<string> {
|
|
64
|
+
const messages = [
|
|
65
|
+
`ID : ${this.id}`,
|
|
66
|
+
`Model : ${this.name}`,
|
|
67
|
+
`Reasoning : ${this.reasoning}`,
|
|
68
|
+
`Capabilities : ${this.capabilities.join(", ")}`,
|
|
69
|
+
`Context size : ${await this.getContextSize()}`,
|
|
70
|
+
];
|
|
71
|
+
|
|
72
|
+
const response = `${messages.join("\n")}\n`;
|
|
73
|
+
return response;
|
|
74
|
+
}
|
|
75
|
+
|
|
53
76
|
/**
|
|
54
77
|
* Converts the llama-server model into a configuration object used by Pi
|
|
55
78
|
* @returns A Pi configuration object
|
|
@@ -1,7 +1,8 @@
|
|
|
1
|
-
import { IRouterModel } from "../interfaces/IRouterModel";
|
|
2
1
|
import { DEFAULT_CTX } from "../constants";
|
|
3
|
-
import {
|
|
2
|
+
import { Mode } from "../enums/mode";
|
|
4
3
|
import { Status } from "../enums/status";
|
|
4
|
+
import { IRouterModel } from "../interfaces/IRouterModel";
|
|
5
|
+
import { rpc } from "../tools/retriever";
|
|
5
6
|
import { BaseModel } from "./baseModel";
|
|
6
7
|
|
|
7
8
|
export class RouterModel extends BaseModel {
|
|
@@ -9,6 +10,10 @@ export class RouterModel extends BaseModel {
|
|
|
9
10
|
super();
|
|
10
11
|
}
|
|
11
12
|
|
|
13
|
+
get mode(): Mode {
|
|
14
|
+
return Mode.ROUTER;
|
|
15
|
+
}
|
|
16
|
+
|
|
12
17
|
get id(): string {
|
|
13
18
|
return this.model.id;
|
|
14
19
|
}
|
|
@@ -25,12 +30,16 @@ export class RouterModel extends BaseModel {
|
|
|
25
30
|
async getStatus(): Promise<Status> {
|
|
26
31
|
const { data } = await rpc<{ data: IRouterModel[] }>("/models");
|
|
27
32
|
const model = data.find((m) => m.id === this.id);
|
|
28
|
-
if (!model) return Status.
|
|
33
|
+
if (!model) return Status.FAILED;
|
|
34
|
+
|
|
35
|
+
const status = this.statusMapper[model.status.value];
|
|
36
|
+
if (status === Status.UNLOADED) {
|
|
37
|
+
if (this.model.status.failed) return Status.FAILED;
|
|
29
38
|
|
|
30
|
-
|
|
31
|
-
|
|
39
|
+
return Status.UNLOADED;
|
|
40
|
+
}
|
|
32
41
|
|
|
33
|
-
return
|
|
42
|
+
return status;
|
|
34
43
|
}
|
|
35
44
|
|
|
36
45
|
async getContextSize(): Promise<number> {
|
|
@@ -1,7 +1,8 @@
|
|
|
1
|
-
import { ISingleModel } from "../interfaces/ISingleModel";
|
|
2
1
|
import { DEFAULT_CTX } from "../constants";
|
|
3
|
-
import {
|
|
2
|
+
import { Mode } from "../enums/mode";
|
|
4
3
|
import { Status } from "../enums/status";
|
|
4
|
+
import { ISingleModel } from "../interfaces/ISingleModel";
|
|
5
|
+
import { rpc } from "../tools/retriever";
|
|
5
6
|
import { BaseModel } from "./baseModel";
|
|
6
7
|
|
|
7
8
|
export class SingleModel extends BaseModel {
|
|
@@ -9,6 +10,10 @@ export class SingleModel extends BaseModel {
|
|
|
9
10
|
super();
|
|
10
11
|
}
|
|
11
12
|
|
|
13
|
+
get mode(): Mode {
|
|
14
|
+
return Mode.SINGLE;
|
|
15
|
+
}
|
|
16
|
+
|
|
12
17
|
get id(): string {
|
|
13
18
|
return this.model.name;
|
|
14
19
|
}
|
package/src/tools/resolver.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { access, readFile, constants } from "node:fs/promises";
|
|
1
|
+
import { access, constants, readFile } from "node:fs/promises";
|
|
3
2
|
import { join } from "node:path";
|
|
4
|
-
import {
|
|
3
|
+
import { DEFAULT_LLAMA_SERVER_URL, PROVIDER_ID } from "../constants";
|
|
4
|
+
import { IAuth, IAuthFile } from "../interfaces/IAuthFile";
|
|
5
5
|
|
|
6
6
|
// The URL is detected once, to reuse forever
|
|
7
7
|
let resolvedUrl: string | undefined;
|
|
@@ -42,10 +42,10 @@ const readContents = async <T>(filePath: string): Promise<T | null> => {
|
|
|
42
42
|
* @param key Key to extract from the parsed JSON
|
|
43
43
|
* @returns The string value, or null if file/key missing or invalid
|
|
44
44
|
*/
|
|
45
|
-
const readConfigValue = async <T>(
|
|
45
|
+
const readConfigValue = async <T, U>(
|
|
46
46
|
filePath: string,
|
|
47
47
|
key: string,
|
|
48
|
-
): Promise<
|
|
48
|
+
): Promise<U> => {
|
|
49
49
|
const cfg = await readContents<T>(filePath);
|
|
50
50
|
return (cfg as Record<string, any>)?.[key] || null;
|
|
51
51
|
};
|
|
@@ -60,8 +60,11 @@ export const resolveApiKey = async (): Promise<string> => {
|
|
|
60
60
|
const authPath = join(process.env.HOME || ".", ".pi", "agent", "auth.json");
|
|
61
61
|
if (!(await fileExists(authPath))) return placeholder;
|
|
62
62
|
|
|
63
|
-
const
|
|
64
|
-
|
|
63
|
+
const cfg = await readConfigValue<IAuthFile, IAuth | null>(
|
|
64
|
+
authPath,
|
|
65
|
+
PROVIDER_ID,
|
|
66
|
+
);
|
|
67
|
+
return cfg?.key ?? placeholder;
|
|
65
68
|
};
|
|
66
69
|
|
|
67
70
|
/**
|
|
@@ -77,7 +80,11 @@ const resolveGlobalUrl = async (): Promise<string | null> => {
|
|
|
77
80
|
);
|
|
78
81
|
|
|
79
82
|
if (!(await fileExists(globalPath))) return null;
|
|
80
|
-
|
|
83
|
+
|
|
84
|
+
return readConfigValue<Record<string, string>, string>(
|
|
85
|
+
globalPath,
|
|
86
|
+
"llamaServerUrl",
|
|
87
|
+
);
|
|
81
88
|
};
|
|
82
89
|
|
|
83
90
|
/**
|
|
@@ -89,7 +96,7 @@ const resolveProjectUrl = async (cwd: string): Promise<string | null> => {
|
|
|
89
96
|
const projectPath = join(cwd, ".pi", "llama-server.json");
|
|
90
97
|
|
|
91
98
|
if (!(await fileExists(projectPath))) return null;
|
|
92
|
-
return readConfigValue<Record<string, string
|
|
99
|
+
return readConfigValue<Record<string, string>, string>(projectPath, "url");
|
|
93
100
|
};
|
|
94
101
|
|
|
95
102
|
/**
|
package/src/tools/retriever.ts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import { ISingleModel } from "../interfaces/ISingleModel";
|
|
2
1
|
import { IRouterModel } from "../interfaces/IRouterModel";
|
|
3
|
-
import {
|
|
4
|
-
import { RouterModel } from "../models/routerModel";
|
|
2
|
+
import { ISingleModel } from "../interfaces/ISingleModel";
|
|
5
3
|
import { BaseModel } from "../models/baseModel";
|
|
4
|
+
import { RouterModel } from "../models/routerModel";
|
|
5
|
+
import { SingleModel } from "../models/singleModel";
|
|
6
6
|
import { resolveApiKey, resolveUrl } from "./resolver";
|
|
7
7
|
|
|
8
8
|
/**
|
package/tsconfig.json
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"target": "ES2020",
|
|
4
|
+
"module": "NodeNext",
|
|
5
|
+
"moduleResolution": "NodeNext",
|
|
6
|
+
"strict": true,
|
|
7
|
+
"esModuleInterop": true,
|
|
8
|
+
"skipLibCheck": true,
|
|
9
|
+
"forceConsistentCasingInFileNames": true,
|
|
10
|
+
"types": ["node"]
|
|
11
|
+
},
|
|
12
|
+
"include": ["src/**/*.ts"]
|
|
13
|
+
}
|