pi-llama-cpp 0.13.0 → 0.15.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 +19 -18
- package/package.json +3 -3
- package/src/api/client.ts +25 -0
- package/src/constants.ts +2 -2
- package/src/enums/status.ts +0 -1
- package/src/index.ts +2 -1
- package/src/interfaces/endpoints/models.ts +1 -1
- package/src/interfaces/settings.ts +7 -1
- package/src/interfaces/sortBy.ts +4 -0
- package/src/managers/command/models.ts +254 -0
- package/src/managers/command.ts +34 -461
- package/src/managers/events.ts +2 -2
- package/src/managers/server.ts +34 -18
- package/src/managers/settings.ts +26 -90
- package/src/models/baseModel.ts +27 -10
- package/src/models/legacyModel.ts +2 -2
- package/src/models/routerModel.ts +2 -2
- package/src/models/singleModel.ts +1 -1
- package/src/server.ts +17 -38
- package/src/sse/client.ts +113 -59
- package/src/sse/fetch.ts +43 -0
- package/src/sse/manager.ts +9 -27
- package/src/sse/types.ts +0 -4
- package/src/ui/dialog/base.ts +118 -0
- package/src/ui/dialog/confirm.ts +45 -0
- package/src/ui/dialog/factory.ts +111 -0
- package/src/ui/dialog/input.ts +63 -0
- package/src/ui/dialog/options.ts +23 -0
- package/src/ui/editors/editorOptions.ts +50 -0
- package/src/ui/editors/itemBuilder.ts +43 -0
- package/src/ui/editors/listEditor.ts +300 -0
- package/src/ui/editors/override/entry.ts +24 -0
- package/src/ui/editors/override/entryEditor.ts +248 -0
- 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 +39 -0
- package/src/ui/editors/override/overrideList.ts +127 -0
- package/src/ui/editors/server/builder.ts +60 -0
- package/src/ui/editors/server/fields.ts +84 -0
- package/src/ui/editors/server/itemBuilder.ts +64 -0
- package/src/ui/editors/server/serverEditor.ts +197 -0
- package/src/ui/editors/server/utils.ts +95 -0
- package/src/ui/editors/server/wizard.ts +110 -0
- package/src/ui/editors/settingField.ts +37 -0
- package/src/ui/editors/settingsListFactory.ts +33 -0
- package/src/ui/settings/index.ts +248 -0
- package/src/ui/strings.ts +25 -4
- package/src/utils/health.ts +2 -1
- package/src/utils/serverIds.ts +21 -0
- package/src/utils/settingsStore.ts +1 -1
- package/src/utils/urlResolver.ts +129 -0
- package/src/utils/urls.ts +33 -13
- package/tests/{commandManager.test.ts → command/commandManager.test.ts} +88 -12
- package/tests/{events.test.ts → events/events.test.ts} +6 -6
- package/tests/mocks.ts +2 -0
- package/tests/models/legacyModel.test.ts +103 -0
- package/tests/{routerModel.test.ts → models/routerModel.test.ts} +71 -72
- package/tests/{singleModel.test.ts → models/singleModel.test.ts} +17 -17
- package/tests/{health.test.ts → server/health.test.ts} +2 -2
- package/tests/{server.test.ts → server/server.test.ts} +5 -17
- package/tests/{serverManager.test.ts → server/serverManager.test.ts} +4 -4
- package/tests/{settings.test.ts → settings/settings.test.ts} +184 -141
- package/tests/{settingsStore.test.ts → settings/settingsStore.test.ts} +1 -1
- package/tests/{sseManager.test.ts → sse/sseManager.test.ts} +8 -26
- package/tests/{dialog.test.ts → ui/dialog.test.ts} +56 -4
- package/tests/{overrides.test.ts → ui/overrides.test.ts} +101 -111
- package/src/ui/dialog.ts +0 -290
- package/src/ui/overrideEntryEditor.ts +0 -119
- package/src/ui/overrideSettingsList.ts +0 -710
- package/src/ui/serverListEditor.ts +0 -59
- package/src/ui/serverSettingsList.ts +0 -513
- package/tests/legacyModel.test.ts +0 -97
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import { beforeEach, describe, expect, it, vi } from "vitest";
|
|
2
|
-
import { THINKING_BUDGETS } from "
|
|
3
|
-
import { Status } from "
|
|
4
|
-
import { EventManager } from "
|
|
5
|
-
import { ServerManager } from "
|
|
6
|
-
import type { Server } from "
|
|
7
|
-
import { createMockModel, createMockServer, makeSettingsStub } from "
|
|
2
|
+
import { THINKING_BUDGETS } from "../../src/constants";
|
|
3
|
+
import { Status } from "../../src/enums/status";
|
|
4
|
+
import { EventManager } from "../../src/managers/events";
|
|
5
|
+
import { ServerManager } from "../../src/managers/server";
|
|
6
|
+
import type { Server } from "../../src/server";
|
|
7
|
+
import { createMockModel, createMockServer, makeSettingsStub } from "../mocks";
|
|
8
8
|
|
|
9
9
|
/**
|
|
10
10
|
* Injected settings stub (EventManager and — in the live-list test — the
|
package/tests/mocks.ts
CHANGED
|
@@ -7,6 +7,7 @@ import {
|
|
|
7
7
|
POLLING_TIMEOUT,
|
|
8
8
|
REACT_TO_MODEL_SELECT,
|
|
9
9
|
SERVER_TIMEOUT,
|
|
10
|
+
SHOW_SERVER_URLS,
|
|
10
11
|
SORT_BY,
|
|
11
12
|
THINKING_BUDGETS,
|
|
12
13
|
} from "../src/constants";
|
|
@@ -40,6 +41,7 @@ export const makeSettingsStub = (
|
|
|
40
41
|
})),
|
|
41
42
|
resolveServers: vi.fn(async () => []),
|
|
42
43
|
resolveSortBy: vi.fn(async () => SORT_BY),
|
|
44
|
+
resolveShowServerUrls: vi.fn(async () => SHOW_SERVER_URLS),
|
|
43
45
|
resolveApiKey: vi.fn(() => API_KEY_PLACEHOLDER),
|
|
44
46
|
resolveReactToModelSelect: vi.fn(async () => REACT_TO_MODEL_SELECT),
|
|
45
47
|
resolveAutoloadOnMessage: vi.fn(async () => AUTOLOAD_ON_MESSAGE),
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
import { beforeEach, describe, expect, it } from "vitest";
|
|
2
|
+
import { FALLBACK_CTX } from "../../src/constants";
|
|
3
|
+
import { Mode } from "../../src/enums/mode";
|
|
4
|
+
import { DataProperty } from "../../src/interfaces/endpoints/models";
|
|
5
|
+
import { LegacyModel } from "../../src/models/legacyModel";
|
|
6
|
+
import { createMockServer, mockRpc } from "../mocks";
|
|
7
|
+
|
|
8
|
+
beforeEach(() => {
|
|
9
|
+
mockRpc.mockReset();
|
|
10
|
+
});
|
|
11
|
+
|
|
12
|
+
const createModel = (extra: Partial<DataProperty> = {}): LegacyModel =>
|
|
13
|
+
new LegacyModel(
|
|
14
|
+
{
|
|
15
|
+
id: "test",
|
|
16
|
+
tags: [],
|
|
17
|
+
object: "model",
|
|
18
|
+
owned_by: "test",
|
|
19
|
+
created: Date.now(),
|
|
20
|
+
...extra,
|
|
21
|
+
},
|
|
22
|
+
createMockServer(),
|
|
23
|
+
);
|
|
24
|
+
|
|
25
|
+
describe("LegacyModel mode", () => {
|
|
26
|
+
it("should always return LEGACY mode", () => {
|
|
27
|
+
const model = createModel();
|
|
28
|
+
expect(model.mode).toBe(Mode.LEGACY);
|
|
29
|
+
});
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
describe("LegacyModel capabilities (via toProviderConfig)", () => {
|
|
33
|
+
it("should detect image capability when multimodal is in capabilities", async () => {
|
|
34
|
+
mockRpc.mockResolvedValueOnce({ modalities: { vision: true } });
|
|
35
|
+
mockRpc.mockResolvedValue({ n_ctx: 4096, data: [{ max_model_len: 8192 }] });
|
|
36
|
+
|
|
37
|
+
const model = createModel();
|
|
38
|
+
const { input } = await model.toProviderConfig();
|
|
39
|
+
|
|
40
|
+
expect(input).toEqual(["text", "image"]);
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
it("should detect text-only capability when multimodal is not in capabilities", async () => {
|
|
44
|
+
mockRpc.mockResolvedValueOnce({ modalities: { vision: false } });
|
|
45
|
+
mockRpc.mockResolvedValue({ n_ctx: 4096, data: [{ max_model_len: 8192 }] });
|
|
46
|
+
|
|
47
|
+
const model = createModel();
|
|
48
|
+
const { input } = await model.toProviderConfig();
|
|
49
|
+
|
|
50
|
+
expect(input).toEqual(["text"]);
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
it("should fall back to text-only when auth fails", async () => {
|
|
54
|
+
mockRpc.mockRejectedValueOnce(new Error("401 Unauthorized")); // /props
|
|
55
|
+
mockRpc.mockRejectedValueOnce(new Error("401 Unauthorized")); // /v1/models in BaseModel's catch
|
|
56
|
+
mockRpc.mockResolvedValue({
|
|
57
|
+
n_ctx: 4096,
|
|
58
|
+
data: [{ max_model_len: 8192 }],
|
|
59
|
+
models: [{ capabilities: ["text"] }],
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
const model = createModel();
|
|
63
|
+
const { input } = await model.toProviderConfig();
|
|
64
|
+
|
|
65
|
+
expect(input).toEqual(["text"]);
|
|
66
|
+
});
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
describe("LegacyModel context size (via toProviderConfig)", () => {
|
|
70
|
+
it("should use max_model_len when it is non-zero", async () => {
|
|
71
|
+
mockRpc.mockResolvedValueOnce({ modalities: { vision: false } }); // capabilities: /props
|
|
72
|
+
mockRpc.mockResolvedValueOnce({ n_ctx: 4096 }); // /props
|
|
73
|
+
mockRpc.mockResolvedValueOnce({ data: [{ max_model_len: 8192 }] }); // /v1/models
|
|
74
|
+
|
|
75
|
+
const model = createModel();
|
|
76
|
+
const { contextWindow } = await model.toProviderConfig();
|
|
77
|
+
|
|
78
|
+
expect(contextWindow).toBe(8192);
|
|
79
|
+
expect(mockRpc).toHaveBeenCalledWith("/v1/models");
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
it("should fall back to n_ctx when max_model_len is 0", async () => {
|
|
83
|
+
mockRpc.mockResolvedValueOnce({ modalities: { vision: false } }); // capabilities: /props
|
|
84
|
+
mockRpc.mockResolvedValueOnce({ n_ctx: 4096 }); // /props
|
|
85
|
+
mockRpc.mockResolvedValueOnce({ data: [{ max_model_len: 0 }] }); // /v1/models
|
|
86
|
+
|
|
87
|
+
const model = createModel();
|
|
88
|
+
const { contextWindow } = await model.toProviderConfig();
|
|
89
|
+
|
|
90
|
+
expect(contextWindow).toBe(4096);
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
it("should return FALLBACK_CTX when both values are missing/null", async () => {
|
|
94
|
+
mockRpc.mockResolvedValueOnce({ modalities: { vision: false } }); // capabilities: /props
|
|
95
|
+
mockRpc.mockResolvedValueOnce({}); // /props
|
|
96
|
+
mockRpc.mockResolvedValueOnce({ data: [{ max_model_len: null }] }); // /v1/models
|
|
97
|
+
|
|
98
|
+
const model = createModel();
|
|
99
|
+
const { contextWindow } = await model.toProviderConfig();
|
|
100
|
+
|
|
101
|
+
expect(contextWindow).toBe(FALLBACK_CTX);
|
|
102
|
+
});
|
|
103
|
+
});
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import { beforeEach, describe, expect, it } from "vitest";
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
2
|
+
import { FALLBACK_CTX } from "../../src/constants";
|
|
3
|
+
import { Mode } from "../../src/enums/mode";
|
|
4
|
+
import { DataProperty } from "../../src/interfaces/endpoints/models";
|
|
5
|
+
import { RouterModel } from "../../src/models/routerModel";
|
|
6
|
+
import { createMockServer, mockRpc } from "../mocks";
|
|
6
7
|
|
|
7
8
|
// Helper to create a mock DataProperty
|
|
8
9
|
const createModel = (overrides: Partial<DataProperty> = {}): DataProperty => ({
|
|
@@ -20,12 +21,21 @@ beforeEach(() => {
|
|
|
20
21
|
mockRpc.mockClear();
|
|
21
22
|
});
|
|
22
23
|
|
|
23
|
-
describe("RouterModel context size
|
|
24
|
-
|
|
24
|
+
describe("RouterModel context size (via toProviderConfig)", () => {
|
|
25
|
+
/** Mocks for an unloaded model: capabilities detection fails (text-only),
|
|
26
|
+
* then the status probe fails so context size falls back to CLI args. */
|
|
27
|
+
const mockUnloaded = () => {
|
|
28
|
+
mockRpc.mockRejectedValueOnce(new Error("props not available")); // capabilities: /props
|
|
29
|
+
mockRpc.mockResolvedValueOnce({ data: [] }); // capabilities: /v1/models
|
|
30
|
+
mockRpc.mockRejectedValueOnce(new Error("props not available")); // status: /props
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
it("should extract --ctx-size when unloaded", async () => {
|
|
34
|
+
mockUnloaded();
|
|
25
35
|
const model = new RouterModel(
|
|
26
36
|
createModel({
|
|
27
37
|
status: {
|
|
28
|
-
value: "
|
|
38
|
+
value: "unloaded",
|
|
29
39
|
args: [
|
|
30
40
|
"--model",
|
|
31
41
|
"gguf",
|
|
@@ -40,16 +50,17 @@ describe("RouterModel context size extraction", () => {
|
|
|
40
50
|
createMockServer(),
|
|
41
51
|
);
|
|
42
52
|
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
expect(
|
|
53
|
+
const { contextWindow } = await model.toProviderConfig();
|
|
54
|
+
|
|
55
|
+
expect(contextWindow).toBe(4096);
|
|
46
56
|
});
|
|
47
57
|
|
|
48
|
-
it("should
|
|
58
|
+
it("should fall back to --fit-ctx when --ctx-size is not present", async () => {
|
|
59
|
+
mockUnloaded();
|
|
49
60
|
const model = new RouterModel(
|
|
50
61
|
createModel({
|
|
51
62
|
status: {
|
|
52
|
-
value: "
|
|
63
|
+
value: "unloaded",
|
|
53
64
|
args: ["--model", "gguf", "--fit-ctx", "8192"],
|
|
54
65
|
preset: "default",
|
|
55
66
|
},
|
|
@@ -57,91 +68,87 @@ describe("RouterModel context size extraction", () => {
|
|
|
57
68
|
createMockServer(),
|
|
58
69
|
);
|
|
59
70
|
|
|
60
|
-
const
|
|
61
|
-
|
|
71
|
+
const { contextWindow } = await model.toProviderConfig();
|
|
72
|
+
|
|
73
|
+
expect(contextWindow).toBe(8192);
|
|
62
74
|
});
|
|
63
75
|
|
|
64
|
-
it("should
|
|
76
|
+
it("should prefer --ctx-size over --fit-ctx", async () => {
|
|
77
|
+
mockUnloaded();
|
|
65
78
|
const model = new RouterModel(
|
|
66
79
|
createModel({
|
|
67
80
|
status: {
|
|
68
|
-
value: "
|
|
69
|
-
args: ["--model", "gguf", "--
|
|
81
|
+
value: "unloaded",
|
|
82
|
+
args: ["--model", "gguf", "--ctx-size", "4096", "--fit-ctx", "8192"],
|
|
70
83
|
preset: "default",
|
|
71
84
|
},
|
|
72
85
|
}),
|
|
73
86
|
createMockServer(),
|
|
74
87
|
);
|
|
75
88
|
|
|
76
|
-
const
|
|
77
|
-
|
|
78
|
-
expect(
|
|
89
|
+
const { contextWindow } = await model.toProviderConfig();
|
|
90
|
+
|
|
91
|
+
expect(contextWindow).toBe(4096);
|
|
79
92
|
});
|
|
80
93
|
|
|
81
|
-
it("should
|
|
94
|
+
it("should fall back to FALLBACK_CTX when no size argument is present", async () => {
|
|
95
|
+
mockUnloaded();
|
|
82
96
|
const model = new RouterModel(
|
|
83
97
|
createModel({
|
|
84
98
|
status: {
|
|
85
|
-
value: "
|
|
86
|
-
args: ["--model", "gguf", "--
|
|
99
|
+
value: "unloaded",
|
|
100
|
+
args: ["--model", "gguf", "--batch-size", "512"],
|
|
87
101
|
preset: "default",
|
|
88
102
|
},
|
|
89
103
|
}),
|
|
90
104
|
createMockServer(),
|
|
91
105
|
);
|
|
92
106
|
|
|
93
|
-
const
|
|
94
|
-
|
|
107
|
+
const { contextWindow } = await model.toProviderConfig();
|
|
108
|
+
|
|
109
|
+
expect(contextWindow).toBe(FALLBACK_CTX);
|
|
95
110
|
});
|
|
96
111
|
|
|
97
|
-
it("should
|
|
112
|
+
it("should fall back to FALLBACK_CTX when the argument has no following value", async () => {
|
|
113
|
+
mockUnloaded();
|
|
98
114
|
const model = new RouterModel(
|
|
99
115
|
createModel({
|
|
100
116
|
status: {
|
|
101
|
-
value: "
|
|
102
|
-
args: ["--model", "gguf", "--ctx-size"
|
|
117
|
+
value: "unloaded",
|
|
118
|
+
args: ["--model", "gguf", "--ctx-size"],
|
|
103
119
|
preset: "default",
|
|
104
120
|
},
|
|
105
121
|
}),
|
|
106
122
|
createMockServer(),
|
|
107
123
|
);
|
|
108
124
|
|
|
109
|
-
const
|
|
110
|
-
expect(extractFrom("--ctx-size")).toBeNull();
|
|
111
|
-
});
|
|
125
|
+
const { contextWindow } = await model.toProviderConfig();
|
|
112
126
|
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
mockRpc.mockResolvedValueOnce({ is_sleeping: false });
|
|
116
|
-
// Second call: super.getContextSize() -> fetchModels with meta.n_ctx
|
|
117
|
-
mockRpc.mockResolvedValueOnce({
|
|
118
|
-
data: [
|
|
119
|
-
{
|
|
120
|
-
id: "test-model",
|
|
121
|
-
meta: { n_ctx: 4096 },
|
|
122
|
-
},
|
|
123
|
-
],
|
|
124
|
-
});
|
|
127
|
+
expect(contextWindow).toBe(FALLBACK_CTX);
|
|
128
|
+
});
|
|
125
129
|
|
|
130
|
+
it("should fall back to FALLBACK_CTX when the argument value is not a valid number", async () => {
|
|
131
|
+
mockUnloaded();
|
|
126
132
|
const model = new RouterModel(
|
|
127
133
|
createModel({
|
|
128
134
|
status: {
|
|
129
|
-
value: "
|
|
130
|
-
args: ["--model", "gguf", "--ctx-size", "
|
|
135
|
+
value: "unloaded",
|
|
136
|
+
args: ["--model", "gguf", "--ctx-size", "not-a-number"],
|
|
131
137
|
preset: "default",
|
|
132
138
|
},
|
|
133
139
|
}),
|
|
134
140
|
createMockServer(),
|
|
135
141
|
);
|
|
136
142
|
|
|
137
|
-
const
|
|
138
|
-
|
|
143
|
+
const { contextWindow } = await model.toProviderConfig();
|
|
144
|
+
|
|
145
|
+
expect(contextWindow).toBe(FALLBACK_CTX);
|
|
139
146
|
});
|
|
140
147
|
|
|
141
|
-
it("should return n_ctx from meta when loaded
|
|
142
|
-
|
|
143
|
-
mockRpc.mockResolvedValueOnce({ is_sleeping: false });
|
|
144
|
-
//
|
|
148
|
+
it("should return n_ctx from meta when loaded", async () => {
|
|
149
|
+
mockRpc.mockResolvedValueOnce({ modalities: { vision: false } }); // capabilities: /props
|
|
150
|
+
mockRpc.mockResolvedValueOnce({ is_sleeping: false }); // status: /props
|
|
151
|
+
// super.getContextSize() -> fetchModels with meta.n_ctx
|
|
145
152
|
mockRpc.mockResolvedValueOnce({
|
|
146
153
|
data: [
|
|
147
154
|
{
|
|
@@ -151,30 +158,22 @@ describe("RouterModel context size extraction", () => {
|
|
|
151
158
|
],
|
|
152
159
|
});
|
|
153
160
|
|
|
154
|
-
const model = new RouterModel(
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
value: "loaded",
|
|
158
|
-
args: ["--model", "gguf"],
|
|
159
|
-
preset: "default",
|
|
160
|
-
},
|
|
161
|
-
}),
|
|
162
|
-
createMockServer(),
|
|
163
|
-
);
|
|
161
|
+
const model = new RouterModel(createModel(), createMockServer());
|
|
162
|
+
|
|
163
|
+
const { contextWindow } = await model.toProviderConfig();
|
|
164
164
|
|
|
165
|
-
|
|
166
|
-
expect(ctxSize).toBe(4096);
|
|
165
|
+
expect(contextWindow).toBe(4096);
|
|
167
166
|
});
|
|
168
167
|
});
|
|
169
168
|
|
|
170
|
-
describe("RouterModel capabilities detection", () => {
|
|
169
|
+
describe("RouterModel capabilities detection (via toProviderConfig)", () => {
|
|
171
170
|
it("should detect image capability when modalities.vision is true", async () => {
|
|
172
171
|
mockRpc.mockResolvedValueOnce({ modalities: { vision: true } });
|
|
173
172
|
|
|
174
173
|
const model = new RouterModel(createModel(), createMockServer());
|
|
175
|
-
const
|
|
174
|
+
const { input } = await model.toProviderConfig();
|
|
176
175
|
|
|
177
|
-
expect(
|
|
176
|
+
expect(input).toEqual(["text", "image"]);
|
|
178
177
|
expect(mockRpc).toHaveBeenCalledWith(
|
|
179
178
|
"/props?model=test-model&autoload=false",
|
|
180
179
|
);
|
|
@@ -187,9 +186,9 @@ describe("RouterModel capabilities detection", () => {
|
|
|
187
186
|
mockRpc.mockResolvedValueOnce({ data: [] });
|
|
188
187
|
|
|
189
188
|
const model = new RouterModel(createModel(), createMockServer());
|
|
190
|
-
const
|
|
189
|
+
const { input } = await model.toProviderConfig();
|
|
191
190
|
|
|
192
|
-
expect(
|
|
191
|
+
expect(input).toEqual(["text"]);
|
|
193
192
|
});
|
|
194
193
|
|
|
195
194
|
it("should detect text-only capability when only text in input_modalities", async () => {
|
|
@@ -215,9 +214,9 @@ describe("RouterModel capabilities detection", () => {
|
|
|
215
214
|
});
|
|
216
215
|
|
|
217
216
|
const model = new RouterModel(createModel(), createMockServer());
|
|
218
|
-
const
|
|
217
|
+
const { input } = await model.toProviderConfig();
|
|
219
218
|
|
|
220
|
-
expect(
|
|
219
|
+
expect(input).toEqual(["text"]);
|
|
221
220
|
});
|
|
222
221
|
|
|
223
222
|
it("should return text when model not found in /models response", async () => {
|
|
@@ -239,9 +238,9 @@ describe("RouterModel capabilities detection", () => {
|
|
|
239
238
|
});
|
|
240
239
|
|
|
241
240
|
const model = new RouterModel(createModel(), createMockServer());
|
|
242
|
-
const
|
|
241
|
+
const { input } = await model.toProviderConfig();
|
|
243
242
|
|
|
244
|
-
expect(
|
|
243
|
+
expect(input).toEqual(["text"]);
|
|
245
244
|
});
|
|
246
245
|
});
|
|
247
246
|
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { beforeEach, describe, expect, it } from "vitest";
|
|
2
|
-
import { Mode } from "
|
|
3
|
-
import { Status } from "
|
|
4
|
-
import { DataProperty } from "
|
|
5
|
-
import { SingleModel } from "
|
|
6
|
-
import { createMockServer, mockRpc } from "
|
|
2
|
+
import { Mode } from "../../src/enums/mode";
|
|
3
|
+
import { Status } from "../../src/enums/status";
|
|
4
|
+
import { DataProperty } from "../../src/interfaces/endpoints/models";
|
|
5
|
+
import { SingleModel } from "../../src/models/singleModel";
|
|
6
|
+
import { createMockServer, mockRpc } from "../mocks";
|
|
7
7
|
|
|
8
8
|
beforeEach(() => {
|
|
9
9
|
mockRpc.mockReset();
|
|
@@ -29,23 +29,23 @@ describe("SingleModel mode", () => {
|
|
|
29
29
|
});
|
|
30
30
|
});
|
|
31
31
|
|
|
32
|
-
describe("SingleModel capabilities", () => {
|
|
32
|
+
describe("SingleModel capabilities (via toProviderConfig)", () => {
|
|
33
33
|
it("should detect image capability when multimodal is in capabilities", async () => {
|
|
34
34
|
mockRpc.mockResolvedValueOnce({ modalities: { vision: true } });
|
|
35
35
|
|
|
36
36
|
const model = createModel();
|
|
37
|
-
const
|
|
37
|
+
const { input } = await model.toProviderConfig();
|
|
38
38
|
|
|
39
|
-
expect(
|
|
39
|
+
expect(input).toEqual(["text", "image"]);
|
|
40
40
|
});
|
|
41
41
|
|
|
42
42
|
it("should detect text-only capability when multimodal is not in capabilities", async () => {
|
|
43
43
|
mockRpc.mockResolvedValueOnce({ modalities: { vision: false } });
|
|
44
44
|
|
|
45
45
|
const model = createModel();
|
|
46
|
-
const
|
|
46
|
+
const { input } = await model.toProviderConfig();
|
|
47
47
|
|
|
48
|
-
expect(
|
|
48
|
+
expect(input).toEqual(["text"]);
|
|
49
49
|
});
|
|
50
50
|
|
|
51
51
|
it("should fall back to the models endpoint when auth fails", async () => {
|
|
@@ -59,9 +59,9 @@ describe("SingleModel capabilities", () => {
|
|
|
59
59
|
}); // /v1/models retry in SingleModel's catch
|
|
60
60
|
|
|
61
61
|
const model = createModel();
|
|
62
|
-
const
|
|
62
|
+
const { input } = await model.toProviderConfig();
|
|
63
63
|
|
|
64
|
-
expect(
|
|
64
|
+
expect(input).toEqual(["text", "image"]);
|
|
65
65
|
});
|
|
66
66
|
|
|
67
67
|
it("should fall back to text-only when the models endpoint reports no multimodal", async () => {
|
|
@@ -75,9 +75,9 @@ describe("SingleModel capabilities", () => {
|
|
|
75
75
|
}); // /v1/models retry in SingleModel's catch
|
|
76
76
|
|
|
77
77
|
const model = createModel();
|
|
78
|
-
const
|
|
78
|
+
const { input } = await model.toProviderConfig();
|
|
79
79
|
|
|
80
|
-
expect(
|
|
80
|
+
expect(input).toEqual(["text"]);
|
|
81
81
|
});
|
|
82
82
|
});
|
|
83
83
|
|
|
@@ -104,16 +104,16 @@ describe("SingleModel getStatus", () => {
|
|
|
104
104
|
});
|
|
105
105
|
});
|
|
106
106
|
|
|
107
|
-
describe("SingleModel
|
|
107
|
+
describe("SingleModel context size (via toProviderConfig)", () => {
|
|
108
108
|
it("should return n_ctx from /v1/models endpoint meta", async () => {
|
|
109
109
|
mockRpc.mockResolvedValue({
|
|
110
110
|
data: [{ id: "test", meta: { n_ctx: 8192 } }],
|
|
111
111
|
});
|
|
112
112
|
|
|
113
113
|
const model = createModel();
|
|
114
|
-
const
|
|
114
|
+
const { contextWindow } = await model.toProviderConfig();
|
|
115
115
|
|
|
116
|
-
expect(
|
|
116
|
+
expect(contextWindow).toBe(8192);
|
|
117
117
|
expect(mockRpc).toHaveBeenCalledWith("/v1/models");
|
|
118
118
|
});
|
|
119
119
|
});
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { afterEach, describe, expect, it, vi } from "vitest";
|
|
2
|
-
import { ServerStatus } from "
|
|
3
|
-
import { checkServerHealth } from "
|
|
2
|
+
import { ServerStatus } from "../../src/enums/serverStatus";
|
|
3
|
+
import { checkServerHealth } from "../../src/utils/health";
|
|
4
4
|
|
|
5
5
|
/**
|
|
6
6
|
* Stubs `global.fetch` for one test. The probe only reads the parsed body,
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
|
2
|
-
import { POLLING_TIMEOUT, SERVER_TIMEOUT } from "
|
|
3
|
-
import { ServerStatus } from "
|
|
4
|
-
import type { LlamaSettingsManager } from "
|
|
5
|
-
import { Server } from "
|
|
6
|
-
import { createMockServer, makeSettingsStub, mockRpc } from "
|
|
2
|
+
import { POLLING_TIMEOUT, SERVER_TIMEOUT } from "../../src/constants";
|
|
3
|
+
import { ServerStatus } from "../../src/enums/serverStatus";
|
|
4
|
+
import type { LlamaSettingsManager } from "../../src/managers/settings";
|
|
5
|
+
import { Server } from "../../src/server";
|
|
6
|
+
import { createMockServer, makeSettingsStub, mockRpc } from "../mocks";
|
|
7
7
|
|
|
8
8
|
// Injected into every real Server below; fresh per test so per-case
|
|
9
9
|
// overrides never leak. The Server constructor resolves the API key eagerly
|
|
@@ -107,18 +107,6 @@ describe("Server fetchModelProps", () => {
|
|
|
107
107
|
});
|
|
108
108
|
});
|
|
109
109
|
|
|
110
|
-
describe("Server fetchServerHealth", () => {
|
|
111
|
-
it("should call the /health endpoint", async () => {
|
|
112
|
-
mockRpc.mockResolvedValueOnce({ status: "ok" });
|
|
113
|
-
|
|
114
|
-
const server = createMockServer();
|
|
115
|
-
const result = await server.fetchServerHealth();
|
|
116
|
-
|
|
117
|
-
expect(result).toEqual({ status: "ok" });
|
|
118
|
-
expect(mockRpc).toHaveBeenCalledWith("/health");
|
|
119
|
-
});
|
|
120
|
-
});
|
|
121
|
-
|
|
122
110
|
describe("Server fetchServerProps", () => {
|
|
123
111
|
it("should call the /props endpoint without model", async () => {
|
|
124
112
|
mockRpc.mockResolvedValueOnce({
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
import { beforeEach, describe, expect, it, vi } from "vitest";
|
|
2
|
-
import { ServerManager } from "
|
|
3
|
-
import { BaseModel } from "
|
|
4
|
-
import { Server } from "
|
|
2
|
+
import { ServerManager } from "../../src/managers/server";
|
|
3
|
+
import { BaseModel } from "../../src/models/baseModel";
|
|
4
|
+
import { Server } from "../../src/server";
|
|
5
5
|
import {
|
|
6
6
|
createMockModel,
|
|
7
7
|
createMockServer,
|
|
8
8
|
makeSettingsStub,
|
|
9
9
|
mockRpc,
|
|
10
|
-
} from "
|
|
10
|
+
} from "../mocks";
|
|
11
11
|
|
|
12
12
|
// Injected settings stub — the single instance passed to ServerManager and
|
|
13
13
|
// to every real `new Server(...)` construction below (Server's eager
|