@frockbot/plugin-custom-models 0.0.0 → 0.3.12

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 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.0.0",
4
- "description": "Placeholder reserving this name for trusted publishing. Superseded by the first release.",
5
- "license": "UNLICENSED",
3
+ "version": "0.3.12",
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.12",
23
+ "@frockbot/client-ui": "0.3.12",
24
+ "@frockbot/configuration-core": "0.3.12",
25
+ "@frockbot/kernel-composition": "0.3.12",
26
+ "@frockbot/kernel-contracts": "0.3.12",
27
+ "@frockbot/plugin-settings": "0.3.12",
28
+ "@frockbot/plugin-shell": "0.3.12",
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>