@clinebot/core 0.0.24 → 0.0.25
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/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +190 -190
- package/dist/providers/local-provider-registry.d.ts +7 -0
- package/dist/providers/local-provider-registry.d.ts.map +1 -1
- package/dist/providers/local-provider-service.d.ts +27 -1
- package/dist/providers/local-provider-service.d.ts.map +1 -1
- package/dist/runtime/runtime-builder.d.ts.map +1 -1
- package/package.json +5 -5
- package/src/index.ts +4 -0
- package/src/providers/local-provider-registry.ts +8 -0
- package/src/providers/local-provider-service.test.ts +259 -1
- package/src/providers/local-provider-service.ts +267 -31
- package/src/runtime/runtime-builder.test.ts +41 -0
- package/src/runtime/runtime-builder.ts +2 -3
|
@@ -30,6 +30,13 @@ export declare function toRpcProviderModel(modelId: string, info: {
|
|
|
30
30
|
capabilities?: string[];
|
|
31
31
|
thinkingConfig?: unknown;
|
|
32
32
|
}): RpcProviderModel;
|
|
33
|
+
/**
|
|
34
|
+
* Custom Provider Registry
|
|
35
|
+
*
|
|
36
|
+
* This module manages the registration of custom OpenAI-compatible providers and
|
|
37
|
+
* their models based on local JSON files. It provides functions to read/write the models
|
|
38
|
+
* registry file and to register providers with the LlmsModels system.
|
|
39
|
+
*/
|
|
33
40
|
export declare function registerCustomProvider(providerId: string, entry: StoredModelsFile["providers"][string]): void;
|
|
34
41
|
export declare function ensureCustomProvidersLoadedSync(manager: ProviderSettingsManager): void;
|
|
35
42
|
export declare function ensureCustomProvidersLoaded(manager: ProviderSettingsManager): Promise<void>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"local-provider-registry.d.ts","sourceRoot":"","sources":["../../src/providers/local-provider-registry.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,qBAAqB,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,CAAC;AAChF,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,sCAAsC,CAAC;AAEpF,MAAM,MAAM,gBAAgB,GAAG;IAC9B,OAAO,EAAE,CAAC,CAAC;IACX,SAAS,EAAE,MAAM,CAChB,MAAM,EACN;QACC,QAAQ,EAAE;YACT,IAAI,EAAE,MAAM,CAAC;YACb,OAAO,EAAE,MAAM,CAAC;YAChB,cAAc,CAAC,EAAE,MAAM,CAAC;YACxB,YAAY,CAAC,EAAE,qBAAqB,EAAE,CAAC;YACvC,eAAe,CAAC,EAAE,MAAM,CAAC;SACzB,CAAC;QACF,MAAM,EAAE,MAAM,CACb,MAAM,EACN;YACC,EAAE,EAAE,MAAM,CAAC;YACX,IAAI,EAAE,MAAM,CAAC;YACb,cAAc,CAAC,EAAE,OAAO,CAAC;YACzB,mBAAmB,CAAC,EAAE,OAAO,CAAC;YAC9B,iBAAiB,CAAC,EAAE,OAAO,CAAC;SAC5B,CACD,CAAC;KACF,CACD,CAAC;CACF,CAAC;AAYF,wBAAgB,yBAAyB,CACxC,OAAO,EAAE,uBAAuB,GAC9B,MAAM,CAER;AAED,wBAAgB,eAAe,IAAI,gBAAgB,CAElD;AAED,wBAAgB,kBAAkB,CAAC,QAAQ,EAAE,MAAM,GAAG,gBAAgB,CAmBrE;AAED,wBAAsB,cAAc,CACnC,QAAQ,EAAE,MAAM,GACd,OAAO,CAAC,gBAAgB,CAAC,CAgB3B;AAED,wBAAgB,mBAAmB,CAClC,QAAQ,EAAE,MAAM,EAChB,KAAK,EAAE,gBAAgB,GACrB,IAAI,CAGN;AAED,wBAAsB,eAAe,CACpC,QAAQ,EAAE,MAAM,EAChB,KAAK,EAAE,gBAAgB,GACrB,OAAO,CAAC,IAAI,CAAC,CAGf;AAED,wBAAgB,kBAAkB,CACjC,OAAO,EAAE,MAAM,EACf,IAAI,EAAE;IACL,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,YAAY,CAAC,EAAE,MAAM,EAAE,CAAC;IACxB,cAAc,CAAC,EAAE,OAAO,CAAC;CACzB,GACC,gBAAgB,CASlB;AA2CD,wBAAgB,sBAAsB,CACrC,UAAU,EAAE,MAAM,EAClB,KAAK,EAAE,gBAAgB,CAAC,WAAW,CAAC,CAAC,MAAM,CAAC,GAC1C,IAAI,
|
|
1
|
+
{"version":3,"file":"local-provider-registry.d.ts","sourceRoot":"","sources":["../../src/providers/local-provider-registry.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,qBAAqB,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,CAAC;AAChF,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,sCAAsC,CAAC;AAEpF,MAAM,MAAM,gBAAgB,GAAG;IAC9B,OAAO,EAAE,CAAC,CAAC;IACX,SAAS,EAAE,MAAM,CAChB,MAAM,EACN;QACC,QAAQ,EAAE;YACT,IAAI,EAAE,MAAM,CAAC;YACb,OAAO,EAAE,MAAM,CAAC;YAChB,cAAc,CAAC,EAAE,MAAM,CAAC;YACxB,YAAY,CAAC,EAAE,qBAAqB,EAAE,CAAC;YACvC,eAAe,CAAC,EAAE,MAAM,CAAC;SACzB,CAAC;QACF,MAAM,EAAE,MAAM,CACb,MAAM,EACN;YACC,EAAE,EAAE,MAAM,CAAC;YACX,IAAI,EAAE,MAAM,CAAC;YACb,cAAc,CAAC,EAAE,OAAO,CAAC;YACzB,mBAAmB,CAAC,EAAE,OAAO,CAAC;YAC9B,iBAAiB,CAAC,EAAE,OAAO,CAAC;SAC5B,CACD,CAAC;KACF,CACD,CAAC;CACF,CAAC;AAYF,wBAAgB,yBAAyB,CACxC,OAAO,EAAE,uBAAuB,GAC9B,MAAM,CAER;AAED,wBAAgB,eAAe,IAAI,gBAAgB,CAElD;AAED,wBAAgB,kBAAkB,CAAC,QAAQ,EAAE,MAAM,GAAG,gBAAgB,CAmBrE;AAED,wBAAsB,cAAc,CACnC,QAAQ,EAAE,MAAM,GACd,OAAO,CAAC,gBAAgB,CAAC,CAgB3B;AAED,wBAAgB,mBAAmB,CAClC,QAAQ,EAAE,MAAM,EAChB,KAAK,EAAE,gBAAgB,GACrB,IAAI,CAGN;AAED,wBAAsB,eAAe,CACpC,QAAQ,EAAE,MAAM,EAChB,KAAK,EAAE,gBAAgB,GACrB,OAAO,CAAC,IAAI,CAAC,CAGf;AAED,wBAAgB,kBAAkB,CACjC,OAAO,EAAE,MAAM,EACf,IAAI,EAAE;IACL,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,YAAY,CAAC,EAAE,MAAM,EAAE,CAAC;IACxB,cAAc,CAAC,EAAE,OAAO,CAAC;CACzB,GACC,gBAAgB,CASlB;AA2CD;;;;;;GAMG;AACH,wBAAgB,sBAAsB,CACrC,UAAU,EAAE,MAAM,EAClB,KAAK,EAAE,gBAAgB,CAAC,WAAW,CAAC,CAAC,MAAM,CAAC,GAC1C,IAAI,CAgCN;AAED,wBAAgB,+BAA+B,CAC9C,OAAO,EAAE,uBAAuB,GAC9B,IAAI,CAUN;AAED,wBAAsB,2BAA2B,CAChD,OAAO,EAAE,uBAAuB,GAC9B,OAAO,CAAC,IAAI,CAAC,CAUf"}
|
|
@@ -1,13 +1,39 @@
|
|
|
1
1
|
import * as LlmsProviders from "@clinebot/llms/providers";
|
|
2
|
-
import type { RpcAddProviderActionRequest, RpcOAuthProviderId, RpcProviderListItem, RpcProviderModel, RpcSaveProviderSettingsActionRequest } from "@clinebot/shared";
|
|
2
|
+
import type { RpcAddProviderActionRequest, RpcOAuthProviderId, RpcProviderCapability, RpcProviderListItem, RpcProviderModel, RpcSaveProviderSettingsActionRequest } from "@clinebot/shared";
|
|
3
3
|
import type { ProviderSettingsManager } from "../storage/provider-settings-manager";
|
|
4
4
|
export { ensureCustomProvidersLoaded } from "./local-provider-registry";
|
|
5
|
+
export interface UpdateLocalProviderRequest {
|
|
6
|
+
providerId: string;
|
|
7
|
+
name?: string;
|
|
8
|
+
baseUrl?: string;
|
|
9
|
+
apiKey?: string | null;
|
|
10
|
+
headers?: Record<string, string> | null;
|
|
11
|
+
timeoutMs?: number | null;
|
|
12
|
+
models?: string[];
|
|
13
|
+
defaultModelId?: string | null;
|
|
14
|
+
modelsSourceUrl?: string | null;
|
|
15
|
+
capabilities?: RpcProviderCapability[] | null;
|
|
16
|
+
}
|
|
17
|
+
export interface DeleteLocalProviderRequest {
|
|
18
|
+
providerId: string;
|
|
19
|
+
}
|
|
5
20
|
export declare function addLocalProvider(manager: ProviderSettingsManager, request: Omit<RpcAddProviderActionRequest, "action">): Promise<{
|
|
6
21
|
providerId: string;
|
|
7
22
|
settingsPath: string;
|
|
8
23
|
modelsPath: string;
|
|
9
24
|
modelsCount: number;
|
|
10
25
|
}>;
|
|
26
|
+
export declare function updateLocalProvider(manager: ProviderSettingsManager, request: UpdateLocalProviderRequest): Promise<{
|
|
27
|
+
providerId: string;
|
|
28
|
+
settingsPath: string;
|
|
29
|
+
modelsPath: string;
|
|
30
|
+
modelsCount: number;
|
|
31
|
+
}>;
|
|
32
|
+
export declare function deleteLocalProvider(manager: ProviderSettingsManager, request: DeleteLocalProviderRequest): Promise<{
|
|
33
|
+
providerId: string;
|
|
34
|
+
settingsPath: string;
|
|
35
|
+
modelsPath: string;
|
|
36
|
+
}>;
|
|
11
37
|
export declare function listLocalProviders(manager: ProviderSettingsManager): Promise<{
|
|
12
38
|
providers: RpcProviderListItem[];
|
|
13
39
|
settingsPath: string;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"local-provider-service.d.ts","sourceRoot":"","sources":["../../src/providers/local-provider-service.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,aAAa,MAAM,0BAA0B,CAAC;AAC1D,OAAO,KAAK,EACX,2BAA2B,EAC3B,kBAAkB,EAClB,mBAAmB,EACnB,gBAAgB,EAChB,oCAAoC,EACpC,MAAM,kBAAkB,CAAC;AAK1B,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,sCAAsC,CAAC;AASpF,OAAO,EAAE,2BAA2B,EAAE,MAAM,2BAA2B,CAAC;
|
|
1
|
+
{"version":3,"file":"local-provider-service.d.ts","sourceRoot":"","sources":["../../src/providers/local-provider-service.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"runtime-builder.d.ts","sourceRoot":"","sources":["../../src/runtime/runtime-builder.ts"],"names":[],"mappings":"AA6BA,OAAO,KAAK,EACX,cAAc,EACd,mBAAmB,EACnB,YAAY,IAAI,kBAAkB,EAClC,MAAM,mBAAmB,CAAC;AAgB3B,wBAAgB,cAAc,IAAI,MAAM,CAEvC;AAqTD,qBAAa,qBAAsB,YAAW,cAAc;IAC3D,OAAO,CAAC,QAAQ,CAAC,mBAAmB,CAA6B;IAEjE,KAAK,CAAC,KAAK,EAAE,mBAAmB,GAAG,kBAAkB;
|
|
1
|
+
{"version":3,"file":"runtime-builder.d.ts","sourceRoot":"","sources":["../../src/runtime/runtime-builder.ts"],"names":[],"mappings":"AA6BA,OAAO,KAAK,EACX,cAAc,EACd,mBAAmB,EACnB,YAAY,IAAI,kBAAkB,EAClC,MAAM,mBAAmB,CAAC;AAgB3B,wBAAgB,cAAc,IAAI,MAAM,CAEvC;AAqTD,qBAAa,qBAAsB,YAAW,cAAc;IAC3D,OAAO,CAAC,QAAQ,CAAC,mBAAmB,CAA6B;IAEjE,KAAK,CAAC,KAAK,EAAE,mBAAmB,GAAG,kBAAkB;CA0QrD"}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@clinebot/core",
|
|
3
3
|
"description": "Cline Core SDK for Node Runtime",
|
|
4
|
-
"version": "0.0.
|
|
4
|
+
"version": "0.0.25",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"types": "./dist/index.d.ts",
|
|
7
7
|
"main": "./dist/index.js",
|
|
@@ -36,8 +36,8 @@
|
|
|
36
36
|
"test:watch": "vitest --config vitest.config.ts"
|
|
37
37
|
},
|
|
38
38
|
"dependencies": {
|
|
39
|
-
"@clinebot/agents": "0.0.
|
|
40
|
-
"@clinebot/llms": "0.0.
|
|
39
|
+
"@clinebot/agents": "0.0.25",
|
|
40
|
+
"@clinebot/llms": "0.0.25",
|
|
41
41
|
"@opentelemetry/api": "^1.9.0",
|
|
42
42
|
"@opentelemetry/api-logs": "^0.56.0",
|
|
43
43
|
"@opentelemetry/exporter-logs-otlp-http": "^0.56.0",
|
|
@@ -54,8 +54,8 @@
|
|
|
54
54
|
"zod": "^4.3.6"
|
|
55
55
|
},
|
|
56
56
|
"devDependencies": {
|
|
57
|
-
"@clinebot/rpc": "0.0.
|
|
58
|
-
"@clinebot/shared": "0.0.
|
|
57
|
+
"@clinebot/rpc": "0.0.25",
|
|
58
|
+
"@clinebot/shared": "0.0.25"
|
|
59
59
|
},
|
|
60
60
|
"engines": {
|
|
61
61
|
"node": ">=20"
|
package/src/index.ts
CHANGED
|
@@ -246,6 +246,8 @@ export {
|
|
|
246
246
|
} from "./mcp";
|
|
247
247
|
export {
|
|
248
248
|
addLocalProvider,
|
|
249
|
+
type DeleteLocalProviderRequest,
|
|
250
|
+
deleteLocalProvider,
|
|
249
251
|
ensureCustomProvidersLoaded,
|
|
250
252
|
getLocalProviderModels,
|
|
251
253
|
listLocalProviders,
|
|
@@ -254,6 +256,8 @@ export {
|
|
|
254
256
|
resolveLocalClineAuthToken,
|
|
255
257
|
saveLocalProviderOAuthCredentials,
|
|
256
258
|
saveLocalProviderSettings,
|
|
259
|
+
type UpdateLocalProviderRequest,
|
|
260
|
+
updateLocalProvider,
|
|
257
261
|
} from "./providers/local-provider-service";
|
|
258
262
|
export type { AvailableRuntimeCommand } from "./runtime/commands";
|
|
259
263
|
export {
|
|
@@ -167,6 +167,13 @@ function toModelCapabilities(
|
|
|
167
167
|
return [...next];
|
|
168
168
|
}
|
|
169
169
|
|
|
170
|
+
/**
|
|
171
|
+
* Custom Provider Registry
|
|
172
|
+
*
|
|
173
|
+
* This module manages the registration of custom OpenAI-compatible providers and
|
|
174
|
+
* their models based on local JSON files. It provides functions to read/write the models
|
|
175
|
+
* registry file and to register providers with the LlmsModels system.
|
|
176
|
+
*/
|
|
170
177
|
export function registerCustomProvider(
|
|
171
178
|
providerId: string,
|
|
172
179
|
entry: StoredModelsFile["providers"][string],
|
|
@@ -195,6 +202,7 @@ export function registerCustomProvider(
|
|
|
195
202
|
id: providerId,
|
|
196
203
|
name: entry.provider.name.trim() || titleCaseFromId(providerId),
|
|
197
204
|
protocol: "openai-chat",
|
|
205
|
+
client: "openai-compatible",
|
|
198
206
|
baseUrl: entry.provider.baseUrl,
|
|
199
207
|
defaultModelId,
|
|
200
208
|
capabilities: toProviderCapabilities(entry.provider.capabilities),
|
|
@@ -4,13 +4,19 @@ import path from "node:path";
|
|
|
4
4
|
import * as LlmsModels from "@clinebot/llms/models";
|
|
5
5
|
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
|
6
6
|
import { ProviderSettingsManager } from "../storage/provider-settings-manager";
|
|
7
|
+
import {
|
|
8
|
+
readModelsFile,
|
|
9
|
+
resolveModelsRegistryPath,
|
|
10
|
+
} from "./local-provider-registry";
|
|
7
11
|
import {
|
|
8
12
|
addLocalProvider,
|
|
13
|
+
deleteLocalProvider,
|
|
9
14
|
getLocalProviderModels,
|
|
10
15
|
listLocalProviders,
|
|
11
16
|
normalizeOAuthProvider,
|
|
12
17
|
resolveLocalClineAuthToken,
|
|
13
18
|
saveLocalProviderSettings,
|
|
19
|
+
updateLocalProvider,
|
|
14
20
|
} from "./local-provider-service";
|
|
15
21
|
|
|
16
22
|
// ---------------------------------------------------------------------------
|
|
@@ -267,12 +273,13 @@ describe("addLocalProvider – validation", () => {
|
|
|
267
273
|
).rejects.toThrow("name is required");
|
|
268
274
|
});
|
|
269
275
|
|
|
270
|
-
it("throws when baseUrl is empty", async () => {
|
|
276
|
+
it("throws when baseUrl is empty and apiKey is provided", async () => {
|
|
271
277
|
await expect(
|
|
272
278
|
addLocalProvider(manager, {
|
|
273
279
|
providerId: "my-provider2",
|
|
274
280
|
name: "My Provider",
|
|
275
281
|
baseUrl: " ",
|
|
282
|
+
apiKey: "present-key",
|
|
276
283
|
models: ["m"],
|
|
277
284
|
}),
|
|
278
285
|
).rejects.toThrow("baseUrl is required");
|
|
@@ -309,6 +316,71 @@ describe("addLocalProvider – validation", () => {
|
|
|
309
316
|
});
|
|
310
317
|
});
|
|
311
318
|
|
|
319
|
+
// ===========================================================================
|
|
320
|
+
// addLocalProvider – delete compatibility path
|
|
321
|
+
// ===========================================================================
|
|
322
|
+
|
|
323
|
+
describe("addLocalProvider – delete compatibility", () => {
|
|
324
|
+
let manager: ProviderSettingsManager;
|
|
325
|
+
let cleanup: () => void;
|
|
326
|
+
|
|
327
|
+
beforeEach(async () => {
|
|
328
|
+
({ manager, cleanup } = makeTempManager());
|
|
329
|
+
await addLocalProvider(manager, {
|
|
330
|
+
providerId: "legacy-delete-provider",
|
|
331
|
+
name: "Legacy Delete Provider",
|
|
332
|
+
baseUrl: "https://example.invalid/v1",
|
|
333
|
+
models: ["legacy-model"],
|
|
334
|
+
});
|
|
335
|
+
manager.saveProviderSettings(
|
|
336
|
+
{
|
|
337
|
+
provider: "legacy-delete-provider",
|
|
338
|
+
baseUrl: "https://example.invalid/v1",
|
|
339
|
+
model: "legacy-model",
|
|
340
|
+
},
|
|
341
|
+
{ setLastUsed: true },
|
|
342
|
+
);
|
|
343
|
+
});
|
|
344
|
+
|
|
345
|
+
afterEach(() => cleanup());
|
|
346
|
+
|
|
347
|
+
it("treats empty baseUrl and apiKey as a delete request for existing provider", async () => {
|
|
348
|
+
const result = await addLocalProvider(manager, {
|
|
349
|
+
providerId: "legacy-delete-provider",
|
|
350
|
+
name: "Ignored",
|
|
351
|
+
baseUrl: " ",
|
|
352
|
+
apiKey: " ",
|
|
353
|
+
models: [],
|
|
354
|
+
});
|
|
355
|
+
|
|
356
|
+
expect(result.providerId).toBe("legacy-delete-provider");
|
|
357
|
+
expect(result.modelsCount).toBe(0);
|
|
358
|
+
expect(LlmsModels.hasProvider("legacy-delete-provider")).toBe(false);
|
|
359
|
+
expect(
|
|
360
|
+
manager.getProviderSettings("legacy-delete-provider"),
|
|
361
|
+
).toBeUndefined();
|
|
362
|
+
expect(manager.getLastUsedProviderSettings()).toBeUndefined();
|
|
363
|
+
});
|
|
364
|
+
|
|
365
|
+
it("is a no-op delete when provider is already missing", async () => {
|
|
366
|
+
await deleteLocalProvider(manager, {
|
|
367
|
+
providerId: "legacy-delete-provider",
|
|
368
|
+
});
|
|
369
|
+
|
|
370
|
+
await expect(
|
|
371
|
+
addLocalProvider(manager, {
|
|
372
|
+
providerId: "legacy-delete-provider",
|
|
373
|
+
name: "Ignored",
|
|
374
|
+
baseUrl: " ",
|
|
375
|
+
models: [],
|
|
376
|
+
}),
|
|
377
|
+
).resolves.toMatchObject({
|
|
378
|
+
providerId: "legacy-delete-provider",
|
|
379
|
+
modelsCount: 0,
|
|
380
|
+
});
|
|
381
|
+
});
|
|
382
|
+
});
|
|
383
|
+
|
|
312
384
|
// ===========================================================================
|
|
313
385
|
// addLocalProvider – defaultModelId selection
|
|
314
386
|
// ===========================================================================
|
|
@@ -568,6 +640,192 @@ describe("saveLocalProviderSettings", () => {
|
|
|
568
640
|
});
|
|
569
641
|
});
|
|
570
642
|
|
|
643
|
+
// ===========================================================================
|
|
644
|
+
// updateLocalProvider
|
|
645
|
+
// ===========================================================================
|
|
646
|
+
|
|
647
|
+
describe("updateLocalProvider", () => {
|
|
648
|
+
let manager: ProviderSettingsManager;
|
|
649
|
+
let cleanup: () => void;
|
|
650
|
+
|
|
651
|
+
beforeEach(async () => {
|
|
652
|
+
({ manager, cleanup } = makeTempManager());
|
|
653
|
+
await addLocalProvider(manager, {
|
|
654
|
+
providerId: "editable-provider",
|
|
655
|
+
name: "Editable Provider",
|
|
656
|
+
baseUrl: "https://example.invalid/v1",
|
|
657
|
+
apiKey: "seed-key",
|
|
658
|
+
models: ["model-a", "model-b"],
|
|
659
|
+
defaultModelId: "model-a",
|
|
660
|
+
});
|
|
661
|
+
});
|
|
662
|
+
|
|
663
|
+
afterEach(() => cleanup());
|
|
664
|
+
|
|
665
|
+
it("updates provider metadata and model registry", async () => {
|
|
666
|
+
await updateLocalProvider(manager, {
|
|
667
|
+
providerId: "editable-provider",
|
|
668
|
+
name: "Renamed Provider",
|
|
669
|
+
baseUrl: "https://api.example.invalid/v2",
|
|
670
|
+
models: ["model-c", "model-d"],
|
|
671
|
+
defaultModelId: "model-d",
|
|
672
|
+
capabilities: ["vision", "reasoning"],
|
|
673
|
+
});
|
|
674
|
+
|
|
675
|
+
const provider = await LlmsModels.getProvider("editable-provider");
|
|
676
|
+
expect(provider?.name).toBe("Renamed Provider");
|
|
677
|
+
expect(provider?.baseUrl).toBe("https://api.example.invalid/v2");
|
|
678
|
+
expect(provider?.defaultModelId).toBe("model-d");
|
|
679
|
+
|
|
680
|
+
const { models } = await getLocalProviderModels("editable-provider");
|
|
681
|
+
expect(models.map((model) => model.id).sort()).toEqual([
|
|
682
|
+
"model-c",
|
|
683
|
+
"model-d",
|
|
684
|
+
]);
|
|
685
|
+
expect(models.find((model) => model.id === "model-c")?.supportsVision).toBe(
|
|
686
|
+
true,
|
|
687
|
+
);
|
|
688
|
+
expect(
|
|
689
|
+
models.find((model) => model.id === "model-c")?.supportsReasoning,
|
|
690
|
+
).toBe(true);
|
|
691
|
+
});
|
|
692
|
+
|
|
693
|
+
it("updates provider settings and can clear optional fields", async () => {
|
|
694
|
+
await updateLocalProvider(manager, {
|
|
695
|
+
providerId: "editable-provider",
|
|
696
|
+
baseUrl: "https://api.example.invalid/v3",
|
|
697
|
+
apiKey: "",
|
|
698
|
+
headers: null,
|
|
699
|
+
timeoutMs: null,
|
|
700
|
+
});
|
|
701
|
+
|
|
702
|
+
const settings = manager.getProviderSettings("editable-provider");
|
|
703
|
+
expect(settings?.baseUrl).toBe("https://api.example.invalid/v3");
|
|
704
|
+
expect(settings).not.toHaveProperty("apiKey");
|
|
705
|
+
expect(settings).not.toHaveProperty("headers");
|
|
706
|
+
expect(settings).not.toHaveProperty("timeout");
|
|
707
|
+
});
|
|
708
|
+
|
|
709
|
+
it("clears capabilities with null and does not treat [] as a clear sentinel", async () => {
|
|
710
|
+
await updateLocalProvider(manager, {
|
|
711
|
+
providerId: "editable-provider",
|
|
712
|
+
capabilities: ["vision"],
|
|
713
|
+
});
|
|
714
|
+
const modelsPath = resolveModelsRegistryPath(manager);
|
|
715
|
+
let modelsState = await readModelsFile(modelsPath);
|
|
716
|
+
expect(
|
|
717
|
+
modelsState.providers["editable-provider"]?.provider.capabilities,
|
|
718
|
+
).toEqual(["vision"]);
|
|
719
|
+
|
|
720
|
+
await updateLocalProvider(manager, {
|
|
721
|
+
providerId: "editable-provider",
|
|
722
|
+
capabilities: [],
|
|
723
|
+
});
|
|
724
|
+
modelsState = await readModelsFile(modelsPath);
|
|
725
|
+
expect(
|
|
726
|
+
modelsState.providers["editable-provider"]?.provider.capabilities,
|
|
727
|
+
).toEqual([]);
|
|
728
|
+
|
|
729
|
+
await updateLocalProvider(manager, {
|
|
730
|
+
providerId: "editable-provider",
|
|
731
|
+
capabilities: null,
|
|
732
|
+
});
|
|
733
|
+
modelsState = await readModelsFile(modelsPath);
|
|
734
|
+
expect(
|
|
735
|
+
modelsState.providers["editable-provider"]?.provider.capabilities,
|
|
736
|
+
).toBeUndefined();
|
|
737
|
+
});
|
|
738
|
+
|
|
739
|
+
it("allows modelsSourceUrl: null to clear URL while preserving existing models", async () => {
|
|
740
|
+
const fetchMock = vi.fn().mockResolvedValue({
|
|
741
|
+
ok: true,
|
|
742
|
+
json: async () => ["remote-a", "remote-b"],
|
|
743
|
+
});
|
|
744
|
+
vi.stubGlobal("fetch", fetchMock);
|
|
745
|
+
|
|
746
|
+
await updateLocalProvider(manager, {
|
|
747
|
+
providerId: "editable-provider",
|
|
748
|
+
models: ["model-a", "model-b"],
|
|
749
|
+
modelsSourceUrl: "https://example.invalid/models",
|
|
750
|
+
});
|
|
751
|
+
|
|
752
|
+
await updateLocalProvider(manager, {
|
|
753
|
+
providerId: "editable-provider",
|
|
754
|
+
modelsSourceUrl: null,
|
|
755
|
+
});
|
|
756
|
+
|
|
757
|
+
const modelsState = await readModelsFile(
|
|
758
|
+
resolveModelsRegistryPath(manager),
|
|
759
|
+
);
|
|
760
|
+
expect(
|
|
761
|
+
modelsState.providers["editable-provider"]?.provider.modelsSourceUrl,
|
|
762
|
+
).toBeUndefined();
|
|
763
|
+
|
|
764
|
+
const { models } = await getLocalProviderModels("editable-provider");
|
|
765
|
+
expect(models.map((model) => model.id).sort()).toEqual([
|
|
766
|
+
"model-a",
|
|
767
|
+
"model-b",
|
|
768
|
+
"remote-a",
|
|
769
|
+
"remote-b",
|
|
770
|
+
]);
|
|
771
|
+
expect(fetchMock).toHaveBeenCalledTimes(1);
|
|
772
|
+
});
|
|
773
|
+
|
|
774
|
+
it("throws when updating a provider that does not exist in custom registry", async () => {
|
|
775
|
+
await expect(
|
|
776
|
+
updateLocalProvider(manager, {
|
|
777
|
+
providerId: "missing-provider",
|
|
778
|
+
name: "Nope",
|
|
779
|
+
}),
|
|
780
|
+
).rejects.toThrow('"missing-provider" does not exist');
|
|
781
|
+
});
|
|
782
|
+
});
|
|
783
|
+
|
|
784
|
+
// ===========================================================================
|
|
785
|
+
// deleteLocalProvider
|
|
786
|
+
// ===========================================================================
|
|
787
|
+
|
|
788
|
+
describe("deleteLocalProvider", () => {
|
|
789
|
+
let manager: ProviderSettingsManager;
|
|
790
|
+
let cleanup: () => void;
|
|
791
|
+
|
|
792
|
+
beforeEach(async () => {
|
|
793
|
+
({ manager, cleanup } = makeTempManager());
|
|
794
|
+
await addLocalProvider(manager, {
|
|
795
|
+
providerId: "delete-me-provider",
|
|
796
|
+
name: "Delete Me",
|
|
797
|
+
baseUrl: "https://example.invalid/v1",
|
|
798
|
+
models: ["delete-model"],
|
|
799
|
+
});
|
|
800
|
+
manager.saveProviderSettings(
|
|
801
|
+
{
|
|
802
|
+
provider: "delete-me-provider",
|
|
803
|
+
baseUrl: "https://example.invalid/v1",
|
|
804
|
+
model: "delete-model",
|
|
805
|
+
},
|
|
806
|
+
{ setLastUsed: true },
|
|
807
|
+
);
|
|
808
|
+
});
|
|
809
|
+
|
|
810
|
+
afterEach(() => cleanup());
|
|
811
|
+
|
|
812
|
+
it("removes provider from registry and local settings", async () => {
|
|
813
|
+
await deleteLocalProvider(manager, {
|
|
814
|
+
providerId: "delete-me-provider",
|
|
815
|
+
});
|
|
816
|
+
|
|
817
|
+
expect(LlmsModels.hasProvider("delete-me-provider")).toBe(false);
|
|
818
|
+
expect(manager.getProviderSettings("delete-me-provider")).toBeUndefined();
|
|
819
|
+
expect(manager.getLastUsedProviderSettings()).toBeUndefined();
|
|
820
|
+
});
|
|
821
|
+
|
|
822
|
+
it("throws when deleting an unknown provider", async () => {
|
|
823
|
+
await expect(
|
|
824
|
+
deleteLocalProvider(manager, { providerId: "missing-provider" }),
|
|
825
|
+
).rejects.toThrow('"missing-provider" does not exist');
|
|
826
|
+
});
|
|
827
|
+
});
|
|
828
|
+
|
|
571
829
|
// ===========================================================================
|
|
572
830
|
// listLocalProviders
|
|
573
831
|
// ===========================================================================
|