@frockbot/plugin-custom-models 0.0.0 → 0.3.13
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 +60 -0
- package/package.json +38 -6
- package/src/client/AccountModelsSection.vue +165 -0
- package/src/client/BotModelSection.vue +139 -0
- package/src/client/ProviderAccounts.vue +615 -0
- package/src/client/ProviderPackageSettings.vue +188 -0
- package/src/client/index.test.ts +182 -0
- package/src/client/index.ts +47 -0
- package/src/client/state.ts +94 -0
- package/src/env.d.ts +6 -0
- package/src/index.ts +5 -0
- package/src/manifest.test.ts +57 -0
- package/src/manifest.ts +3 -0
- package/src/model-resolution.test.ts +151 -0
- package/src/model-settings.ts +19 -0
- package/tsconfig.json +15 -0
- package/vite.config.ts +32 -0
- package/README.md +0 -3
package/frockbot.json
ADDED
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
{
|
|
2
|
+
"schemaVersion": 4,
|
|
3
|
+
"id": "custom-models",
|
|
4
|
+
"displayName": "Custom models",
|
|
5
|
+
"version": "0.0.1",
|
|
6
|
+
"compatibility": { "frockbot": ">=0.0.1" },
|
|
7
|
+
"dependencies": {
|
|
8
|
+
"settings": ">=0.0.1",
|
|
9
|
+
"shell": ">=0.0.1",
|
|
10
|
+
"ui-theme": ">=0.0.1"
|
|
11
|
+
},
|
|
12
|
+
"defaultEnablement": "disabled",
|
|
13
|
+
"contributions": {
|
|
14
|
+
"client": {
|
|
15
|
+
"entry": "./client",
|
|
16
|
+
"mounts": [
|
|
17
|
+
{ "slot": "frockbot.models-sections", "order": 0 },
|
|
18
|
+
{ "slot": "frockbot.bot-settings-sections", "order": 0 }
|
|
19
|
+
],
|
|
20
|
+
"outlets": []
|
|
21
|
+
}
|
|
22
|
+
},
|
|
23
|
+
"configuration": {
|
|
24
|
+
"settings": [
|
|
25
|
+
{
|
|
26
|
+
"id": "account-model",
|
|
27
|
+
"schemaVersion": 1,
|
|
28
|
+
"scopes": ["user"],
|
|
29
|
+
"role": "model",
|
|
30
|
+
"schema": {
|
|
31
|
+
"type": "object",
|
|
32
|
+
"properties": {
|
|
33
|
+
"connectionId": { "type": "string" },
|
|
34
|
+
"providerModelId": { "type": "string" }
|
|
35
|
+
},
|
|
36
|
+
"required": ["connectionId", "providerModelId"],
|
|
37
|
+
"additionalProperties": false
|
|
38
|
+
}
|
|
39
|
+
},
|
|
40
|
+
{
|
|
41
|
+
"id": "model",
|
|
42
|
+
"schemaVersion": 1,
|
|
43
|
+
"scopes": ["bot"],
|
|
44
|
+
"role": "model",
|
|
45
|
+
"schema": {
|
|
46
|
+
"type": "object",
|
|
47
|
+
"properties": {
|
|
48
|
+
"connectionId": { "type": "string" },
|
|
49
|
+
"providerModelId": { "type": "string" }
|
|
50
|
+
},
|
|
51
|
+
"required": ["connectionId", "providerModelId"],
|
|
52
|
+
"additionalProperties": false
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
],
|
|
56
|
+
"connectionTypes": [],
|
|
57
|
+
"capabilities": []
|
|
58
|
+
},
|
|
59
|
+
"permissions": []
|
|
60
|
+
}
|
package/package.json
CHANGED
|
@@ -1,14 +1,46 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@frockbot/plugin-custom-models",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"
|
|
5
|
-
"
|
|
3
|
+
"version": "0.3.13",
|
|
4
|
+
"private": false,
|
|
5
|
+
"type": "module",
|
|
6
|
+
"exports": {
|
|
7
|
+
".": "./src/index.ts",
|
|
8
|
+
"./client": "./src/client/index.ts",
|
|
9
|
+
"./frockbot.json": "./frockbot.json",
|
|
10
|
+
"./manifest": "./src/manifest.ts",
|
|
11
|
+
"./package.json": "./package.json"
|
|
12
|
+
},
|
|
13
|
+
"frockbot": {
|
|
14
|
+
"manifest": "./frockbot.json"
|
|
15
|
+
},
|
|
16
|
+
"scripts": {
|
|
17
|
+
"test": "bun test src",
|
|
18
|
+
"build": "vite build",
|
|
19
|
+
"typecheck": "vue-tsc --noEmit -p tsconfig.json"
|
|
20
|
+
},
|
|
21
|
+
"dependencies": {
|
|
22
|
+
"@frockbot/client-core": "0.3.13",
|
|
23
|
+
"@frockbot/client-ui": "0.3.13",
|
|
24
|
+
"@frockbot/configuration-core": "0.3.13",
|
|
25
|
+
"@frockbot/kernel-composition": "0.3.13",
|
|
26
|
+
"@frockbot/kernel-contracts": "0.3.13",
|
|
27
|
+
"@frockbot/plugin-settings": "0.3.13",
|
|
28
|
+
"@frockbot/plugin-shell": "0.3.13",
|
|
29
|
+
"vue": "3.5.41"
|
|
30
|
+
},
|
|
31
|
+
"devDependencies": {
|
|
32
|
+
"@types/bun": "1.4.0",
|
|
33
|
+
"@vitejs/plugin-vue": "6.0.8",
|
|
34
|
+
"typescript": "npm:typescript-native-bridge@6.0.3-bridge.16.tsgo.7.0.2",
|
|
35
|
+
"vite": "8.2.2",
|
|
36
|
+
"vue-tsc": "3.3.10"
|
|
37
|
+
},
|
|
38
|
+
"publishConfig": {
|
|
39
|
+
"access": "public"
|
|
40
|
+
},
|
|
6
41
|
"repository": {
|
|
7
42
|
"type": "git",
|
|
8
43
|
"url": "git+https://github.com/timoconnellaus/frockbot.git",
|
|
9
44
|
"directory": "packages/plugin-custom-models"
|
|
10
|
-
},
|
|
11
|
-
"publishConfig": {
|
|
12
|
-
"access": "public"
|
|
13
45
|
}
|
|
14
46
|
}
|
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import { UiButton, UiField } from "@frockbot/client-ui";
|
|
3
|
+
import {
|
|
4
|
+
decodeModelSelection,
|
|
5
|
+
describeModelBinding,
|
|
6
|
+
eligibleModelConnections,
|
|
7
|
+
encodeModelSelection,
|
|
8
|
+
modelSelectOptions,
|
|
9
|
+
} from "@frockbot/plugin-settings/client";
|
|
10
|
+
import { frockBotWebDataKey } from "@frockbot/plugin-shell/shared";
|
|
11
|
+
import { computed, inject, onMounted, ref, watch } from "vue";
|
|
12
|
+
import {
|
|
13
|
+
ACCOUNT_MODEL_SETTING_ID_V1,
|
|
14
|
+
storedModelBindingV1,
|
|
15
|
+
} from "../model-settings.js";
|
|
16
|
+
import ProviderAccounts from "./ProviderAccounts.vue";
|
|
17
|
+
import { customModelsClientStateKey } from "./state.js";
|
|
18
|
+
|
|
19
|
+
const providedState = inject(customModelsClientStateKey);
|
|
20
|
+
const providedWeb = inject(frockBotWebDataKey);
|
|
21
|
+
if (!providedState || !providedWeb) {
|
|
22
|
+
throw new Error("Custom models client services were not provided");
|
|
23
|
+
}
|
|
24
|
+
const state = providedState;
|
|
25
|
+
const web = providedWeb;
|
|
26
|
+
|
|
27
|
+
const installation = computed(() =>
|
|
28
|
+
web.value.userSettings?.packages.find(
|
|
29
|
+
(candidate) => candidate.packageId === "custom-models",
|
|
30
|
+
),
|
|
31
|
+
);
|
|
32
|
+
const enabled = computed(() => installation.value?.state === "installed");
|
|
33
|
+
const connections = computed(() => web.value.userSettings?.connections ?? []);
|
|
34
|
+
const readyConnections = computed(() =>
|
|
35
|
+
eligibleModelConnections({
|
|
36
|
+
connections: connections.value,
|
|
37
|
+
packages: web.value.userSettings?.packages ?? [],
|
|
38
|
+
catalog: web.value.pluginCatalog,
|
|
39
|
+
}),
|
|
40
|
+
);
|
|
41
|
+
const options = computed(() => modelSelectOptions(readyConnections.value));
|
|
42
|
+
const storedModel = computed(() =>
|
|
43
|
+
storedModelBindingV1(
|
|
44
|
+
installation.value?.values?.[ACCOUNT_MODEL_SETTING_ID_V1],
|
|
45
|
+
),
|
|
46
|
+
);
|
|
47
|
+
const platformModelLabel = computed(
|
|
48
|
+
() =>
|
|
49
|
+
describeModelBinding(
|
|
50
|
+
web.value.userSettings?.platformModel,
|
|
51
|
+
connections.value,
|
|
52
|
+
) ?? "not available",
|
|
53
|
+
);
|
|
54
|
+
const selectedModel = ref("");
|
|
55
|
+
const saving = ref(false);
|
|
56
|
+
|
|
57
|
+
watch(
|
|
58
|
+
() => encodeModelSelection(storedModel.value),
|
|
59
|
+
(selection) => {
|
|
60
|
+
selectedModel.value = selection;
|
|
61
|
+
},
|
|
62
|
+
{ immediate: true },
|
|
63
|
+
);
|
|
64
|
+
|
|
65
|
+
onMounted(() => {
|
|
66
|
+
void web.value.loadPluginCatalog();
|
|
67
|
+
void web.value.loadUserSettings();
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
async function save(): Promise<void> {
|
|
71
|
+
saving.value = true;
|
|
72
|
+
try {
|
|
73
|
+
await state.setAccountModel(decodeModelSelection(selectedModel.value));
|
|
74
|
+
web.value.settingsError = undefined;
|
|
75
|
+
} catch (error) {
|
|
76
|
+
web.value.settingsError =
|
|
77
|
+
error instanceof Error ? error.message : "Could not save account model";
|
|
78
|
+
} finally {
|
|
79
|
+
saving.value = false;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
</script>
|
|
83
|
+
|
|
84
|
+
<template>
|
|
85
|
+
<div v-if="enabled" class="custom-models">
|
|
86
|
+
<section
|
|
87
|
+
id="user-default-model-content"
|
|
88
|
+
class="model-section"
|
|
89
|
+
aria-labelledby="user-default-model"
|
|
90
|
+
>
|
|
91
|
+
<UiField label="Account model" hint="used by every Bot">
|
|
92
|
+
<select v-model="selectedModel">
|
|
93
|
+
<option value="">
|
|
94
|
+
Follow the platform model — {{ platformModelLabel }}
|
|
95
|
+
</option>
|
|
96
|
+
<option
|
|
97
|
+
v-for="option in options"
|
|
98
|
+
:key="option.value"
|
|
99
|
+
:value="option.value"
|
|
100
|
+
>
|
|
101
|
+
{{ option.label }}
|
|
102
|
+
</option>
|
|
103
|
+
</select>
|
|
104
|
+
</UiField>
|
|
105
|
+
<p class="field-hint">
|
|
106
|
+
With no account choice, every Bot follows the platform model:
|
|
107
|
+
{{ platformModelLabel }}.
|
|
108
|
+
</p>
|
|
109
|
+
<div class="section-actions">
|
|
110
|
+
<UiButton variant="primary" :disabled="saving" @click="save">
|
|
111
|
+
{{ saving ? "Saving…" : "Save account model" }}
|
|
112
|
+
</UiButton>
|
|
113
|
+
</div>
|
|
114
|
+
</section>
|
|
115
|
+
|
|
116
|
+
<section
|
|
117
|
+
id="user-model-providers-content"
|
|
118
|
+
class="model-section"
|
|
119
|
+
aria-labelledby="user-model-providers"
|
|
120
|
+
>
|
|
121
|
+
<div>
|
|
122
|
+
<strong>Model providers</strong>
|
|
123
|
+
<p class="field-hint">
|
|
124
|
+
Accounts and endpoints for the model providers you have enabled.
|
|
125
|
+
</p>
|
|
126
|
+
</div>
|
|
127
|
+
<ProviderAccounts />
|
|
128
|
+
</section>
|
|
129
|
+
</div>
|
|
130
|
+
</template>
|
|
131
|
+
|
|
132
|
+
<style scoped>
|
|
133
|
+
.custom-models,
|
|
134
|
+
.model-section {
|
|
135
|
+
display: flex;
|
|
136
|
+
flex-direction: column;
|
|
137
|
+
gap: 16px;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
.model-section {
|
|
141
|
+
padding-right: var(--frock-control-sm);
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
.model-section + .model-section {
|
|
145
|
+
padding-top: 16px;
|
|
146
|
+
border-top: 1px solid var(--frock-border);
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
.model-section strong {
|
|
150
|
+
color: var(--frock-text);
|
|
151
|
+
font-size: var(--frock-text-md);
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
.field-hint {
|
|
155
|
+
margin: 0;
|
|
156
|
+
color: var(--frock-text-muted);
|
|
157
|
+
font-size: var(--frock-text-sm);
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
.section-actions {
|
|
161
|
+
display: flex;
|
|
162
|
+
justify-content: flex-end;
|
|
163
|
+
gap: 8px;
|
|
164
|
+
}
|
|
165
|
+
</style>
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import { UiButton, UiField } from "@frockbot/client-ui";
|
|
3
|
+
import {
|
|
4
|
+
decodeModelSelection,
|
|
5
|
+
describeModelBinding,
|
|
6
|
+
eligibleModelConnections,
|
|
7
|
+
encodeModelSelection,
|
|
8
|
+
modelSelectOptions,
|
|
9
|
+
} from "@frockbot/plugin-settings/client";
|
|
10
|
+
import { frockBotWebDataKey } from "@frockbot/plugin-shell/shared";
|
|
11
|
+
import { computed, inject, onMounted, ref, watch } from "vue";
|
|
12
|
+
import {
|
|
13
|
+
ACCOUNT_MODEL_SETTING_ID_V1,
|
|
14
|
+
BOT_MODEL_SETTING_ID_V1,
|
|
15
|
+
storedModelBindingV1,
|
|
16
|
+
} from "../model-settings.js";
|
|
17
|
+
import { customModelsClientStateKey } from "./state.js";
|
|
18
|
+
|
|
19
|
+
const providedState = inject(customModelsClientStateKey);
|
|
20
|
+
const providedWeb = inject(frockBotWebDataKey);
|
|
21
|
+
if (!providedState || !providedWeb) {
|
|
22
|
+
throw new Error("Custom models client services were not provided");
|
|
23
|
+
}
|
|
24
|
+
const state = providedState;
|
|
25
|
+
const web = providedWeb;
|
|
26
|
+
|
|
27
|
+
const installation = computed(() =>
|
|
28
|
+
web.value.userSettings?.packages.find(
|
|
29
|
+
(candidate) => candidate.packageId === "custom-models",
|
|
30
|
+
),
|
|
31
|
+
);
|
|
32
|
+
const enabled = computed(() => installation.value?.state === "installed");
|
|
33
|
+
const connections = computed(() => web.value.userSettings?.connections ?? []);
|
|
34
|
+
const readyConnections = computed(() =>
|
|
35
|
+
eligibleModelConnections({
|
|
36
|
+
connections: connections.value,
|
|
37
|
+
packages: web.value.userSettings?.packages ?? [],
|
|
38
|
+
catalog: web.value.pluginCatalog,
|
|
39
|
+
}),
|
|
40
|
+
);
|
|
41
|
+
const options = computed(() => modelSelectOptions(readyConnections.value));
|
|
42
|
+
const accountModel = computed(() =>
|
|
43
|
+
storedModelBindingV1(
|
|
44
|
+
installation.value?.values?.[ACCOUNT_MODEL_SETTING_ID_V1],
|
|
45
|
+
),
|
|
46
|
+
);
|
|
47
|
+
const inheritedModel = computed(
|
|
48
|
+
() => accountModel.value ?? web.value.userSettings?.platformModel,
|
|
49
|
+
);
|
|
50
|
+
const inheritedModelLabel = computed(
|
|
51
|
+
() =>
|
|
52
|
+
describeModelBinding(inheritedModel.value, connections.value) ??
|
|
53
|
+
"not available",
|
|
54
|
+
);
|
|
55
|
+
const botModel = computed(() =>
|
|
56
|
+
storedModelBindingV1(
|
|
57
|
+
web.value.botSettings?.packageValues["custom-models"]?.[
|
|
58
|
+
BOT_MODEL_SETTING_ID_V1
|
|
59
|
+
],
|
|
60
|
+
),
|
|
61
|
+
);
|
|
62
|
+
const selectedModel = ref("");
|
|
63
|
+
const saving = ref(false);
|
|
64
|
+
|
|
65
|
+
watch(
|
|
66
|
+
() => encodeModelSelection(botModel.value),
|
|
67
|
+
(selection) => {
|
|
68
|
+
selectedModel.value = selection;
|
|
69
|
+
},
|
|
70
|
+
{ immediate: true },
|
|
71
|
+
);
|
|
72
|
+
|
|
73
|
+
onMounted(() => {
|
|
74
|
+
void web.value.loadPluginCatalog();
|
|
75
|
+
void web.value.loadUserSettings();
|
|
76
|
+
void web.value.loadBotSettings();
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
async function save(): Promise<void> {
|
|
80
|
+
saving.value = true;
|
|
81
|
+
try {
|
|
82
|
+
await state.setBotModel(decodeModelSelection(selectedModel.value));
|
|
83
|
+
web.value.settingsError = undefined;
|
|
84
|
+
} catch (error) {
|
|
85
|
+
web.value.settingsError =
|
|
86
|
+
error instanceof Error ? error.message : "Could not save Bot model";
|
|
87
|
+
} finally {
|
|
88
|
+
saving.value = false;
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
</script>
|
|
92
|
+
|
|
93
|
+
<template>
|
|
94
|
+
<section v-if="enabled" class="bot-model-section">
|
|
95
|
+
<UiField label="Model" hint="for this Bot">
|
|
96
|
+
<select v-model="selectedModel">
|
|
97
|
+
<option value="">
|
|
98
|
+
Follow the account model — {{ inheritedModelLabel }}
|
|
99
|
+
</option>
|
|
100
|
+
<option
|
|
101
|
+
v-for="option in options"
|
|
102
|
+
:key="option.value"
|
|
103
|
+
:value="option.value"
|
|
104
|
+
>
|
|
105
|
+
{{ option.label }}
|
|
106
|
+
</option>
|
|
107
|
+
</select>
|
|
108
|
+
</UiField>
|
|
109
|
+
<p class="field-hint">
|
|
110
|
+
With no override, this Bot inherits {{ inheritedModelLabel }}.
|
|
111
|
+
</p>
|
|
112
|
+
<div class="section-actions">
|
|
113
|
+
<UiButton variant="primary" :disabled="saving" @click="save">
|
|
114
|
+
{{ saving ? "Saving…" : "Save Bot model" }}
|
|
115
|
+
</UiButton>
|
|
116
|
+
</div>
|
|
117
|
+
</section>
|
|
118
|
+
</template>
|
|
119
|
+
|
|
120
|
+
<style scoped>
|
|
121
|
+
.bot-model-section {
|
|
122
|
+
display: flex;
|
|
123
|
+
flex-direction: column;
|
|
124
|
+
gap: 12px;
|
|
125
|
+
padding-right: var(--frock-control-sm);
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
.field-hint {
|
|
129
|
+
margin: 0;
|
|
130
|
+
color: var(--frock-text-muted);
|
|
131
|
+
font-size: var(--frock-text-sm);
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
.section-actions {
|
|
135
|
+
display: flex;
|
|
136
|
+
justify-content: flex-end;
|
|
137
|
+
gap: 8px;
|
|
138
|
+
}
|
|
139
|
+
</style>
|