pi-llama-cpp 0.14.0 → 0.16.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/README.md +52 -13
- package/package.json +3 -3
- package/src/api/client.ts +59 -22
- package/src/constants.ts +5 -0
- package/src/enums/mode.ts +1 -0
- package/src/index.ts +2 -1
- package/src/interfaces/endpoints/models.ts +6 -0
- package/src/interfaces/endpoints/props.ts +14 -0
- package/src/interfaces/settings.ts +6 -0
- package/src/managers/command/models.ts +12 -6
- package/src/managers/command.ts +6 -1
- package/src/managers/server.ts +30 -38
- package/src/managers/settings.ts +23 -3
- package/src/models/legacyModel.ts +3 -3
- package/src/models/llamaSwapModel.ts +90 -0
- package/src/models/routerModel.ts +2 -2
- package/src/models/singleModel.ts +2 -2
- package/src/server.ts +42 -15
- package/src/sse/manager.ts +2 -2
- package/src/ui/dialog/confirm.ts +1 -1
- package/src/ui/dialog/input.ts +1 -1
- package/src/ui/editors/editorOptions.ts +8 -1
- package/src/ui/editors/itemBuilder.ts +2 -6
- package/src/ui/editors/listEditor.ts +24 -15
- package/src/ui/editors/override/entryEditor.ts +105 -23
- package/src/ui/editors/override/fields/base.ts +53 -0
- package/src/ui/editors/override/fields/capabilities.ts +36 -0
- package/src/ui/editors/override/fields/cost.ts +64 -0
- package/src/ui/editors/override/fields/index.ts +68 -0
- package/src/ui/editors/override/fields/numeric.ts +55 -0
- package/src/ui/editors/override/fields/pattern.ts +24 -0
- package/src/ui/editors/override/fields/reasoning.ts +33 -0
- package/src/ui/editors/override/itemBuilder.ts +4 -4
- package/src/ui/editors/server/fields.ts +2 -2
- package/src/ui/editors/server/itemBuilder.ts +2 -3
- package/src/ui/editors/server/serverEditor.ts +58 -22
- package/src/ui/editors/server/utils.ts +50 -0
- package/src/ui/settings/index.ts +11 -0
- package/src/ui/strings.ts +17 -0
- package/src/utils/credentialResolver.ts +110 -0
- package/src/utils/health.ts +2 -2
- package/src/utils/urlResolver.ts +1 -1
- package/tests/{commandManager.test.ts → command/commandManager.test.ts} +80 -9
- package/tests/{events.test.ts → events/events.test.ts} +6 -6
- package/tests/mocks.ts +4 -0
- package/tests/{legacyModel.test.ts → models/legacyModel.test.ts} +5 -5
- package/tests/models/llamaSwapModel.test.ts +327 -0
- package/tests/{routerModel.test.ts → models/routerModel.test.ts} +5 -5
- package/tests/{singleModel.test.ts → models/singleModel.test.ts} +5 -5
- package/tests/{health.test.ts → server/health.test.ts} +4 -4
- package/tests/{server.test.ts → server/server.test.ts} +57 -7
- package/tests/{serverManager.test.ts → server/serverManager.test.ts} +4 -4
- package/tests/{settings.test.ts → settings/settings.test.ts} +198 -355
- package/tests/{settingsStore.test.ts → settings/settingsStore.test.ts} +1 -1
- package/tests/{sseManager.test.ts → sse/sseManager.test.ts} +2 -2
- package/tests/{dialog.test.ts → ui/dialog.test.ts} +5 -4
- package/tests/{overrides.test.ts → ui/overrides.test.ts} +9 -42
- package/tests/utils/credentialResolver.test.ts +66 -0
- package/tests/utils/urlResolver.test.ts +116 -0
- package/src/ui/editors/override/fields.ts +0 -294
- package/src/ui/editors/override/handlers.ts +0 -118
- package/src/ui/editors/server/handlers.ts +0 -32
|
@@ -6,12 +6,17 @@ import {
|
|
|
6
6
|
POLLING_TIMEOUT,
|
|
7
7
|
PROVIDER_PREFIX,
|
|
8
8
|
SERVER_TIMEOUT,
|
|
9
|
-
} from "
|
|
10
|
-
import type { ModelOverride } from "
|
|
11
|
-
import {
|
|
12
|
-
|
|
9
|
+
} from "../../src/constants";
|
|
10
|
+
import type { ModelOverride } from "../../src/interfaces/settings";
|
|
11
|
+
import {
|
|
12
|
+
createSettingsManager,
|
|
13
|
+
type LlamaSettingsManager,
|
|
14
|
+
} from "../../src/managers/settings";
|
|
15
|
+
import { Server } from "../../src/server";
|
|
16
|
+
|
|
17
|
+
let manager: LlamaSettingsManager;
|
|
13
18
|
|
|
14
|
-
// Hoisted mock instances
|
|
19
|
+
// Hoisted mock instances
|
|
15
20
|
const mockReadStoredCredential = vi.hoisted(() => vi.fn());
|
|
16
21
|
|
|
17
22
|
const mockSettingsManager = vi.hoisted(() => ({
|
|
@@ -43,314 +48,117 @@ vi.mock("node:fs/promises", () => ({
|
|
|
43
48
|
import { getAgentDir } from "@earendil-works/pi-coding-agent";
|
|
44
49
|
import { access } from "node:fs/promises";
|
|
45
50
|
|
|
46
|
-
describe("
|
|
47
|
-
const mockGetAgentDir = vi.mocked(getAgentDir);
|
|
48
|
-
const mockGetProjectSettings = vi.mocked(
|
|
49
|
-
mockSettingsManager.getProjectSettings,
|
|
50
|
-
);
|
|
51
|
-
const mockGetGlobalSettings = vi.mocked(
|
|
52
|
-
mockSettingsManager.getGlobalSettings,
|
|
53
|
-
);
|
|
54
|
-
|
|
55
|
-
afterEach(() => {
|
|
56
|
-
delete process.env.LLAMA_SERVER_URL;
|
|
57
|
-
vi.resetModules();
|
|
58
|
-
});
|
|
59
|
-
|
|
60
|
-
beforeEach(() => {
|
|
61
|
-
vi.clearAllMocks();
|
|
62
|
-
mockGetAgentDir.mockReturnValue("/fake/agent/dir");
|
|
63
|
-
// Default: no settings found
|
|
64
|
-
mockGetProjectSettings.mockReturnValue({});
|
|
65
|
-
mockGetGlobalSettings.mockReturnValue({});
|
|
66
|
-
});
|
|
67
|
-
|
|
68
|
-
it("should return default URL when no config is found", async () => {
|
|
69
|
-
// Ensure env var is not set (and not inherited from environment)
|
|
70
|
-
delete process.env.LLAMA_SERVER_URL;
|
|
71
|
-
|
|
72
|
-
const result = await settings.resolveUrls();
|
|
73
|
-
|
|
74
|
-
expect(result).toEqual([LLAMA_SERVER_URL]);
|
|
75
|
-
});
|
|
76
|
-
|
|
77
|
-
it("should prioritize env variable over project config", async () => {
|
|
78
|
-
mockGetProjectSettings.mockReturnValue({
|
|
79
|
-
llamaServerUrl: "http://localhost:9999",
|
|
80
|
-
});
|
|
81
|
-
process.env.LLAMA_SERVER_URL = "http://env-url:8080";
|
|
82
|
-
|
|
83
|
-
const result = await settings.resolveUrls();
|
|
84
|
-
|
|
85
|
-
expect(result).toEqual(["http://env-url:8080"]);
|
|
86
|
-
});
|
|
87
|
-
|
|
88
|
-
it("should use env variable when no other config exists", async () => {
|
|
89
|
-
process.env.LLAMA_SERVER_URL = "http://env-url:8080";
|
|
90
|
-
|
|
91
|
-
const result = await settings.resolveUrls();
|
|
92
|
-
|
|
93
|
-
expect(result).toEqual(["http://env-url:8080"]);
|
|
94
|
-
});
|
|
95
|
-
|
|
96
|
-
it("should use legacy llamaServerUrl when env is not set", async () => {
|
|
97
|
-
mockGetProjectSettings.mockReturnValue({
|
|
98
|
-
llamaServerUrl: "http://project:9999",
|
|
99
|
-
});
|
|
100
|
-
|
|
101
|
-
const result = await settings.resolveUrls();
|
|
102
|
-
|
|
103
|
-
expect(result).toEqual(["http://project:9999"]);
|
|
104
|
-
});
|
|
105
|
-
|
|
106
|
-
it("should use global settings when no project config or env exists", async () => {
|
|
107
|
-
mockGetGlobalSettings.mockReturnValue({
|
|
108
|
-
llamaServerUrl: "http://global:8080",
|
|
109
|
-
});
|
|
110
|
-
|
|
111
|
-
const result = await settings.resolveUrls();
|
|
112
|
-
|
|
113
|
-
expect(result).toEqual(["http://global:8080"]);
|
|
114
|
-
});
|
|
115
|
-
|
|
116
|
-
it("should strip trailing slashes from resolved URL", async () => {
|
|
117
|
-
process.env.LLAMA_SERVER_URL = "http://localhost:8080/";
|
|
118
|
-
|
|
119
|
-
const result = await settings.resolveUrls();
|
|
120
|
-
|
|
121
|
-
expect(result).toEqual(["http://localhost:8080"]);
|
|
122
|
-
});
|
|
123
|
-
|
|
124
|
-
it("should cache the resolved URL on subsequent calls", async () => {
|
|
125
|
-
process.env.LLAMA_SERVER_URL = "http://first:8080";
|
|
126
|
-
|
|
127
|
-
const result1 = await settings.resolveUrls();
|
|
128
|
-
const result2 = await settings.resolveUrls();
|
|
129
|
-
|
|
130
|
-
expect(result1).toEqual(["http://first:8080"]);
|
|
131
|
-
expect(result2).toEqual(["http://first:8080"]);
|
|
132
|
-
});
|
|
133
|
-
|
|
134
|
-
it("should handle multiple URLs separated by semicolons", async () => {
|
|
135
|
-
process.env.LLAMA_SERVER_URL = "http://first:8080;http://second:9090/";
|
|
136
|
-
|
|
137
|
-
const result = await settings.resolveUrls();
|
|
138
|
-
|
|
139
|
-
expect(result).toEqual(["http://first:8080", "http://second:9090"]);
|
|
140
|
-
});
|
|
141
|
-
|
|
142
|
-
it("should drop env URLs without an http(s) scheme, warn, and fall through", async () => {
|
|
143
|
-
process.env.LLAMA_SERVER_URL = "127.0.0.1:8080";
|
|
144
|
-
|
|
145
|
-
const result = await settings.resolveUrls();
|
|
146
|
-
|
|
147
|
-
expect(result).toEqual([LLAMA_SERVER_URL]);
|
|
148
|
-
expect(settings.takeWarnings()).toEqual([
|
|
149
|
-
"Ignoring invalid server URL '127.0.0.1:8080' (needs http(s)://)",
|
|
150
|
-
]);
|
|
151
|
-
expect(settings.takeWarnings()).toEqual([]); // drained
|
|
152
|
-
});
|
|
153
|
-
|
|
154
|
-
it("should drop server entries without an http(s) scheme and warn", async () => {
|
|
155
|
-
mockGetProjectSettings.mockReturnValue({
|
|
156
|
-
llamaSettings: {
|
|
157
|
-
servers: [{ url: "127.0.0.1:8080" }, { url: "http://good:8080/" }],
|
|
158
|
-
},
|
|
159
|
-
});
|
|
160
|
-
|
|
161
|
-
const result = await settings.resolveUrls();
|
|
162
|
-
|
|
163
|
-
expect(result).toEqual(["http://good:8080"]);
|
|
164
|
-
expect(settings.takeWarnings()).toEqual([
|
|
165
|
-
"Ignoring invalid server URL '127.0.0.1:8080' (needs http(s)://)",
|
|
166
|
-
]);
|
|
167
|
-
expect(settings.takeWarnings()).toEqual([]); // drained
|
|
168
|
-
});
|
|
169
|
-
});
|
|
170
|
-
|
|
171
|
-
describe("llamaSettings.servers resolution", () => {
|
|
51
|
+
describe("API key resolution", () => {
|
|
172
52
|
const mockGetAgentDir = vi.mocked(getAgentDir);
|
|
173
|
-
const mockGetProjectSettings = vi.mocked(
|
|
174
|
-
mockSettingsManager.getProjectSettings,
|
|
175
|
-
);
|
|
176
|
-
const mockGetGlobalSettings = vi.mocked(
|
|
177
|
-
mockSettingsManager.getGlobalSettings,
|
|
178
|
-
);
|
|
179
53
|
|
|
180
|
-
afterEach(() => {
|
|
181
|
-
delete process.env.LLAMA_SERVER_URL;
|
|
182
|
-
vi.resetModules();
|
|
183
|
-
});
|
|
54
|
+
afterEach(() => {});
|
|
184
55
|
|
|
185
56
|
beforeEach(() => {
|
|
186
57
|
vi.clearAllMocks();
|
|
187
58
|
mockGetAgentDir.mockReturnValue("/fake/agent/dir");
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
});
|
|
191
|
-
|
|
192
|
-
it("should resolve URLs from llamaSettings.servers in project config", async () => {
|
|
193
|
-
mockGetProjectSettings.mockReturnValue({
|
|
194
|
-
llamaSettings: {
|
|
195
|
-
servers: [
|
|
196
|
-
{ url: "http://project-server:8080" },
|
|
197
|
-
{ url: "http://project-server2:9090" },
|
|
198
|
-
],
|
|
199
|
-
},
|
|
200
|
-
});
|
|
201
|
-
|
|
202
|
-
const result = await settings.resolveUrls();
|
|
203
|
-
|
|
204
|
-
expect(result).toEqual([
|
|
205
|
-
"http://project-server:8080",
|
|
206
|
-
"http://project-server2:9090",
|
|
207
|
-
]);
|
|
59
|
+
mockReadStoredCredential.mockReturnValue(undefined);
|
|
60
|
+
manager = createSettingsManager();
|
|
208
61
|
});
|
|
209
62
|
|
|
210
|
-
it("should
|
|
211
|
-
|
|
212
|
-
llamaSettings: {
|
|
213
|
-
servers: [{ url: "http://project:8080" }],
|
|
214
|
-
},
|
|
215
|
-
});
|
|
216
|
-
mockGetGlobalSettings.mockReturnValue({
|
|
217
|
-
llamaSettings: {
|
|
218
|
-
servers: [{ url: "http://global:9090" }],
|
|
219
|
-
},
|
|
220
|
-
});
|
|
63
|
+
it("should return placeholder when credential is not found", () => {
|
|
64
|
+
mockReadStoredCredential.mockReturnValue(undefined);
|
|
221
65
|
|
|
222
|
-
const result =
|
|
66
|
+
const result = manager.resolveApiKey("llama-server=http://127.0.0.1:8080");
|
|
223
67
|
|
|
224
|
-
expect(result).toEqual(
|
|
68
|
+
expect(result).toEqual(API_KEY_PLACEHOLDER);
|
|
225
69
|
});
|
|
226
70
|
|
|
227
|
-
it("should
|
|
228
|
-
|
|
229
|
-
llamaSettings: {
|
|
230
|
-
servers: [{ url: "http://global:8080" }],
|
|
231
|
-
},
|
|
232
|
-
});
|
|
71
|
+
it("should return placeholder when apiKey is missing from credential", () => {
|
|
72
|
+
mockReadStoredCredential.mockReturnValue({});
|
|
233
73
|
|
|
234
|
-
const result =
|
|
74
|
+
const result = manager.resolveApiKey("llama-server=http://127.0.0.1:8080");
|
|
235
75
|
|
|
236
|
-
expect(result).toEqual(
|
|
76
|
+
expect(result).toEqual(API_KEY_PLACEHOLDER);
|
|
237
77
|
});
|
|
238
78
|
|
|
239
|
-
it("should
|
|
240
|
-
|
|
241
|
-
mockGetProjectSettings.mockReturnValue({
|
|
242
|
-
llamaSettings: {
|
|
243
|
-
servers: [{ url: "http://server:9090" }],
|
|
244
|
-
},
|
|
245
|
-
});
|
|
79
|
+
it("should return the key when present in credential", () => {
|
|
80
|
+
mockReadStoredCredential.mockReturnValue({ key: "test-api-key" });
|
|
246
81
|
|
|
247
|
-
const result =
|
|
82
|
+
const result = manager.resolveApiKey("llama-server=http://127.0.0.1:8080");
|
|
248
83
|
|
|
249
|
-
expect(result).toEqual(
|
|
84
|
+
expect(result).toEqual("test-api-key");
|
|
250
85
|
});
|
|
251
86
|
|
|
252
|
-
it("should
|
|
253
|
-
|
|
254
|
-
llamaSettings: {
|
|
255
|
-
servers: [{ url: "http://server:9090" }],
|
|
256
|
-
},
|
|
257
|
-
});
|
|
87
|
+
it("should call readStoredCredential with the provider ID", () => {
|
|
88
|
+
mockReadStoredCredential.mockReturnValue({ key: "test-key" });
|
|
258
89
|
|
|
259
|
-
|
|
90
|
+
manager.resolveApiKey("llama-server=http://127.0.0.1:8080");
|
|
260
91
|
|
|
261
|
-
expect(
|
|
92
|
+
expect(mockReadStoredCredential).toHaveBeenCalledWith(
|
|
93
|
+
"llama-server=http://127.0.0.1:8080",
|
|
94
|
+
);
|
|
262
95
|
});
|
|
263
96
|
|
|
264
|
-
it("should
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
llamaSettings: {
|
|
268
|
-
servers: [{ url: "http://server:9090" }],
|
|
269
|
-
},
|
|
270
|
-
});
|
|
97
|
+
it("should resolve $VAR env reference from process.env", () => {
|
|
98
|
+
mockReadStoredCredential.mockReturnValue({ key: "$MY_API_KEY" });
|
|
99
|
+
process.env.MY_API_KEY = "env-resolved-key";
|
|
271
100
|
|
|
272
|
-
const result =
|
|
101
|
+
const result = manager.resolveApiKey("llama-server=http://127.0.0.1:8080");
|
|
273
102
|
|
|
274
|
-
expect(result).
|
|
103
|
+
expect(result).toBe("env-resolved-key");
|
|
104
|
+
delete process.env.MY_API_KEY;
|
|
275
105
|
});
|
|
276
106
|
|
|
277
|
-
it("should
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
llamaSettings: {
|
|
281
|
-
servers: [],
|
|
282
|
-
},
|
|
283
|
-
});
|
|
107
|
+
it("should resolve ${VAR} braced env reference from process.env", () => {
|
|
108
|
+
mockReadStoredCredential.mockReturnValue({ key: "${MY_API_KEY}" });
|
|
109
|
+
process.env.MY_API_KEY = "braced-resolved-key";
|
|
284
110
|
|
|
285
|
-
const result =
|
|
111
|
+
const result = manager.resolveApiKey("llama-server=http://127.0.0.1:8080");
|
|
286
112
|
|
|
287
|
-
expect(result).
|
|
113
|
+
expect(result).toBe("braced-resolved-key");
|
|
114
|
+
delete process.env.MY_API_KEY;
|
|
288
115
|
});
|
|
289
116
|
|
|
290
|
-
it("should
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
},
|
|
117
|
+
it("should prefer credential.env over process.env", () => {
|
|
118
|
+
mockReadStoredCredential.mockReturnValue({
|
|
119
|
+
key: "$MY_KEY",
|
|
120
|
+
env: { MY_KEY: "credential-env-value" },
|
|
295
121
|
});
|
|
122
|
+
process.env.MY_KEY = "process-env-value";
|
|
296
123
|
|
|
297
|
-
const result =
|
|
124
|
+
const result = manager.resolveApiKey("llama-server=http://127.0.0.1:8080");
|
|
298
125
|
|
|
299
|
-
expect(result).
|
|
126
|
+
expect(result).toBe("credential-env-value");
|
|
127
|
+
delete process.env.MY_KEY;
|
|
300
128
|
});
|
|
301
|
-
});
|
|
302
129
|
|
|
303
|
-
|
|
304
|
-
|
|
130
|
+
it("should return placeholder when env var is not set", () => {
|
|
131
|
+
mockReadStoredCredential.mockReturnValue({ key: "$UNSET_VAR" });
|
|
132
|
+
delete process.env.UNSET_VAR;
|
|
305
133
|
|
|
306
|
-
|
|
307
|
-
vi.resetModules();
|
|
308
|
-
});
|
|
134
|
+
const result = manager.resolveApiKey("llama-server=http://127.0.0.1:8080");
|
|
309
135
|
|
|
310
|
-
|
|
311
|
-
vi.clearAllMocks();
|
|
312
|
-
mockGetAgentDir.mockReturnValue("/fake/agent/dir");
|
|
313
|
-
mockReadStoredCredential.mockReturnValue(undefined);
|
|
136
|
+
expect(result).toBe(API_KEY_PLACEHOLDER);
|
|
314
137
|
});
|
|
315
138
|
|
|
316
|
-
|
|
317
|
-
|
|
139
|
+
// Shell command tests are covered by Pi core's resolve-config-value tests.
|
|
140
|
+
// The local implementation delegates to execSync which is tested there.
|
|
318
141
|
|
|
319
|
-
|
|
142
|
+
it("should escape $$ as literal $", () => {
|
|
143
|
+
mockReadStoredCredential.mockReturnValue({ key: "$$LITERAL" });
|
|
320
144
|
|
|
321
|
-
|
|
322
|
-
});
|
|
323
|
-
|
|
324
|
-
it("should return placeholder when apiKey is missing from credential", () => {
|
|
325
|
-
mockReadStoredCredential.mockReturnValue({});
|
|
326
|
-
|
|
327
|
-
const result = settings.resolveApiKey("llama-server=http://127.0.0.1:8080");
|
|
145
|
+
const result = manager.resolveApiKey("llama-server=http://127.0.0.1:8080");
|
|
328
146
|
|
|
329
|
-
expect(result).
|
|
147
|
+
expect(result).toBe("$LITERAL");
|
|
330
148
|
});
|
|
331
149
|
|
|
332
|
-
it("should
|
|
333
|
-
mockReadStoredCredential.mockReturnValue({ key: "
|
|
334
|
-
|
|
335
|
-
const result = settings.resolveApiKey("llama-server=http://127.0.0.1:8080");
|
|
336
|
-
|
|
337
|
-
expect(result).toEqual("test-api-key");
|
|
338
|
-
});
|
|
339
|
-
|
|
340
|
-
it("should call readStoredCredential with the provider ID", () => {
|
|
341
|
-
mockReadStoredCredential.mockReturnValue({ key: "test-key" });
|
|
150
|
+
it("should escape $! as literal !", () => {
|
|
151
|
+
mockReadStoredCredential.mockReturnValue({ key: "$!LITERAL" });
|
|
342
152
|
|
|
343
|
-
|
|
153
|
+
const result = manager.resolveApiKey("llama-server=http://127.0.0.1:8080");
|
|
344
154
|
|
|
345
|
-
expect(
|
|
346
|
-
"llama-server=http://127.0.0.1:8080",
|
|
347
|
-
);
|
|
155
|
+
expect(result).toBe("!LITERAL");
|
|
348
156
|
});
|
|
349
157
|
});
|
|
350
158
|
|
|
351
159
|
describe("Server with custom id", () => {
|
|
352
160
|
it("should use custom id as providerId when provided", () => {
|
|
353
|
-
const server = new Server(
|
|
161
|
+
const server = new Server(manager, {
|
|
354
162
|
baseUrl: "http://127.0.0.1:8080",
|
|
355
163
|
customId: "my-custom-id",
|
|
356
164
|
});
|
|
@@ -359,7 +167,9 @@ describe("Server with custom id", () => {
|
|
|
359
167
|
});
|
|
360
168
|
|
|
361
169
|
it("should fall back to URL-based providerId when no custom id", () => {
|
|
362
|
-
const server = new Server(
|
|
170
|
+
const server = new Server(manager, {
|
|
171
|
+
baseUrl: "http://127.0.0.1:8080",
|
|
172
|
+
});
|
|
363
173
|
|
|
364
174
|
expect(server.providerId).toEqual(
|
|
365
175
|
`${PROVIDER_PREFIX}=http://127.0.0.1:8080`,
|
|
@@ -367,7 +177,7 @@ describe("Server with custom id", () => {
|
|
|
367
177
|
});
|
|
368
178
|
|
|
369
179
|
it("should try custom id first in getApiKey(), then fall back to URL-based", () => {
|
|
370
|
-
const server = new Server(
|
|
180
|
+
const server = new Server(manager, {
|
|
371
181
|
baseUrl: "http://127.0.0.1:8080",
|
|
372
182
|
customId: "my-custom-id",
|
|
373
183
|
});
|
|
@@ -380,7 +190,7 @@ describe("Server with custom id", () => {
|
|
|
380
190
|
.mockReturnValueOnce(API_KEY_PLACEHOLDER)
|
|
381
191
|
.mockReturnValueOnce({ key: "fallback-key" });
|
|
382
192
|
|
|
383
|
-
const result = server.
|
|
193
|
+
const result = server.apiKey;
|
|
384
194
|
|
|
385
195
|
expect(result).toEqual("fallback-key");
|
|
386
196
|
expect(mockReadStoredCredential).toHaveBeenNthCalledWith(1, "my-custom-id");
|
|
@@ -393,12 +203,12 @@ describe("Server with custom id", () => {
|
|
|
393
203
|
it("should return custom id key directly when found", () => {
|
|
394
204
|
mockReadStoredCredential.mockReturnValue({ key: "custom-key" });
|
|
395
205
|
|
|
396
|
-
const server = new Server(
|
|
206
|
+
const server = new Server(manager, {
|
|
397
207
|
baseUrl: "http://127.0.0.1:8080",
|
|
398
208
|
customId: "my-custom-id",
|
|
399
209
|
});
|
|
400
210
|
|
|
401
|
-
const result = server.
|
|
211
|
+
const result = server.apiKey;
|
|
402
212
|
|
|
403
213
|
expect(result).toEqual("custom-key");
|
|
404
214
|
expect(mockReadStoredCredential).toHaveBeenCalledWith("my-custom-id");
|
|
@@ -407,7 +217,7 @@ describe("Server with custom id", () => {
|
|
|
407
217
|
|
|
408
218
|
describe("Server with custom name", () => {
|
|
409
219
|
it("should use custom name as suffix in providerName", () => {
|
|
410
|
-
const server = new Server(
|
|
220
|
+
const server = new Server(manager, {
|
|
411
221
|
baseUrl: "http://127.0.0.1:8080",
|
|
412
222
|
customName: "Remote Server",
|
|
413
223
|
});
|
|
@@ -416,13 +226,15 @@ describe("Server with custom name", () => {
|
|
|
416
226
|
});
|
|
417
227
|
|
|
418
228
|
it("should fall back to URL-based name when no custom name", () => {
|
|
419
|
-
const server = new Server(
|
|
229
|
+
const server = new Server(manager, {
|
|
230
|
+
baseUrl: "http://127.0.0.1:8080",
|
|
231
|
+
});
|
|
420
232
|
|
|
421
233
|
expect(server.providerName).toEqual(`Llama.cpp (http://127.0.0.1:8080)`);
|
|
422
234
|
});
|
|
423
235
|
|
|
424
236
|
it("should use custom name even with custom id", () => {
|
|
425
|
-
const server = new Server(
|
|
237
|
+
const server = new Server(manager, {
|
|
426
238
|
baseUrl: "http://127.0.0.1:8080",
|
|
427
239
|
customId: "my-custom-id",
|
|
428
240
|
customName: "Remote Server",
|
|
@@ -434,30 +246,22 @@ describe("Server with custom name", () => {
|
|
|
434
246
|
});
|
|
435
247
|
|
|
436
248
|
describe("reactToModelSelect and autoloadOnMessage fallbacks", () => {
|
|
437
|
-
afterEach(() => {
|
|
438
|
-
vi.resetModules();
|
|
439
|
-
});
|
|
249
|
+
afterEach(() => {});
|
|
440
250
|
|
|
441
251
|
it("should return true when reactToModelSelect is not set", async () => {
|
|
442
|
-
const
|
|
443
|
-
|
|
444
|
-
const result = await settings.resolveReactToModelSelect();
|
|
252
|
+
const result = await manager.resolveReactToModelSelect();
|
|
445
253
|
|
|
446
254
|
expect(result).toBe(true);
|
|
447
255
|
});
|
|
448
256
|
|
|
449
257
|
it("should return false when autoloadOnMessage is not set", async () => {
|
|
450
|
-
const
|
|
451
|
-
|
|
452
|
-
const result = await settings.resolveAutoloadOnMessage();
|
|
258
|
+
const result = await manager.resolveAutoloadOnMessage();
|
|
453
259
|
|
|
454
260
|
expect(result).toBe(false);
|
|
455
261
|
});
|
|
456
262
|
|
|
457
263
|
it("should return 'asc' when sortBy is not set", async () => {
|
|
458
|
-
const
|
|
459
|
-
|
|
460
|
-
const result = await settings.resolveSortBy();
|
|
264
|
+
const result = await manager.resolveSortBy();
|
|
461
265
|
|
|
462
266
|
expect(result).toBe("asc");
|
|
463
267
|
});
|
|
@@ -470,10 +274,72 @@ describe("reactToModelSelect and autoloadOnMessage fallbacks", () => {
|
|
|
470
274
|
},
|
|
471
275
|
});
|
|
472
276
|
|
|
473
|
-
const
|
|
277
|
+
const manager = createSettingsManager();
|
|
278
|
+
|
|
279
|
+
expect(await manager.resolveReactToModelSelect()).toBe(false);
|
|
280
|
+
expect(await manager.resolveAutoloadOnMessage()).toBe(true);
|
|
281
|
+
});
|
|
282
|
+
});
|
|
283
|
+
|
|
284
|
+
describe("resolveShowServerUrls", () => {
|
|
285
|
+
const mockGetAgentDir = vi.mocked(getAgentDir);
|
|
286
|
+
const mockGetProjectSettings = vi.mocked(
|
|
287
|
+
mockSettingsManager.getProjectSettings,
|
|
288
|
+
);
|
|
289
|
+
const mockGetGlobalSettings = vi.mocked(
|
|
290
|
+
mockSettingsManager.getGlobalSettings,
|
|
291
|
+
);
|
|
292
|
+
|
|
293
|
+
afterEach(() => {});
|
|
294
|
+
|
|
295
|
+
beforeEach(() => {
|
|
296
|
+
vi.clearAllMocks();
|
|
297
|
+
mockGetAgentDir.mockReturnValue("/fake/agent/dir");
|
|
298
|
+
mockGetProjectSettings.mockReturnValue({});
|
|
299
|
+
mockGetGlobalSettings.mockReturnValue({});
|
|
300
|
+
manager = createSettingsManager();
|
|
301
|
+
});
|
|
302
|
+
|
|
303
|
+
it("should return true (default) when showServerUrls is not set", async () => {
|
|
304
|
+
const result = await manager.resolveShowServerUrls();
|
|
305
|
+
|
|
306
|
+
expect(result).toBe(true);
|
|
307
|
+
});
|
|
308
|
+
|
|
309
|
+
it("should return false when explicitly set to false", async () => {
|
|
310
|
+
mockGetProjectSettings.mockReturnValue({
|
|
311
|
+
llamaSettings: {
|
|
312
|
+
showServerUrls: false,
|
|
313
|
+
},
|
|
314
|
+
});
|
|
315
|
+
|
|
316
|
+
const manager = createSettingsManager();
|
|
474
317
|
|
|
475
|
-
expect(await
|
|
476
|
-
|
|
318
|
+
expect(await manager.resolveShowServerUrls()).toBe(false);
|
|
319
|
+
});
|
|
320
|
+
|
|
321
|
+
it("should return true when explicitly set to true", async () => {
|
|
322
|
+
mockGetProjectSettings.mockReturnValue({
|
|
323
|
+
llamaSettings: {
|
|
324
|
+
showServerUrls: true,
|
|
325
|
+
},
|
|
326
|
+
});
|
|
327
|
+
|
|
328
|
+
const manager = createSettingsManager();
|
|
329
|
+
|
|
330
|
+
expect(await manager.resolveShowServerUrls()).toBe(true);
|
|
331
|
+
});
|
|
332
|
+
|
|
333
|
+
it("should use global settings when no project config exists", async () => {
|
|
334
|
+
mockGetGlobalSettings.mockReturnValue({
|
|
335
|
+
llamaSettings: {
|
|
336
|
+
showServerUrls: false,
|
|
337
|
+
},
|
|
338
|
+
});
|
|
339
|
+
|
|
340
|
+
const manager = createSettingsManager();
|
|
341
|
+
|
|
342
|
+
expect(await manager.resolveShowServerUrls()).toBe(false);
|
|
477
343
|
});
|
|
478
344
|
});
|
|
479
345
|
|
|
@@ -488,7 +354,6 @@ describe("resolveServers", () => {
|
|
|
488
354
|
|
|
489
355
|
afterEach(() => {
|
|
490
356
|
delete process.env.LLAMA_SERVER_URL;
|
|
491
|
-
vi.resetModules();
|
|
492
357
|
});
|
|
493
358
|
|
|
494
359
|
beforeEach(() => {
|
|
@@ -496,6 +361,7 @@ describe("resolveServers", () => {
|
|
|
496
361
|
mockGetAgentDir.mockReturnValue("/fake/agent/dir");
|
|
497
362
|
mockGetProjectSettings.mockReturnValue({});
|
|
498
363
|
mockGetGlobalSettings.mockReturnValue({});
|
|
364
|
+
manager = createSettingsManager();
|
|
499
365
|
});
|
|
500
366
|
|
|
501
367
|
it("should use llamaSettings.servers when configured", async () => {
|
|
@@ -507,7 +373,7 @@ describe("resolveServers", () => {
|
|
|
507
373
|
},
|
|
508
374
|
});
|
|
509
375
|
|
|
510
|
-
const result = await
|
|
376
|
+
const result = await manager.resolveServers();
|
|
511
377
|
|
|
512
378
|
expect(result).toHaveLength(1);
|
|
513
379
|
expect(result[0].baseUrl).toBe("http://custom:8080");
|
|
@@ -517,14 +383,14 @@ describe("resolveServers", () => {
|
|
|
517
383
|
it("should fall back to resolveUrls when servers is empty", async () => {
|
|
518
384
|
process.env.LLAMA_SERVER_URL = "http://env-server:9090";
|
|
519
385
|
|
|
520
|
-
const result = await
|
|
386
|
+
const result = await manager.resolveServers();
|
|
521
387
|
|
|
522
388
|
expect(result).toHaveLength(1);
|
|
523
389
|
expect(result[0].baseUrl).toBe("http://env-server:9090");
|
|
524
390
|
});
|
|
525
391
|
|
|
526
392
|
it("should fall back to default URL when no config exists", async () => {
|
|
527
|
-
const result = await
|
|
393
|
+
const result = await manager.resolveServers();
|
|
528
394
|
|
|
529
395
|
expect(result).toHaveLength(1);
|
|
530
396
|
expect(result[0].baseUrl).toBe(LLAMA_SERVER_URL);
|
|
@@ -539,7 +405,7 @@ describe("resolveServers", () => {
|
|
|
539
405
|
},
|
|
540
406
|
});
|
|
541
407
|
|
|
542
|
-
const result = await
|
|
408
|
+
const result = await manager.resolveServers();
|
|
543
409
|
|
|
544
410
|
expect(result).toHaveLength(1);
|
|
545
411
|
expect(result[0].baseUrl).toBe("http://127.0.0.1:8080");
|
|
@@ -555,7 +421,7 @@ describe("resolveServers", () => {
|
|
|
555
421
|
});
|
|
556
422
|
process.env.LLAMA_SERVER_URL = "http://first:8080;http://second:9090";
|
|
557
423
|
|
|
558
|
-
const result = await
|
|
424
|
+
const result = await manager.resolveServers();
|
|
559
425
|
|
|
560
426
|
expect(result).toHaveLength(2);
|
|
561
427
|
expect(result[0].baseUrl).toBe("http://first:8080");
|
|
@@ -572,7 +438,7 @@ describe("resolveServers", () => {
|
|
|
572
438
|
},
|
|
573
439
|
});
|
|
574
440
|
|
|
575
|
-
const result = await
|
|
441
|
+
const result = await manager.resolveServers();
|
|
576
442
|
|
|
577
443
|
// env variable takes precedence via resolveUrls
|
|
578
444
|
expect(result).toHaveLength(1);
|
|
@@ -581,14 +447,10 @@ describe("resolveServers", () => {
|
|
|
581
447
|
});
|
|
582
448
|
|
|
583
449
|
describe("resolveTimeouts", () => {
|
|
584
|
-
afterEach(() => {
|
|
585
|
-
vi.resetModules();
|
|
586
|
-
});
|
|
450
|
+
afterEach(() => {});
|
|
587
451
|
|
|
588
452
|
it("should return default timeouts when not configured", async () => {
|
|
589
|
-
const
|
|
590
|
-
|
|
591
|
-
const result = await settings.resolveTimeouts();
|
|
453
|
+
const result = await manager.resolveTimeouts();
|
|
592
454
|
|
|
593
455
|
expect(result).toEqual({
|
|
594
456
|
pollingTimeout: POLLING_TIMEOUT,
|
|
@@ -603,9 +465,7 @@ describe("resolveTimeouts", () => {
|
|
|
603
465
|
},
|
|
604
466
|
});
|
|
605
467
|
|
|
606
|
-
const
|
|
607
|
-
|
|
608
|
-
const result = await settings.resolveTimeouts();
|
|
468
|
+
const result = await manager.resolveTimeouts();
|
|
609
469
|
|
|
610
470
|
expect(result.pollingTimeout).toBe(120000);
|
|
611
471
|
expect(result.serverTimeout).toBe(SERVER_TIMEOUT);
|
|
@@ -618,9 +478,7 @@ describe("resolveTimeouts", () => {
|
|
|
618
478
|
},
|
|
619
479
|
});
|
|
620
480
|
|
|
621
|
-
const
|
|
622
|
-
|
|
623
|
-
const result = await settings.resolveTimeouts();
|
|
481
|
+
const result = await manager.resolveTimeouts();
|
|
624
482
|
|
|
625
483
|
expect(result.pollingTimeout).toBe(POLLING_TIMEOUT);
|
|
626
484
|
expect(result.serverTimeout).toBe(3000);
|
|
@@ -634,9 +492,7 @@ describe("resolveTimeouts", () => {
|
|
|
634
492
|
},
|
|
635
493
|
});
|
|
636
494
|
|
|
637
|
-
const
|
|
638
|
-
|
|
639
|
-
const result = await settings.resolveTimeouts();
|
|
495
|
+
const result = await manager.resolveTimeouts();
|
|
640
496
|
|
|
641
497
|
expect(result).toEqual({
|
|
642
498
|
pollingTimeout: 90000,
|
|
@@ -648,21 +504,20 @@ describe("resolveTimeouts", () => {
|
|
|
648
504
|
describe("Thinking config resolution", () => {
|
|
649
505
|
const mockGetAgentDir = vi.mocked(getAgentDir);
|
|
650
506
|
|
|
651
|
-
afterEach(() => {
|
|
652
|
-
vi.resetModules();
|
|
653
|
-
});
|
|
507
|
+
afterEach(() => {});
|
|
654
508
|
|
|
655
509
|
beforeEach(() => {
|
|
656
510
|
vi.clearAllMocks();
|
|
657
511
|
mockGetAgentDir.mockReturnValue("/fake/agent/dir");
|
|
658
512
|
mockSettingsManager.getDefaultThinkingLevel.mockReturnValue("medium");
|
|
659
513
|
mockSettingsManager.getThinkingBudgets.mockReturnValue({});
|
|
514
|
+
manager = createSettingsManager();
|
|
660
515
|
});
|
|
661
516
|
|
|
662
517
|
it("should return the default thinking level from SettingsManager", () => {
|
|
663
518
|
mockSettingsManager.getDefaultThinkingLevel.mockReturnValue("low");
|
|
664
519
|
|
|
665
|
-
const result =
|
|
520
|
+
const result = manager.resolveThinkingLevel();
|
|
666
521
|
|
|
667
522
|
expect(result).toEqual("low");
|
|
668
523
|
});
|
|
@@ -672,7 +527,7 @@ describe("Thinking config resolution", () => {
|
|
|
672
527
|
low: 4096,
|
|
673
528
|
});
|
|
674
529
|
|
|
675
|
-
const result =
|
|
530
|
+
const result = manager.resolveThinkingBudgets();
|
|
676
531
|
|
|
677
532
|
expect(result).toEqual(
|
|
678
533
|
expect.objectContaining({
|
|
@@ -706,9 +561,7 @@ describe("setLlamaSetting", () => {
|
|
|
706
561
|
const PROJECT_SETTINGS_PATH = "/fake/project/.pi/settings.json";
|
|
707
562
|
const FAKE_CWD = "/fake/project";
|
|
708
563
|
|
|
709
|
-
afterEach(() => {
|
|
710
|
-
vi.resetModules();
|
|
711
|
-
});
|
|
564
|
+
afterEach(() => {});
|
|
712
565
|
|
|
713
566
|
beforeEach(() => {
|
|
714
567
|
vi.clearAllMocks();
|
|
@@ -720,14 +573,14 @@ describe("setLlamaSetting", () => {
|
|
|
720
573
|
mockReadFile.mockResolvedValue("{}");
|
|
721
574
|
mockWriteFile.mockResolvedValue(undefined);
|
|
722
575
|
mockRename.mockResolvedValue(undefined);
|
|
576
|
+
manager = createSettingsManager();
|
|
723
577
|
});
|
|
724
578
|
|
|
725
579
|
it("should write to project settings when .pi/settings.json exists (auto scope)", async () => {
|
|
726
580
|
mockAccess.mockResolvedValue(undefined);
|
|
727
581
|
mockReadFile.mockResolvedValue("{}");
|
|
728
582
|
|
|
729
|
-
|
|
730
|
-
await settings.setLlamaSetting("sortBy", "desc");
|
|
583
|
+
await manager.setLlamaSetting("sortBy", "desc");
|
|
731
584
|
|
|
732
585
|
expect(mockWriteFile).toHaveBeenCalledWith(
|
|
733
586
|
`${PROJECT_SETTINGS_PATH}.tmp`,
|
|
@@ -744,8 +597,7 @@ describe("setLlamaSetting", () => {
|
|
|
744
597
|
mockAccess.mockRejectedValue(new Error("ENOENT"));
|
|
745
598
|
mockReadFile.mockResolvedValue("{}");
|
|
746
599
|
|
|
747
|
-
|
|
748
|
-
await settings.setLlamaSetting("sortBy", "desc");
|
|
600
|
+
await manager.setLlamaSetting("sortBy", "desc");
|
|
749
601
|
|
|
750
602
|
expect(mockWriteFile).toHaveBeenCalledWith(
|
|
751
603
|
`${GLOBAL_SETTINGS_PATH}.tmp`,
|
|
@@ -762,8 +614,7 @@ describe("setLlamaSetting", () => {
|
|
|
762
614
|
mockAccess.mockResolvedValue(undefined); // project exists but we override
|
|
763
615
|
mockReadFile.mockResolvedValue("{}");
|
|
764
616
|
|
|
765
|
-
|
|
766
|
-
await settings.setLlamaSetting("sortBy", "desc", "global");
|
|
617
|
+
await manager.setLlamaSetting("sortBy", "desc", "global");
|
|
767
618
|
|
|
768
619
|
expect(mockWriteFile).toHaveBeenCalledWith(
|
|
769
620
|
`${GLOBAL_SETTINGS_PATH}.tmp`,
|
|
@@ -776,8 +627,7 @@ describe("setLlamaSetting", () => {
|
|
|
776
627
|
mockAccess.mockRejectedValue(new Error("ENOENT")); // project doesn't exist but we override
|
|
777
628
|
mockReadFile.mockResolvedValue("{}");
|
|
778
629
|
|
|
779
|
-
|
|
780
|
-
await settings.setLlamaSetting("sortBy", "desc", "project");
|
|
630
|
+
await manager.setLlamaSetting("sortBy", "desc", "project");
|
|
781
631
|
|
|
782
632
|
expect(mockWriteFile).toHaveBeenCalledWith(
|
|
783
633
|
`${PROJECT_SETTINGS_PATH}.tmp`,
|
|
@@ -796,8 +646,7 @@ describe("setLlamaSetting", () => {
|
|
|
796
646
|
),
|
|
797
647
|
);
|
|
798
648
|
|
|
799
|
-
|
|
800
|
-
await settings.setLlamaSetting("sortBy", "desc");
|
|
649
|
+
await manager.setLlamaSetting("sortBy", "desc");
|
|
801
650
|
|
|
802
651
|
expect(mockWriteFile).toHaveBeenCalledTimes(1);
|
|
803
652
|
const [tmpPath, written, encoding] = mockWriteFile.mock.calls[0];
|
|
@@ -815,9 +664,7 @@ describe("setLlamaSetting", () => {
|
|
|
815
664
|
expect(mockReload).toHaveBeenCalledTimes(1);
|
|
816
665
|
});
|
|
817
666
|
|
|
818
|
-
afterEach(() => {
|
|
819
|
-
vi.resetModules();
|
|
820
|
-
});
|
|
667
|
+
afterEach(() => {});
|
|
821
668
|
|
|
822
669
|
it("should reflect the new value in resolvers immediately after the write", async () => {
|
|
823
670
|
mockSettingsManager.reload.mockImplementation(async () => {
|
|
@@ -826,18 +673,16 @@ describe("setLlamaSetting", () => {
|
|
|
826
673
|
});
|
|
827
674
|
});
|
|
828
675
|
|
|
829
|
-
|
|
830
|
-
await settings.setLlamaSetting("sortBy", "desc");
|
|
676
|
+
await manager.setLlamaSetting("sortBy", "desc");
|
|
831
677
|
|
|
832
|
-
expect(await
|
|
678
|
+
expect(await manager.resolveSortBy()).toBe("desc");
|
|
833
679
|
});
|
|
834
680
|
|
|
835
681
|
it("should reject and skip reload when the write fails", async () => {
|
|
836
682
|
mockAccess.mockRejectedValue(new Error("ENOENT"));
|
|
837
683
|
mockWriteFile.mockRejectedValue(new Error("ENOSPC: simulated"));
|
|
838
684
|
|
|
839
|
-
|
|
840
|
-
await expect(settings.setLlamaSetting("sortBy", "desc")).rejects.toThrow(
|
|
685
|
+
await expect(manager.setLlamaSetting("sortBy", "desc")).rejects.toThrow(
|
|
841
686
|
"ENOSPC",
|
|
842
687
|
);
|
|
843
688
|
expect(mockReload).not.toHaveBeenCalled();
|
|
@@ -847,8 +692,7 @@ describe("setLlamaSetting", () => {
|
|
|
847
692
|
mockAccess.mockRejectedValue(new Error("ENOENT"));
|
|
848
693
|
mockReadFile.mockResolvedValue("{ broken");
|
|
849
694
|
|
|
850
|
-
|
|
851
|
-
await expect(settings.setLlamaSetting("sortBy", "desc")).rejects.toThrow(
|
|
695
|
+
await expect(manager.setLlamaSetting("sortBy", "desc")).rejects.toThrow(
|
|
852
696
|
/Cannot parse/,
|
|
853
697
|
);
|
|
854
698
|
expect(mockWriteFile).not.toHaveBeenCalled();
|
|
@@ -857,15 +701,15 @@ describe("setLlamaSetting", () => {
|
|
|
857
701
|
|
|
858
702
|
it("should persist booleans and numbers with type fidelity", async () => {
|
|
859
703
|
mockAccess.mockRejectedValue(new Error("ENOENT"));
|
|
860
|
-
|
|
861
|
-
await
|
|
704
|
+
|
|
705
|
+
await manager.setLlamaSetting("reactToModelSelect", false);
|
|
862
706
|
|
|
863
707
|
const [, firstWrite] = mockWriteFile.mock.calls[0];
|
|
864
708
|
expect(JSON.parse(firstWrite as string)).toEqual({
|
|
865
709
|
llamaSettings: { reactToModelSelect: false },
|
|
866
710
|
});
|
|
867
711
|
|
|
868
|
-
await
|
|
712
|
+
await manager.setLlamaSetting("pollingTimeout", 120000);
|
|
869
713
|
|
|
870
714
|
const [, secondWrite] = mockWriteFile.mock.calls[1];
|
|
871
715
|
expect(JSON.parse(secondWrite as string)).toEqual({
|
|
@@ -874,7 +718,8 @@ describe("setLlamaSetting", () => {
|
|
|
874
718
|
});
|
|
875
719
|
|
|
876
720
|
it("should still construct the manager without arguments", async () => {
|
|
877
|
-
const { LlamaSettingsManager } =
|
|
721
|
+
const { LlamaSettingsManager } =
|
|
722
|
+
await import("../../src/managers/settings");
|
|
878
723
|
|
|
879
724
|
expect(() => new LlamaSettingsManager()).not.toThrow();
|
|
880
725
|
});
|
|
@@ -889,15 +734,14 @@ describe("resolveServerOverrides", () => {
|
|
|
889
734
|
mockSettingsManager.getGlobalSettings,
|
|
890
735
|
);
|
|
891
736
|
|
|
892
|
-
afterEach(() => {
|
|
893
|
-
vi.resetModules();
|
|
894
|
-
});
|
|
737
|
+
afterEach(() => {});
|
|
895
738
|
|
|
896
739
|
beforeEach(() => {
|
|
897
740
|
vi.clearAllMocks();
|
|
898
741
|
mockGetAgentDir.mockReturnValue("/fake/agent/dir");
|
|
899
742
|
mockGetProjectSettings.mockReturnValue({});
|
|
900
743
|
mockGetGlobalSettings.mockReturnValue({});
|
|
744
|
+
manager = createSettingsManager();
|
|
901
745
|
});
|
|
902
746
|
|
|
903
747
|
it("should return overrides for a server that has them configured", async () => {
|
|
@@ -922,7 +766,7 @@ describe("resolveServerOverrides", () => {
|
|
|
922
766
|
},
|
|
923
767
|
});
|
|
924
768
|
|
|
925
|
-
const result = await
|
|
769
|
+
const result = await manager.resolveServerOverrides(
|
|
926
770
|
"http://127.0.0.1:8080",
|
|
927
771
|
);
|
|
928
772
|
|
|
@@ -946,7 +790,7 @@ describe("resolveServerOverrides", () => {
|
|
|
946
790
|
},
|
|
947
791
|
});
|
|
948
792
|
|
|
949
|
-
const result = await
|
|
793
|
+
const result = await manager.resolveServerOverrides(
|
|
950
794
|
"http://127.0.0.1:8080",
|
|
951
795
|
);
|
|
952
796
|
|
|
@@ -960,7 +804,7 @@ describe("resolveServerOverrides", () => {
|
|
|
960
804
|
},
|
|
961
805
|
});
|
|
962
806
|
|
|
963
|
-
const result = await
|
|
807
|
+
const result = await manager.resolveServerOverrides(
|
|
964
808
|
"http://127.0.0.1:8080",
|
|
965
809
|
);
|
|
966
810
|
|
|
@@ -979,7 +823,7 @@ describe("resolveServerOverrides", () => {
|
|
|
979
823
|
},
|
|
980
824
|
});
|
|
981
825
|
|
|
982
|
-
const result = await
|
|
826
|
+
const result = await manager.resolveServerOverrides("http://global:8080");
|
|
983
827
|
|
|
984
828
|
expect(result).toEqual({ "model-a": { cost: { input: 0.5 } } });
|
|
985
829
|
});
|
|
@@ -1006,7 +850,7 @@ describe("resolveServerOverrides", () => {
|
|
|
1006
850
|
},
|
|
1007
851
|
});
|
|
1008
852
|
|
|
1009
|
-
const result = await
|
|
853
|
+
const result = await manager.resolveServerOverrides("http://shared:8080");
|
|
1010
854
|
|
|
1011
855
|
expect(result).toEqual({
|
|
1012
856
|
"model-b": { cost: { input: 0.1, output: 0.2 } },
|
|
@@ -1018,7 +862,7 @@ describe("resolveServerOverrides", () => {
|
|
|
1018
862
|
llamaSettings: { servers: [] },
|
|
1019
863
|
});
|
|
1020
864
|
|
|
1021
|
-
const result = await
|
|
865
|
+
const result = await manager.resolveServerOverrides(
|
|
1022
866
|
"http://127.0.0.1:8080",
|
|
1023
867
|
);
|
|
1024
868
|
|
|
@@ -1028,7 +872,7 @@ describe("resolveServerOverrides", () => {
|
|
|
1028
872
|
it("should return empty object when llamaSettings is missing", async () => {
|
|
1029
873
|
mockGetProjectSettings.mockReturnValue({});
|
|
1030
874
|
|
|
1031
|
-
const result = await
|
|
875
|
+
const result = await manager.resolveServerOverrides(
|
|
1032
876
|
"http://127.0.0.1:8080",
|
|
1033
877
|
);
|
|
1034
878
|
|
|
@@ -1047,7 +891,7 @@ describe("resolveServerOverrides", () => {
|
|
|
1047
891
|
},
|
|
1048
892
|
});
|
|
1049
893
|
|
|
1050
|
-
const result = await
|
|
894
|
+
const result = await manager.resolveServerOverrides(
|
|
1051
895
|
"http://127.0.0.1:8080",
|
|
1052
896
|
);
|
|
1053
897
|
|
|
@@ -1057,7 +901,7 @@ describe("resolveServerOverrides", () => {
|
|
|
1057
901
|
|
|
1058
902
|
describe("Server with overrides", () => {
|
|
1059
903
|
it("should store and expose resolved overrides", () => {
|
|
1060
|
-
const server = new Server(
|
|
904
|
+
const server = new Server(manager, {
|
|
1061
905
|
baseUrl: "http://127.0.0.1:8080",
|
|
1062
906
|
overrides: {
|
|
1063
907
|
"model-a": { cost: { input: 0.2, output: 0.6 } },
|
|
@@ -1074,7 +918,7 @@ describe("Server with overrides", () => {
|
|
|
1074
918
|
});
|
|
1075
919
|
|
|
1076
920
|
it("should return undefined for findOverrideForModel when no overrides are provided", () => {
|
|
1077
|
-
const server = new Server(
|
|
921
|
+
const server = new Server(manager, {
|
|
1078
922
|
baseUrl: "http://127.0.0.1:8080",
|
|
1079
923
|
});
|
|
1080
924
|
|
|
@@ -1091,15 +935,14 @@ describe("resolveServers passes overrides", () => {
|
|
|
1091
935
|
mockSettingsManager.getGlobalSettings,
|
|
1092
936
|
);
|
|
1093
937
|
|
|
1094
|
-
afterEach(() => {
|
|
1095
|
-
vi.resetModules();
|
|
1096
|
-
});
|
|
938
|
+
afterEach(() => {});
|
|
1097
939
|
|
|
1098
940
|
beforeEach(() => {
|
|
1099
941
|
vi.clearAllMocks();
|
|
1100
942
|
mockGetAgentDir.mockReturnValue("/fake/agent/dir");
|
|
1101
943
|
mockGetProjectSettings.mockReturnValue({});
|
|
1102
944
|
mockGetGlobalSettings.mockReturnValue({});
|
|
945
|
+
manager = createSettingsManager();
|
|
1103
946
|
});
|
|
1104
947
|
|
|
1105
948
|
it("should pass resolved overrides to Server instances", async () => {
|
|
@@ -1117,7 +960,7 @@ describe("resolveServers passes overrides", () => {
|
|
|
1117
960
|
},
|
|
1118
961
|
});
|
|
1119
962
|
|
|
1120
|
-
const result = await
|
|
963
|
+
const result = await manager.resolveServers();
|
|
1121
964
|
|
|
1122
965
|
expect(result).toHaveLength(2);
|
|
1123
966
|
expect(result[0].findOverrideForModel("model-x")).toEqual({
|
|
@@ -1129,7 +972,7 @@ describe("resolveServers passes overrides", () => {
|
|
|
1129
972
|
|
|
1130
973
|
describe("Server.findOverrideForModel", () => {
|
|
1131
974
|
function createServer(overrides: Record<string, ModelOverride>): Server {
|
|
1132
|
-
return new Server(
|
|
975
|
+
return new Server(createSettingsManager() as any, {
|
|
1133
976
|
baseUrl: "http://127.0.0.1:8080",
|
|
1134
977
|
overrides,
|
|
1135
978
|
});
|