@frockbot/plugin-provider-ollama-cloud 0.1.3 → 0.1.4
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 +4 -4
- package/frockbot.json +2 -0
- package/package.json +11 -11
- package/src/user.test.ts +6 -91
- package/src/user.ts +1 -53
- package/src/web-search.test.ts +5 -12
- package/src/web-search.ts +7 -9
package/README.md
CHANGED
|
@@ -38,10 +38,10 @@ kernel.
|
|
|
38
38
|
| Effect class | read-only, `idempotent: true` |
|
|
39
39
|
| Turn types | all four (manifest v4 `admission`) |
|
|
40
40
|
|
|
41
|
-
**Authority.** The tool mounts only
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
41
|
+
**Authority.** The tool mounts only while `ollama-cloud-web-search` is enabled
|
|
42
|
+
account-wide and resolves a ready Connection. There is no unauthenticated
|
|
43
|
+
fallback: without that capability the Bot is never offered the tool. The key
|
|
44
|
+
is leased per durable `effectId`, opened inside this Package, used
|
|
45
45
|
and settled, so it never reaches a tool argument, a tool result, or the event
|
|
46
46
|
log.
|
|
47
47
|
|
package/frockbot.json
CHANGED
|
@@ -5,10 +5,12 @@
|
|
|
5
5
|
"version": "0.0.1",
|
|
6
6
|
"compatibility": { "frockbot": ">=0.0.1" },
|
|
7
7
|
"dependencies": {
|
|
8
|
+
"custom-models": ">=0.0.1",
|
|
8
9
|
"credentials": ">=0.0.1",
|
|
9
10
|
"settings": ">=0.0.1",
|
|
10
11
|
"web": ">=0.0.1"
|
|
11
12
|
},
|
|
13
|
+
"defaultEnablement": "disabled",
|
|
12
14
|
"contributions": {
|
|
13
15
|
"backend": [{ "entry": "./user", "host": "user" }],
|
|
14
16
|
"runtime": { "entry": "./runtime" }
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@frockbot/plugin-provider-ollama-cloud",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.4",
|
|
4
4
|
"private": false,
|
|
5
5
|
"type": "module",
|
|
6
6
|
"exports": {
|
|
@@ -20,19 +20,19 @@
|
|
|
20
20
|
"typecheck": "tsc --noEmit -p tsconfig.json"
|
|
21
21
|
},
|
|
22
22
|
"dependencies": {
|
|
23
|
-
"@frockbot/configuration-core": "0.1.
|
|
24
|
-
"@frockbot/connection-core": "0.1.
|
|
25
|
-
"@frockbot/kernel-agent-loop": "0.1.
|
|
26
|
-
"@frockbot/kernel-contracts": "0.1.
|
|
27
|
-
"@frockbot/plugin-credentials": "0.1.
|
|
28
|
-
"@frockbot/plugin-models": "0.1.
|
|
29
|
-
"@frockbot/plugin-settings": "0.1.
|
|
30
|
-
"@frockbot/plugin-web": "0.1.
|
|
31
|
-
"@frockbot/provider-openai-compatible": "0.1.
|
|
23
|
+
"@frockbot/configuration-core": "0.1.4",
|
|
24
|
+
"@frockbot/connection-core": "0.1.4",
|
|
25
|
+
"@frockbot/kernel-agent-loop": "0.1.4",
|
|
26
|
+
"@frockbot/kernel-contracts": "0.1.4",
|
|
27
|
+
"@frockbot/plugin-credentials": "0.1.4",
|
|
28
|
+
"@frockbot/plugin-models": "0.1.4",
|
|
29
|
+
"@frockbot/plugin-settings": "0.1.4",
|
|
30
|
+
"@frockbot/plugin-web": "0.1.4",
|
|
31
|
+
"@frockbot/provider-openai-compatible": "0.1.4",
|
|
32
32
|
"cordis": "4.0.0-rc.8"
|
|
33
33
|
},
|
|
34
34
|
"devDependencies": {
|
|
35
|
-
"@frockbot/plugin-tools": "0.1.
|
|
35
|
+
"@frockbot/plugin-tools": "0.1.4",
|
|
36
36
|
"@types/bun": "1.3.6",
|
|
37
37
|
"typescript": "^7.0.2"
|
|
38
38
|
},
|
package/src/user.test.ts
CHANGED
|
@@ -12,7 +12,6 @@ import {
|
|
|
12
12
|
import { OllamaCloudClient, type OllamaFetch } from "./client.js";
|
|
13
13
|
import {
|
|
14
14
|
createOllamaCloudUserBackendContribution,
|
|
15
|
-
selectAutomaticOllamaModelV1,
|
|
16
15
|
type OllamaUserBackendHost,
|
|
17
16
|
} from "./user.js";
|
|
18
17
|
|
|
@@ -254,84 +253,14 @@ describe("Ollama Cloud User Contribution", () => {
|
|
|
254
253
|
models: [{ providerModelId: "glm-5.3-flash:cloud" }],
|
|
255
254
|
},
|
|
256
255
|
});
|
|
257
|
-
expect(
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
providerModelId: "glm-5.3-flash:cloud",
|
|
261
|
-
},
|
|
262
|
-
newBotModelTemplateSource: "auto",
|
|
263
|
-
});
|
|
256
|
+
expect(
|
|
257
|
+
Object.hasOwn(await settings.read("account-1"), "platformModel"),
|
|
258
|
+
).toBe(false);
|
|
264
259
|
expect(JSON.stringify([...storage.values.values()])).not.toContain(
|
|
265
260
|
"ollama-secret",
|
|
266
261
|
);
|
|
267
262
|
});
|
|
268
263
|
|
|
269
|
-
test("never replaces a User-chosen default when a Connection succeeds", async () => {
|
|
270
|
-
const { settings, ollama } = await fixture();
|
|
271
|
-
const before = await settings.read("account-1");
|
|
272
|
-
await settings.executeConfiguration({
|
|
273
|
-
schemaVersion: 1,
|
|
274
|
-
userId: "account-1",
|
|
275
|
-
command: {
|
|
276
|
-
schemaVersion: 1,
|
|
277
|
-
type: "user/set-new-bot-model",
|
|
278
|
-
commandId: "choose-model",
|
|
279
|
-
expectedRevision: before.revision,
|
|
280
|
-
model: {
|
|
281
|
-
connectionId: "chosen-connection",
|
|
282
|
-
providerModelId: "chosen-model",
|
|
283
|
-
},
|
|
284
|
-
source: "user",
|
|
285
|
-
},
|
|
286
|
-
});
|
|
287
|
-
|
|
288
|
-
await ollama.executeConnection("account-1", {
|
|
289
|
-
schemaVersion: 1,
|
|
290
|
-
type: "connection/create-api-key",
|
|
291
|
-
commandId: "connect-after-choice",
|
|
292
|
-
packageId: "provider-ollama-cloud",
|
|
293
|
-
connectionTypeId: "ollama-cloud-account",
|
|
294
|
-
label: "Work",
|
|
295
|
-
apiKey: "ollama-secret",
|
|
296
|
-
});
|
|
297
|
-
|
|
298
|
-
expect(await settings.read("account-1")).toMatchObject({
|
|
299
|
-
newBotModelTemplate: {
|
|
300
|
-
connectionId: "chosen-connection",
|
|
301
|
-
providerModelId: "chosen-model",
|
|
302
|
-
},
|
|
303
|
-
newBotModelTemplateSource: "user",
|
|
304
|
-
});
|
|
305
|
-
});
|
|
306
|
-
|
|
307
|
-
test("prefers gpt-oss, then GLM, then the first catalog model", () => {
|
|
308
|
-
const model = (providerModelId: string) => ({
|
|
309
|
-
providerModelId,
|
|
310
|
-
displayName: providerModelId,
|
|
311
|
-
capabilities: { tools: true, vision: false, reasoning: true },
|
|
312
|
-
source: "discovered" as const,
|
|
313
|
-
});
|
|
314
|
-
const catalog = (ids: string[]) => ({
|
|
315
|
-
schemaVersion: 1 as const,
|
|
316
|
-
generation: "catalog-1",
|
|
317
|
-
state: "fresh" as const,
|
|
318
|
-
models: ids.map(model),
|
|
319
|
-
});
|
|
320
|
-
expect(
|
|
321
|
-
selectAutomaticOllamaModelV1(
|
|
322
|
-
catalog(["other", "glm-5.3-flash:cloud", "gpt-oss:20b"]),
|
|
323
|
-
)?.providerModelId,
|
|
324
|
-
).toBe("gpt-oss:20b");
|
|
325
|
-
expect(
|
|
326
|
-
selectAutomaticOllamaModelV1(catalog(["other", "glm-5.3-flash:cloud"]))
|
|
327
|
-
?.providerModelId,
|
|
328
|
-
).toBe("glm-5.3-flash:cloud");
|
|
329
|
-
expect(
|
|
330
|
-
selectAutomaticOllamaModelV1(catalog(["other", "second"]))
|
|
331
|
-
?.providerModelId,
|
|
332
|
-
).toBe("other");
|
|
333
|
-
});
|
|
334
|
-
|
|
335
264
|
test("atomically admits the command, Connection, and encrypted credential", async () => {
|
|
336
265
|
const { storage, settings, ollama } = await fixture();
|
|
337
266
|
storage.failNextEntriesContaining =
|
|
@@ -891,7 +820,7 @@ describe("Ollama Cloud User Contribution", () => {
|
|
|
891
820
|
);
|
|
892
821
|
});
|
|
893
822
|
|
|
894
|
-
test("
|
|
823
|
+
test("disconnects without a retired per-Bot dependency lock", async () => {
|
|
895
824
|
const { settings, ollama } = await fixture();
|
|
896
825
|
const created = await ollama.executeConnection("account-1", {
|
|
897
826
|
schemaVersion: 1,
|
|
@@ -902,20 +831,6 @@ describe("Ollama Cloud User Contribution", () => {
|
|
|
902
831
|
label: "Work",
|
|
903
832
|
apiKey: "key",
|
|
904
833
|
});
|
|
905
|
-
await settings.claimConnectionDependency(
|
|
906
|
-
"account-1",
|
|
907
|
-
created.connectionId,
|
|
908
|
-
"bot-1",
|
|
909
|
-
"assignment-1",
|
|
910
|
-
{
|
|
911
|
-
schemaVersion: 1,
|
|
912
|
-
packageId: "provider-ollama-cloud",
|
|
913
|
-
packageVersion: "0.0.1",
|
|
914
|
-
capabilityId: "ollama-cloud-models",
|
|
915
|
-
connectionTypeIds: ["ollama-cloud-account"],
|
|
916
|
-
},
|
|
917
|
-
);
|
|
918
|
-
|
|
919
834
|
await expect(
|
|
920
835
|
ollama.executeConnection("account-1", {
|
|
921
836
|
schemaVersion: 1,
|
|
@@ -924,10 +839,10 @@ describe("Ollama Cloud User Contribution", () => {
|
|
|
924
839
|
connectionId: created.connectionId,
|
|
925
840
|
revokeUpstream: false,
|
|
926
841
|
}),
|
|
927
|
-
).resolves.toMatchObject({ status: "
|
|
842
|
+
).resolves.toMatchObject({ status: "applied" });
|
|
928
843
|
expect(
|
|
929
844
|
await settings.getConnection("account-1", created.connectionId),
|
|
930
|
-
).toMatchObject({ state: "
|
|
845
|
+
).toMatchObject({ state: "revoked" });
|
|
931
846
|
});
|
|
932
847
|
|
|
933
848
|
test("does not downgrade concurrent upstream revocation reconciliation", async () => {
|
package/src/user.ts
CHANGED
|
@@ -44,23 +44,6 @@ const MAX_CATALOG_REFRESHES_PER_ALARM = 1;
|
|
|
44
44
|
const ACCOUNT_KEY = "ollama-connection-account";
|
|
45
45
|
const AUTOMATIC_REFRESH_RECEIPT_PREFIX = "ollama-refresh-receipt:";
|
|
46
46
|
|
|
47
|
-
const AUTOMATIC_DEFAULT_MODEL_PREFERENCES = [
|
|
48
|
-
"gpt-oss:20b",
|
|
49
|
-
"glm-5.3-flash:cloud",
|
|
50
|
-
] as const;
|
|
51
|
-
|
|
52
|
-
/** Choose the safest useful default from the catalog a new Connection proved. */
|
|
53
|
-
export function selectAutomaticOllamaModelV1(
|
|
54
|
-
catalog: ConnectionModelCatalogV1,
|
|
55
|
-
): ConnectionModelV1 | undefined {
|
|
56
|
-
for (const providerModelId of AUTOMATIC_DEFAULT_MODEL_PREFERENCES) {
|
|
57
|
-
const preferred = catalog.models.find(
|
|
58
|
-
(model) => model.providerModelId === providerModelId,
|
|
59
|
-
);
|
|
60
|
-
if (preferred) return preferred;
|
|
61
|
-
}
|
|
62
|
-
return catalog.models[0];
|
|
63
|
-
}
|
|
64
47
|
const MUTATION_SEQUENCE_PREFIX = "ollama-mutation-sequence:";
|
|
65
48
|
const MODEL_RESOLUTION_PREFIX = "ollama-model-resolution:";
|
|
66
49
|
const REFRESH_INTERVAL_MS = 60 * 60 * 1_000;
|
|
@@ -1402,35 +1385,6 @@ export class OllamaCloudUserBackendContribution {
|
|
|
1402
1385
|
},
|
|
1403
1386
|
storage,
|
|
1404
1387
|
);
|
|
1405
|
-
if (
|
|
1406
|
-
record.operation === "connection/create-api-key" &&
|
|
1407
|
-
outcome.validationCatalog
|
|
1408
|
-
) {
|
|
1409
|
-
const user = await this.host.settings.read(
|
|
1410
|
-
record.accountId,
|
|
1411
|
-
storage,
|
|
1412
|
-
);
|
|
1413
|
-
const selected = selectAutomaticOllamaModelV1(
|
|
1414
|
-
outcome.validationCatalog,
|
|
1415
|
-
);
|
|
1416
|
-
if (selected && user.newBotModelTemplateSource !== "user") {
|
|
1417
|
-
await this.host.settings.executeConfigurationCommand(
|
|
1418
|
-
record.accountId,
|
|
1419
|
-
{
|
|
1420
|
-
schemaVersion: 1,
|
|
1421
|
-
type: "user/set-new-bot-model",
|
|
1422
|
-
commandId: record.commandId,
|
|
1423
|
-
expectedRevision: user.revision,
|
|
1424
|
-
model: {
|
|
1425
|
-
connectionId: record.connectionId,
|
|
1426
|
-
providerModelId: selected.providerModelId,
|
|
1427
|
-
},
|
|
1428
|
-
source: "auto",
|
|
1429
|
-
},
|
|
1430
|
-
storage,
|
|
1431
|
-
);
|
|
1432
|
-
}
|
|
1433
|
-
}
|
|
1434
1388
|
},
|
|
1435
1389
|
);
|
|
1436
1390
|
} catch (error) {
|
|
@@ -1858,12 +1812,6 @@ export class OllamaCloudUserBackendContribution {
|
|
|
1858
1812
|
if (current.state === "reconciliation-required") {
|
|
1859
1813
|
return "reconciliation-required";
|
|
1860
1814
|
}
|
|
1861
|
-
if (
|
|
1862
|
-
Array.isArray(current.safeMetadata.dependentAssignments) &&
|
|
1863
|
-
current.safeMetadata.dependentAssignments.length > 0
|
|
1864
|
-
) {
|
|
1865
|
-
return "dependent";
|
|
1866
|
-
}
|
|
1867
1815
|
await this.cancelPendingCredentialMutations(record, storage);
|
|
1868
1816
|
if (current.state !== "revoking") {
|
|
1869
1817
|
await this.host.settings.replaceConnection(
|
|
@@ -1877,7 +1825,7 @@ export class OllamaCloudUserBackendContribution {
|
|
|
1877
1825
|
return "revoking";
|
|
1878
1826
|
},
|
|
1879
1827
|
);
|
|
1880
|
-
if (transition === "stale"
|
|
1828
|
+
if (transition === "stale") {
|
|
1881
1829
|
return this.finishRecord(record, "failed");
|
|
1882
1830
|
}
|
|
1883
1831
|
if (transition === "revoked") return this.finishRecord(record, "applied");
|
package/src/web-search.test.ts
CHANGED
|
@@ -13,11 +13,10 @@ const CONNECTION_ID = "connection-1";
|
|
|
13
13
|
const GENERATION = "generation-1";
|
|
14
14
|
const API_KEY = "ollama-test-key";
|
|
15
15
|
|
|
16
|
-
const
|
|
16
|
+
const CAPABILITY = {
|
|
17
17
|
packageId: "provider-ollama-cloud",
|
|
18
18
|
capabilityId: "ollama-cloud-web-search",
|
|
19
19
|
connectionId: CONNECTION_ID,
|
|
20
|
-
state: "enabled",
|
|
21
20
|
} as const;
|
|
22
21
|
|
|
23
22
|
interface Recorded {
|
|
@@ -86,7 +85,7 @@ async function mount(options: {
|
|
|
86
85
|
await root.plugin(ToolRegistry);
|
|
87
86
|
await root.plugin(FakeCredentialLease);
|
|
88
87
|
const plugin = createConfiguredOllamaWebSearchRuntimeContribution({
|
|
89
|
-
|
|
88
|
+
capability: CAPABILITY,
|
|
90
89
|
accountId: "user-1",
|
|
91
90
|
connectionId: CONNECTION_ID,
|
|
92
91
|
connectionGeneration: GENERATION,
|
|
@@ -336,7 +335,7 @@ describe("the Ollama Cloud web_search Capability", () => {
|
|
|
336
335
|
await root.fiber.dispose();
|
|
337
336
|
});
|
|
338
337
|
|
|
339
|
-
test("mounts
|
|
338
|
+
test("mounts only for the enabled Capability bound to the Connection", () => {
|
|
340
339
|
const base = {
|
|
341
340
|
accountId: "user-1",
|
|
342
341
|
connectionId: CONNECTION_ID,
|
|
@@ -347,19 +346,13 @@ describe("the Ollama Cloud web_search Capability", () => {
|
|
|
347
346
|
expect(
|
|
348
347
|
createConfiguredOllamaWebSearchRuntimeContribution({
|
|
349
348
|
...base,
|
|
350
|
-
|
|
349
|
+
capability: { ...CAPABILITY, capabilityId: "ollama-cloud-models" },
|
|
351
350
|
}),
|
|
352
351
|
).toBeUndefined();
|
|
353
352
|
expect(
|
|
354
353
|
createConfiguredOllamaWebSearchRuntimeContribution({
|
|
355
354
|
...base,
|
|
356
|
-
|
|
357
|
-
}),
|
|
358
|
-
).toBeUndefined();
|
|
359
|
-
expect(
|
|
360
|
-
createConfiguredOllamaWebSearchRuntimeContribution({
|
|
361
|
-
...base,
|
|
362
|
-
assignment: { ...ASSIGNMENT, connectionId: "other" },
|
|
355
|
+
capability: { ...CAPABILITY, connectionId: "other" },
|
|
363
356
|
}),
|
|
364
357
|
).toBeUndefined();
|
|
365
358
|
});
|
package/src/web-search.ts
CHANGED
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
// `@frockbot/plugin-web/contract`, and a second provider satisfies it with no
|
|
13
13
|
// change here and none in the kernel.
|
|
14
14
|
//
|
|
15
|
-
// AUTHORITY. `web_search` needs
|
|
15
|
+
// AUTHORITY. `web_search` needs the enabled
|
|
16
16
|
// `ollama-cloud-web-search` Capability bound to a ready `ollama-cloud-account`
|
|
17
17
|
// Connection. Without one this module mounts nothing and the tool is simply
|
|
18
18
|
// absent from the catalog, which is what "fail visibly" means for a tool: the
|
|
@@ -249,16 +249,15 @@ export function createOllamaWebSearchRuntimePlugin(
|
|
|
249
249
|
}
|
|
250
250
|
|
|
251
251
|
/**
|
|
252
|
-
* The
|
|
253
|
-
* enabled
|
|
252
|
+
* The enablement fence (D3): `web_search` exists for a Bot only through an
|
|
253
|
+
* enabled `ollama-cloud-web-search` Capability bound to a Connection. There
|
|
254
254
|
* is no unauthenticated fallback provider.
|
|
255
255
|
*/
|
|
256
256
|
export function createConfiguredOllamaWebSearchRuntimeContribution(config: {
|
|
257
|
-
|
|
257
|
+
capability: {
|
|
258
258
|
packageId: string;
|
|
259
259
|
capabilityId: string;
|
|
260
260
|
connectionId?: string;
|
|
261
|
-
state: string;
|
|
262
261
|
};
|
|
263
262
|
accountId: string;
|
|
264
263
|
connectionId: string;
|
|
@@ -274,10 +273,9 @@ export function createConfiguredOllamaWebSearchRuntimeContribution(config: {
|
|
|
274
273
|
fetch?: OllamaFetch;
|
|
275
274
|
}): Plugin.Function | undefined {
|
|
276
275
|
if (
|
|
277
|
-
config.
|
|
278
|
-
config.
|
|
279
|
-
config.
|
|
280
|
-
config.assignment.connectionId !== config.connectionId
|
|
276
|
+
config.capability.packageId !== "provider-ollama-cloud" ||
|
|
277
|
+
config.capability.capabilityId !== "ollama-cloud-web-search" ||
|
|
278
|
+
config.capability.connectionId !== config.connectionId
|
|
281
279
|
) {
|
|
282
280
|
return undefined;
|
|
283
281
|
}
|