@frockbot/plugin-settings 0.1.3 → 0.2.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/frockbot.json +1 -0
- package/package.json +9 -9
- package/src/client/BotSettingsSurface.vue +22 -575
- package/src/client/ConnectionsSurface.vue +59 -10
- package/src/client/{PluginsTrigger.vue → ConnectorsTrigger.vue} +2 -2
- package/src/client/ModelsSurface.vue +26 -364
- package/src/client/PackageAccounts.vue +2 -25
- package/src/client/PackageCatalogSurface.vue +3 -3
- package/src/client/PackageSettingsForm.vue +6 -1
- package/src/client/PluginsSurface.vue +1 -1
- package/src/client/UserProfileTrigger.vue +2 -2
- package/src/client/UserSettingsSurface.vue +4 -18
- package/src/client/bot-settings.test.ts +71 -160
- package/src/client/bot-settings.ts +5 -42
- package/src/client/index.test.ts +7 -41
- package/src/client/index.ts +15 -5
- package/src/client/package-surfaces.test.ts +29 -3
- package/src/client/package-surfaces.ts +12 -3
- package/src/user.test.ts +425 -316
- package/src/user.ts +133 -346
- package/src/client/assignment-operations.ts +0 -22
|
@@ -88,9 +88,12 @@ async function save(): Promise<void> {
|
|
|
88
88
|
>
|
|
89
89
|
<p class="field-hint">
|
|
90
90
|
Settings of the Packages you have enabled. Model providers are
|
|
91
|
-
configured in Models and accounts in
|
|
91
|
+
configured in Models and accounts in Connectors.
|
|
92
92
|
</p>
|
|
93
93
|
<PackageSettingsSection />
|
|
94
|
+
<UiButton type="button" @click="surfaces.open('plugins')">
|
|
95
|
+
Open Plugins
|
|
96
|
+
</UiButton>
|
|
94
97
|
</UiAnchor>
|
|
95
98
|
<p v-if="web.settingsError" class="settings-error" role="alert">
|
|
96
99
|
{{ web.settingsError }}
|
|
@@ -174,23 +177,6 @@ async function save(): Promise<void> {
|
|
|
174
177
|
font-size: var(--frock-text-sm);
|
|
175
178
|
}
|
|
176
179
|
|
|
177
|
-
.model-empty {
|
|
178
|
-
display: flex;
|
|
179
|
-
flex-direction: column;
|
|
180
|
-
align-items: flex-start;
|
|
181
|
-
gap: 10px;
|
|
182
|
-
padding: 12px;
|
|
183
|
-
border: 1px solid var(--frock-border);
|
|
184
|
-
border-radius: var(--frock-radius-card);
|
|
185
|
-
background: var(--frock-surface-subtle);
|
|
186
|
-
}
|
|
187
|
-
|
|
188
|
-
.model-empty p {
|
|
189
|
-
margin: 0;
|
|
190
|
-
color: var(--frock-text-muted);
|
|
191
|
-
font-size: var(--frock-text-sm);
|
|
192
|
-
}
|
|
193
|
-
|
|
194
180
|
.settings-error {
|
|
195
181
|
margin: 0;
|
|
196
182
|
color: var(--frock-danger-text);
|
|
@@ -1,183 +1,94 @@
|
|
|
1
1
|
import { describe, expect, test } from "bun:test";
|
|
2
2
|
import {
|
|
3
3
|
decodeModelSelection,
|
|
4
|
-
|
|
4
|
+
describeModelBinding,
|
|
5
5
|
encodeModelSelection,
|
|
6
6
|
eligibleModelConnections,
|
|
7
7
|
isModelConnectionEligible,
|
|
8
8
|
modelSelectOptions,
|
|
9
|
-
resolveBotSettingsModel,
|
|
10
9
|
} from "./bot-settings.js";
|
|
11
10
|
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
displayName: "Work",
|
|
19
|
-
state: "ready" as const,
|
|
20
|
-
providerType: "ollama-cloud",
|
|
21
|
-
generation: "generation-1",
|
|
22
|
-
safeMetadata: {},
|
|
23
|
-
};
|
|
24
|
-
const catalog = [
|
|
11
|
+
const catalog = [
|
|
12
|
+
{
|
|
13
|
+
packageId: "provider-ollama-cloud",
|
|
14
|
+
displayName: "Ollama Cloud",
|
|
15
|
+
version: "0.0.1",
|
|
16
|
+
capabilities: [
|
|
25
17
|
{
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
capabilities: [
|
|
30
|
-
{
|
|
31
|
-
id: "ollama-cloud-models",
|
|
32
|
-
kind: "model" as const,
|
|
33
|
-
connectionTypes: ["ollama-cloud-account"],
|
|
34
|
-
},
|
|
35
|
-
],
|
|
36
|
-
connectionTypes: [
|
|
37
|
-
{
|
|
38
|
-
id: "ollama-cloud-account",
|
|
39
|
-
displayName: "Ollama Cloud account",
|
|
40
|
-
allowMultiple: true,
|
|
41
|
-
authorizationKind: "api-key" as const,
|
|
42
|
-
capabilities: ["ollama-cloud-models"],
|
|
43
|
-
},
|
|
44
|
-
],
|
|
18
|
+
id: "ollama-cloud-models",
|
|
19
|
+
kind: "model" as const,
|
|
20
|
+
connectionTypes: ["ollama-cloud-account"],
|
|
45
21
|
},
|
|
46
|
-
]
|
|
22
|
+
],
|
|
23
|
+
connectionTypes: [
|
|
24
|
+
{
|
|
25
|
+
id: "ollama-cloud-account",
|
|
26
|
+
displayName: "Ollama Cloud account",
|
|
27
|
+
allowMultiple: true,
|
|
28
|
+
authorizationKind: "api-key" as const,
|
|
29
|
+
capabilities: ["ollama-cloud-models"],
|
|
30
|
+
},
|
|
31
|
+
],
|
|
32
|
+
},
|
|
33
|
+
];
|
|
34
|
+
const packages = [
|
|
35
|
+
{
|
|
36
|
+
packageId: "provider-ollama-cloud",
|
|
37
|
+
version: "0.0.1",
|
|
38
|
+
state: "installed" as const,
|
|
39
|
+
},
|
|
40
|
+
];
|
|
41
|
+
const connections = [
|
|
42
|
+
{
|
|
43
|
+
connectionId: "ollama-1",
|
|
44
|
+
packageId: "provider-ollama-cloud",
|
|
45
|
+
connectionTypeId: "ollama-cloud-account",
|
|
46
|
+
displayName: "Work",
|
|
47
|
+
state: "ready" as const,
|
|
48
|
+
providerType: "ollama-cloud",
|
|
49
|
+
modelCatalog: {
|
|
50
|
+
schemaVersion: 1 as const,
|
|
51
|
+
generation: "catalog-1",
|
|
52
|
+
state: "fresh" as const,
|
|
53
|
+
models: [
|
|
54
|
+
{
|
|
55
|
+
providerModelId: "llama-3:cloud",
|
|
56
|
+
displayName: "Llama 3",
|
|
57
|
+
capabilities: { tools: true, vision: false, reasoning: false },
|
|
58
|
+
source: "discovered" as const,
|
|
59
|
+
},
|
|
60
|
+
],
|
|
61
|
+
},
|
|
62
|
+
safeMetadata: {},
|
|
63
|
+
},
|
|
64
|
+
{
|
|
65
|
+
connectionId: "ollama-2",
|
|
66
|
+
packageId: "provider-ollama-cloud",
|
|
67
|
+
connectionTypeId: "ollama-cloud-account",
|
|
68
|
+
displayName: "Revoked",
|
|
69
|
+
state: "revoked" as const,
|
|
70
|
+
safeMetadata: {},
|
|
71
|
+
},
|
|
72
|
+
];
|
|
47
73
|
|
|
74
|
+
describe("shared model option building", () => {
|
|
75
|
+
test("requires an enabled model provider Package", () => {
|
|
48
76
|
expect(
|
|
49
77
|
isModelConnectionEligible({
|
|
50
|
-
connection
|
|
51
|
-
packages: [
|
|
52
|
-
{
|
|
53
|
-
packageId: "provider-ollama-cloud",
|
|
54
|
-
version: "0.0.1",
|
|
55
|
-
state: "disabled",
|
|
56
|
-
},
|
|
57
|
-
],
|
|
78
|
+
connection: connections[0]!,
|
|
79
|
+
packages: [{ ...packages[0]!, state: "disabled" }],
|
|
58
80
|
catalog,
|
|
59
81
|
}),
|
|
60
82
|
).toBe(false);
|
|
61
83
|
expect(
|
|
62
84
|
isModelConnectionEligible({
|
|
63
|
-
connection
|
|
64
|
-
packages
|
|
65
|
-
{
|
|
66
|
-
packageId: "provider-ollama-cloud",
|
|
67
|
-
version: "0.0.1",
|
|
68
|
-
state: "installed",
|
|
69
|
-
},
|
|
70
|
-
],
|
|
85
|
+
connection: connections[0]!,
|
|
86
|
+
packages,
|
|
71
87
|
catalog,
|
|
72
88
|
}),
|
|
73
89
|
).toBe(true);
|
|
74
90
|
});
|
|
75
91
|
|
|
76
|
-
test("preserves a model-less Bot while unrelated settings save", () => {
|
|
77
|
-
expect(
|
|
78
|
-
resolveBotSettingsModel({
|
|
79
|
-
useExactModel: false,
|
|
80
|
-
selectedModel: "",
|
|
81
|
-
exactConnectionId: "",
|
|
82
|
-
exactProviderModelId: "",
|
|
83
|
-
}),
|
|
84
|
-
).toBeUndefined();
|
|
85
|
-
});
|
|
86
|
-
|
|
87
|
-
test("rejects malformed model selections before settings mutate", () => {
|
|
88
|
-
expect(() =>
|
|
89
|
-
resolveBotSettingsModel({
|
|
90
|
-
useExactModel: false,
|
|
91
|
-
selectedModel: "not-json",
|
|
92
|
-
exactConnectionId: "",
|
|
93
|
-
exactProviderModelId: "",
|
|
94
|
-
}),
|
|
95
|
-
).toThrow("A Connection and model ID are required");
|
|
96
|
-
});
|
|
97
|
-
|
|
98
|
-
test("preserves an existing binding without a new selection", () => {
|
|
99
|
-
expect(
|
|
100
|
-
resolveBotSettingsModel({
|
|
101
|
-
current: {
|
|
102
|
-
connectionId: "ollama-1",
|
|
103
|
-
providerModelId: "glm:cloud",
|
|
104
|
-
},
|
|
105
|
-
useExactModel: false,
|
|
106
|
-
selectedModel: "",
|
|
107
|
-
exactConnectionId: "",
|
|
108
|
-
exactProviderModelId: "",
|
|
109
|
-
}),
|
|
110
|
-
).toEqual({
|
|
111
|
-
connectionId: "ollama-1",
|
|
112
|
-
providerModelId: "glm:cloud",
|
|
113
|
-
});
|
|
114
|
-
});
|
|
115
|
-
});
|
|
116
|
-
|
|
117
|
-
describe("shared model option building", () => {
|
|
118
|
-
const catalog = [
|
|
119
|
-
{
|
|
120
|
-
packageId: "provider-ollama-cloud",
|
|
121
|
-
displayName: "Ollama Cloud",
|
|
122
|
-
version: "0.0.1",
|
|
123
|
-
capabilities: [
|
|
124
|
-
{
|
|
125
|
-
id: "ollama-cloud-models",
|
|
126
|
-
kind: "model" as const,
|
|
127
|
-
connectionTypes: ["ollama-cloud-account"],
|
|
128
|
-
},
|
|
129
|
-
],
|
|
130
|
-
connectionTypes: [
|
|
131
|
-
{
|
|
132
|
-
id: "ollama-cloud-account",
|
|
133
|
-
displayName: "Ollama Cloud account",
|
|
134
|
-
allowMultiple: true,
|
|
135
|
-
authorizationKind: "api-key" as const,
|
|
136
|
-
capabilities: ["ollama-cloud-models"],
|
|
137
|
-
},
|
|
138
|
-
],
|
|
139
|
-
},
|
|
140
|
-
];
|
|
141
|
-
const packages = [
|
|
142
|
-
{
|
|
143
|
-
packageId: "provider-ollama-cloud",
|
|
144
|
-
version: "0.0.1",
|
|
145
|
-
state: "installed" as const,
|
|
146
|
-
},
|
|
147
|
-
];
|
|
148
|
-
const connections = [
|
|
149
|
-
{
|
|
150
|
-
connectionId: "ollama-1",
|
|
151
|
-
packageId: "provider-ollama-cloud",
|
|
152
|
-
connectionTypeId: "ollama-cloud-account",
|
|
153
|
-
displayName: "Work",
|
|
154
|
-
state: "ready" as const,
|
|
155
|
-
providerType: "ollama-cloud",
|
|
156
|
-
modelCatalog: {
|
|
157
|
-
schemaVersion: 1 as const,
|
|
158
|
-
generation: "catalog-1",
|
|
159
|
-
state: "fresh" as const,
|
|
160
|
-
models: [
|
|
161
|
-
{
|
|
162
|
-
providerModelId: "llama-3:cloud",
|
|
163
|
-
displayName: "Llama 3",
|
|
164
|
-
capabilities: { tools: true, vision: false, reasoning: false },
|
|
165
|
-
source: "discovered" as const,
|
|
166
|
-
},
|
|
167
|
-
],
|
|
168
|
-
},
|
|
169
|
-
safeMetadata: {},
|
|
170
|
-
},
|
|
171
|
-
{
|
|
172
|
-
connectionId: "ollama-2",
|
|
173
|
-
packageId: "provider-ollama-cloud",
|
|
174
|
-
connectionTypeId: "ollama-cloud-account",
|
|
175
|
-
displayName: "Revoked",
|
|
176
|
-
state: "revoked" as const,
|
|
177
|
-
safeMetadata: {},
|
|
178
|
-
},
|
|
179
|
-
];
|
|
180
|
-
|
|
181
92
|
test("offers one option per advertised model of an eligible Connection", () => {
|
|
182
93
|
const eligible = eligibleModelConnections({
|
|
183
94
|
connections,
|
|
@@ -195,7 +106,7 @@ describe("shared model option building", () => {
|
|
|
195
106
|
]);
|
|
196
107
|
});
|
|
197
108
|
|
|
198
|
-
test("round-trips a
|
|
109
|
+
test("round-trips a binding and names it for prose", () => {
|
|
199
110
|
const model = {
|
|
200
111
|
connectionId: "ollama-1",
|
|
201
112
|
providerModelId: "llama-3:cloud",
|
|
@@ -206,13 +117,13 @@ describe("shared model option building", () => {
|
|
|
206
117
|
expect(() => decodeModelSelection("not-json")).toThrow(
|
|
207
118
|
"A Connection and model ID are required",
|
|
208
119
|
);
|
|
209
|
-
expect(
|
|
120
|
+
expect(describeModelBinding(model, connections)).toBe("Llama 3 — Work");
|
|
210
121
|
expect(
|
|
211
|
-
|
|
122
|
+
describeModelBinding(
|
|
212
123
|
{ connectionId: "ollama-1", providerModelId: "custom:cloud" },
|
|
213
124
|
connections,
|
|
214
125
|
),
|
|
215
126
|
).toBe("custom:cloud — Work");
|
|
216
|
-
expect(
|
|
127
|
+
expect(describeModelBinding(undefined, connections)).toBeUndefined();
|
|
217
128
|
});
|
|
218
129
|
});
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type {
|
|
2
2
|
ConnectionView,
|
|
3
|
-
|
|
3
|
+
ModelBindingV1,
|
|
4
4
|
PackageInstallationView,
|
|
5
5
|
} from "@frockbot/configuration-core";
|
|
6
6
|
import type { PluginCatalogItem } from "@frockbot/plugin-shell/shared";
|
|
@@ -31,43 +31,6 @@ export function isModelConnectionEligible(input: {
|
|
|
31
31
|
);
|
|
32
32
|
}
|
|
33
33
|
|
|
34
|
-
export function resolveBotSettingsModel(input: {
|
|
35
|
-
current?: ModelAssignment;
|
|
36
|
-
useExactModel: boolean;
|
|
37
|
-
selectedModel: string;
|
|
38
|
-
exactConnectionId: string;
|
|
39
|
-
exactProviderModelId: string;
|
|
40
|
-
}): ModelAssignment | undefined {
|
|
41
|
-
if (input.useExactModel) {
|
|
42
|
-
const selected = {
|
|
43
|
-
connectionId: input.exactConnectionId,
|
|
44
|
-
providerModelId: input.exactProviderModelId.trim(),
|
|
45
|
-
};
|
|
46
|
-
if (!selected.connectionId || !selected.providerModelId) {
|
|
47
|
-
throw new Error("A Connection and model ID are required");
|
|
48
|
-
}
|
|
49
|
-
return selected;
|
|
50
|
-
}
|
|
51
|
-
if (!input.selectedModel) return input.current;
|
|
52
|
-
let value: unknown;
|
|
53
|
-
try {
|
|
54
|
-
value = JSON.parse(input.selectedModel);
|
|
55
|
-
} catch {
|
|
56
|
-
throw new Error("A Connection and model ID are required");
|
|
57
|
-
}
|
|
58
|
-
if (
|
|
59
|
-
!Array.isArray(value) ||
|
|
60
|
-
value.length !== 2 ||
|
|
61
|
-
typeof value[0] !== "string" ||
|
|
62
|
-
value[0].length === 0 ||
|
|
63
|
-
typeof value[1] !== "string" ||
|
|
64
|
-
value[1].length === 0
|
|
65
|
-
) {
|
|
66
|
-
throw new Error("A Connection and model ID are required");
|
|
67
|
-
}
|
|
68
|
-
return { connectionId: value[0], providerModelId: value[1] };
|
|
69
|
-
}
|
|
70
|
-
|
|
71
34
|
export interface ModelSelectOption {
|
|
72
35
|
value: string;
|
|
73
36
|
label: string;
|
|
@@ -103,15 +66,15 @@ export function modelSelectOptions(
|
|
|
103
66
|
);
|
|
104
67
|
}
|
|
105
68
|
|
|
106
|
-
export function encodeModelSelection(model?:
|
|
69
|
+
export function encodeModelSelection(model?: ModelBindingV1): string {
|
|
107
70
|
return model
|
|
108
71
|
? JSON.stringify([model.connectionId, model.providerModelId])
|
|
109
72
|
: "";
|
|
110
73
|
}
|
|
111
74
|
|
|
112
75
|
/** How a bound model reads in prose, e.g. "Llama 3 — Work". */
|
|
113
|
-
export function
|
|
114
|
-
model:
|
|
76
|
+
export function describeModelBinding(
|
|
77
|
+
model: ModelBindingV1 | undefined,
|
|
115
78
|
connections: readonly ConnectionView[],
|
|
116
79
|
): string | undefined {
|
|
117
80
|
if (!model) return undefined;
|
|
@@ -128,7 +91,7 @@ export function describeModelAssignment(
|
|
|
128
91
|
/** The inverse of {@link encodeModelSelection}; an empty value means no model. */
|
|
129
92
|
export function decodeModelSelection(
|
|
130
93
|
value: string,
|
|
131
|
-
):
|
|
94
|
+
): ModelBindingV1 | undefined {
|
|
132
95
|
if (!value) return undefined;
|
|
133
96
|
let parsed: unknown;
|
|
134
97
|
try {
|
package/src/client/index.test.ts
CHANGED
|
@@ -5,50 +5,10 @@ import {
|
|
|
5
5
|
type ClientSlotRegistration,
|
|
6
6
|
} from "@frockbot/client-core";
|
|
7
7
|
import { createClientSurfaceRegistry } from "@frockbot/client-ui";
|
|
8
|
+
import manifest from "../../frockbot.json";
|
|
8
9
|
import { settingsClientPlugin } from "./index.js";
|
|
9
|
-
import {
|
|
10
|
-
assignmentHasPendingOperation,
|
|
11
|
-
projectAssignmentOperations,
|
|
12
|
-
} from "./assignment-operations.js";
|
|
13
10
|
|
|
14
11
|
describe("settings client contribution", () => {
|
|
15
|
-
it("projects every Assignment operation without catalog ownership", () => {
|
|
16
|
-
const operations = [
|
|
17
|
-
{
|
|
18
|
-
commandId: "assign-1",
|
|
19
|
-
kind: "assigning" as const,
|
|
20
|
-
assignmentId: "orphan-assign",
|
|
21
|
-
state: "retrying" as const,
|
|
22
|
-
target: {
|
|
23
|
-
assignmentId: "orphan-assign",
|
|
24
|
-
packageId: "missing-package",
|
|
25
|
-
capabilityId: "missing-capability",
|
|
26
|
-
},
|
|
27
|
-
},
|
|
28
|
-
{
|
|
29
|
-
commandId: "replace-1",
|
|
30
|
-
kind: "replacing" as const,
|
|
31
|
-
assignmentId: "orphan-replace",
|
|
32
|
-
state: "pending" as const,
|
|
33
|
-
},
|
|
34
|
-
{
|
|
35
|
-
commandId: "unassign-1",
|
|
36
|
-
kind: "unassigning" as const,
|
|
37
|
-
assignmentId: "orphan-unassign",
|
|
38
|
-
state: "retrying" as const,
|
|
39
|
-
},
|
|
40
|
-
];
|
|
41
|
-
const projected = projectAssignmentOperations({
|
|
42
|
-
assignmentOperations: operations,
|
|
43
|
-
});
|
|
44
|
-
expect(projected).toEqual(operations);
|
|
45
|
-
expect(projected).not.toBe(operations);
|
|
46
|
-
expect(assignmentHasPendingOperation(projected, "orphan-unassign")).toBe(
|
|
47
|
-
true,
|
|
48
|
-
);
|
|
49
|
-
expect(assignmentHasPendingOperation(projected, "stable")).toBe(false);
|
|
50
|
-
});
|
|
51
|
-
|
|
52
12
|
it("registers feature surfaces and shell-owned trigger seats", () => {
|
|
53
13
|
const surfaces = createClientSurfaceRegistry();
|
|
54
14
|
const slots: ClientSlotRegistration[] = [];
|
|
@@ -95,6 +55,12 @@ describe("settings client contribution", () => {
|
|
|
95
55
|
]) {
|
|
96
56
|
expect(surfaces.has(id)).toBe(true);
|
|
97
57
|
}
|
|
58
|
+
surfaces.open("connections");
|
|
59
|
+
expect(surfaces.active.value?.title).toBe("Connectors");
|
|
60
|
+
surfaces.close();
|
|
61
|
+
expect(manifest.contributions.client.outlets).toContain(
|
|
62
|
+
"frockbot.models-sections",
|
|
63
|
+
);
|
|
98
64
|
|
|
99
65
|
for (const dispose of result.toReversed()) dispose();
|
|
100
66
|
expect(slots).toEqual([]);
|
package/src/client/index.ts
CHANGED
|
@@ -7,11 +7,11 @@ import {
|
|
|
7
7
|
import BotPanel from "./BotPanel.vue";
|
|
8
8
|
import BotSettingsSurface from "./BotSettingsSurface.vue";
|
|
9
9
|
import BotSettingsTrigger from "./BotSettingsTrigger.vue";
|
|
10
|
+
import ConnectorsTrigger from "./ConnectorsTrigger.vue";
|
|
10
11
|
import ConnectionsSurface from "./ConnectionsSurface.vue";
|
|
11
12
|
import ModelsSurface from "./ModelsSurface.vue";
|
|
12
13
|
import PluginsSurface from "./PluginsSurface.vue";
|
|
13
14
|
import PackageCatalogSurface from "./PackageCatalogSurface.vue";
|
|
14
|
-
import PluginsTrigger from "./PluginsTrigger.vue";
|
|
15
15
|
import UserProfileTrigger from "./UserProfileTrigger.vue";
|
|
16
16
|
import UserSettingsSurface from "./UserSettingsSurface.vue";
|
|
17
17
|
|
|
@@ -25,8 +25,8 @@ export const settingsClientPlugin: ClientPlugin = (ctx) => {
|
|
|
25
25
|
placement: "panel",
|
|
26
26
|
}),
|
|
27
27
|
// Enablement and configuration are separate surfaces: Plugins turns a
|
|
28
|
-
// Package on and off, Models
|
|
29
|
-
//
|
|
28
|
+
// Package on and off, Models hosts opt-in model configuration, and
|
|
29
|
+
// Connectors authorizes Connections for every Bot the User owns.
|
|
30
30
|
surfaces.register({
|
|
31
31
|
id: "plugins",
|
|
32
32
|
title: "Plugins",
|
|
@@ -39,7 +39,7 @@ export const settingsClientPlugin: ClientPlugin = (ctx) => {
|
|
|
39
39
|
}),
|
|
40
40
|
surfaces.register({
|
|
41
41
|
id: "connections",
|
|
42
|
-
title: "
|
|
42
|
+
title: "Connectors",
|
|
43
43
|
component: ConnectionsSurface,
|
|
44
44
|
}),
|
|
45
45
|
surfaces.register({
|
|
@@ -55,7 +55,7 @@ export const settingsClientPlugin: ClientPlugin = (ctx) => {
|
|
|
55
55
|
ctx.slot({
|
|
56
56
|
slot: "frockbot.sidebar-actions",
|
|
57
57
|
order: 10,
|
|
58
|
-
component:
|
|
58
|
+
component: ConnectorsTrigger,
|
|
59
59
|
}),
|
|
60
60
|
ctx.slot({
|
|
61
61
|
slot: "frockbot.user-profile",
|
|
@@ -75,4 +75,14 @@ export const settingsClientPlugin: ClientPlugin = (ctx) => {
|
|
|
75
75
|
];
|
|
76
76
|
};
|
|
77
77
|
|
|
78
|
+
export {
|
|
79
|
+
decodeModelSelection,
|
|
80
|
+
describeModelBinding,
|
|
81
|
+
eligibleModelConnections,
|
|
82
|
+
encodeModelSelection,
|
|
83
|
+
isModelConnectionEligible,
|
|
84
|
+
modelSelectOptions,
|
|
85
|
+
type ModelSelectOption,
|
|
86
|
+
} from "./bot-settings.js";
|
|
87
|
+
|
|
78
88
|
export default settingsClientPlugin;
|
|
@@ -72,12 +72,33 @@ const settingsOnly = catalogItem({
|
|
|
72
72
|
] as PluginCatalogItem["settings"],
|
|
73
73
|
});
|
|
74
74
|
|
|
75
|
+
const customModels = catalogItem({
|
|
76
|
+
packageId: "custom-models",
|
|
77
|
+
settings: [
|
|
78
|
+
{
|
|
79
|
+
id: "model",
|
|
80
|
+
schemaVersion: 1,
|
|
81
|
+
scopes: ["user", "bot"],
|
|
82
|
+
role: "model",
|
|
83
|
+
schema: {
|
|
84
|
+
type: "object",
|
|
85
|
+
properties: {
|
|
86
|
+
connectionId: { type: "string" },
|
|
87
|
+
providerModelId: { type: "string" },
|
|
88
|
+
},
|
|
89
|
+
required: ["connectionId", "providerModelId"],
|
|
90
|
+
additionalProperties: false,
|
|
91
|
+
},
|
|
92
|
+
},
|
|
93
|
+
],
|
|
94
|
+
});
|
|
95
|
+
|
|
75
96
|
const plain = catalogItem({
|
|
76
97
|
packageId: "web",
|
|
77
98
|
capabilities: [{ id: "web-tools", kind: "tool", connectionTypes: [] }],
|
|
78
99
|
});
|
|
79
100
|
|
|
80
|
-
const catalog = [provider, connector, settingsOnly, plain];
|
|
101
|
+
const catalog = [provider, connector, settingsOnly, customModels, plain];
|
|
81
102
|
|
|
82
103
|
function installation(
|
|
83
104
|
packageId: string,
|
|
@@ -91,6 +112,7 @@ describe("Package configuration homes", () => {
|
|
|
91
112
|
expect(packageConfigurationHome(provider)).toBe("models");
|
|
92
113
|
expect(packageConfigurationHome(connector)).toBe("connections");
|
|
93
114
|
expect(packageConfigurationHome(settingsOnly)).toBe("user-settings");
|
|
115
|
+
expect(packageConfigurationHome(customModels)).toBe("models");
|
|
94
116
|
// Nothing declared is nothing to configure: the Package appears in Plugins
|
|
95
117
|
// to be enabled and disabled, and on no configuration surface at all.
|
|
96
118
|
expect(packageConfigurationHome(plain)).toBe("none");
|
|
@@ -103,11 +125,15 @@ describe("Package configuration homes", () => {
|
|
|
103
125
|
["provider-ollama-cloud", "models"],
|
|
104
126
|
["composio", "connections"],
|
|
105
127
|
["image", "user-settings"],
|
|
128
|
+
["custom-models", "models"],
|
|
106
129
|
]);
|
|
107
130
|
});
|
|
108
131
|
|
|
109
132
|
test("a model provider that declares Connections is Models', not Connections'", () => {
|
|
110
|
-
expect(packagesForHome(catalog, "models")).toEqual([
|
|
133
|
+
expect(packagesForHome(catalog, "models")).toEqual([
|
|
134
|
+
provider,
|
|
135
|
+
customModels,
|
|
136
|
+
]);
|
|
111
137
|
expect(packagesForHome(catalog, "connections")).toEqual([connector]);
|
|
112
138
|
expect(packagesForHome(catalog, "user-settings")).toEqual([settingsOnly]);
|
|
113
139
|
});
|
|
@@ -138,7 +164,7 @@ describe("Package configuration homes", () => {
|
|
|
138
164
|
|
|
139
165
|
test("names the surface a Plugins row points at", () => {
|
|
140
166
|
expect(configurationHomeLabel("models")).toBe("Models");
|
|
141
|
-
expect(configurationHomeLabel("connections")).toBe("
|
|
167
|
+
expect(configurationHomeLabel("connections")).toBe("Connectors");
|
|
142
168
|
expect(configurationHomeLabel("user-settings")).toBe(
|
|
143
169
|
"Application settings",
|
|
144
170
|
);
|
|
@@ -26,9 +26,18 @@ export function declaresConnections(item: PluginCatalogItem): boolean {
|
|
|
26
26
|
export function packageConfigurationHome(
|
|
27
27
|
item: PluginCatalogItem,
|
|
28
28
|
): PackageConfigurationHome {
|
|
29
|
-
if (
|
|
29
|
+
if (
|
|
30
|
+
isModelProviderPackage(item) ||
|
|
31
|
+
item.settings?.some((setting) => setting.role === "model")
|
|
32
|
+
)
|
|
33
|
+
return "models";
|
|
30
34
|
if (declaresConnections(item)) return "connections";
|
|
31
|
-
if (
|
|
35
|
+
if (
|
|
36
|
+
item.settings?.some(
|
|
37
|
+
(setting) => setting.role !== "model" && setting.scopes.includes("user"),
|
|
38
|
+
)
|
|
39
|
+
)
|
|
40
|
+
return "user-settings";
|
|
32
41
|
return "none";
|
|
33
42
|
}
|
|
34
43
|
|
|
@@ -87,7 +96,7 @@ export function configurationHomeLabel(
|
|
|
87
96
|
home: PackageConfigurationHome,
|
|
88
97
|
): string | undefined {
|
|
89
98
|
if (home === "models") return "Models";
|
|
90
|
-
if (home === "connections") return "
|
|
99
|
+
if (home === "connections") return "Connectors";
|
|
91
100
|
if (home === "user-settings") return "Application settings";
|
|
92
101
|
return undefined;
|
|
93
102
|
}
|