@adminforth/agent 1.7.0 → 1.8.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.
@@ -170,21 +170,9 @@ function createAgentLlmMetricsLogger() {
170
170
  return new AgentLlmMetricsLogger();
171
171
  }
172
172
 
173
- function normalizeReasoning(reasoning: AgentReasoning) {
174
- if (reasoning === "none") {
175
- return undefined;
176
- }
177
-
178
- return {
179
- effort: reasoning as "minimal" | "low" | "medium" | "high" | "xhigh",
180
- summary: "auto" as const,
181
- };
182
- }
183
-
184
173
  export function createAgentChatModel(params: {
185
174
  adapter: CompletionAdapter;
186
175
  maxTokens: number;
187
- reasoning: AgentReasoning;
188
176
  modelName?: string;
189
177
  }) {
190
178
  const adapter = params.adapter as OpenAIBackedCompletionAdapter;
@@ -198,7 +186,7 @@ export function createAgentChatModel(params: {
198
186
 
199
187
  const model = params.modelName ?? options.model ?? "gpt-5-nano";
200
188
  const baseURL = options.baseURL ?? options.baseUrl;
201
- const reasoning = normalizeReasoning(params.reasoning);
189
+ const reasoning = options.extraRequestBodyParameters?.reasoning;
202
190
 
203
191
  // @ts-ignore
204
192
  return new ChatOpenAI({
package/build.log CHANGED
@@ -29,5 +29,5 @@ custom/skills/fetch_data/SKILL.md
29
29
  custom/skills/mutate_data/
30
30
  custom/skills/mutate_data/SKILL.md
31
31
 
32
- sent 170,560 bytes received 413 bytes 341,946.00 bytes/sec
33
- total size is 168,886 speedup is 0.99
32
+ sent 174,726 bytes received 413 bytes 350,278.00 bytes/sec
33
+ total size is 173,048 speedup is 0.99
@@ -1,6 +1,6 @@
1
1
  <template>
2
2
  <div
3
- class="relative w-6 h-6 cursor-pointer mr-6 mt-1
3
+ class="relative w-6 h-6 cursor-pointer mr-1 mt-1
4
4
  text-lightNavbarIcons hover:text-lightNavbarIcons/80
5
5
  dark:text-darkNavbarIcons hover:text-darkNavbarIcons/80
6
6
  hover:scale-110 transition-colors duration-200"
@@ -78,13 +78,49 @@
78
78
  v-model="agentStore.userMessageInput"
79
79
  ref="textInput"
80
80
  @input="autoResize"
81
- class="min-h-12 p-4 pr-12 w-full resize-none overflow-hidden border text-lightInputText dark:text-darkInputText rounded-md bg-transparent text-sm bg-gray-50 dark:bg-gray-700 dark:border-gray-600 focus:outline-none"
81
+ :class="[
82
+ 'min-h-12 w-full resize-none overflow-hidden border text-lightInputText dark:text-darkInputText rounded-md bg-transparent text-sm bg-gray-50 dark:bg-gray-700 dark:border-gray-600 focus:outline-none',
83
+ agentStore.availableModes.length > 1 ? 'p-4 pr-12 pb-12' : 'p-4 pr-12',
84
+ ]"
82
85
  placeholder="Type a message..."
83
- @keydown.enter.exact.prevent="async () => {await agentStore.sendMessage(); autoResize();}"
86
+ @keydown.enter.exact.prevent="sendMessage"
84
87
  />
88
+ <div
89
+ v-if="agentStore.availableModes.length > 1"
90
+ ref="modeMenu"
91
+ class="absolute bottom-2 left-4"
92
+ >
93
+ <button
94
+ aria-label="Select mode"
95
+ class="flex h-8 w-8 items-center justify-center rounded-md border border-gray-200 bg-white text-lightNavbarIcons transition-colors duration-200 hover:bg-gray-100 dark:border-gray-600 dark:bg-gray-800 dark:text-darkNavbarIcons dark:hover:bg-gray-700"
96
+ :class="isModeMenuOpen ? 'bg-gray-100 dark:bg-gray-700' : ''"
97
+ :disabled="agentStore.isResponseInProgress"
98
+ title="Select mode"
99
+ type="button"
100
+ @click="toggleModeMenu"
101
+ >
102
+ <IconBrainOutline class="h-4 w-4" />
103
+ </button>
104
+
105
+ <div
106
+ v-if="isModeMenuOpen"
107
+ class="absolute bottom-full left-0 mb-2 min-w-40 overflow-hidden rounded-md border border-gray-200 bg-white shadow-lg dark:border-gray-600 dark:bg-gray-800"
108
+ >
109
+ <button
110
+ v-for="mode in agentStore.availableModes"
111
+ :key="mode.name"
112
+ class="block w-full px-3 py-2 text-left text-sm text-lightInputText transition-colors duration-150 hover:bg-gray-100 dark:text-darkInputText dark:hover:bg-gray-700"
113
+ :class="mode.name === agentStore.activeModeName ? 'bg-gray-100 dark:bg-gray-700' : ''"
114
+ type="button"
115
+ @click="selectMode(mode.name)"
116
+ >
117
+ {{ mode.name }}
118
+ </button>
119
+ </div>
120
+ </div>
85
121
  <Button
86
122
  class="absolute right-4 bottom-2 !p-0 h-[34px] w-[34px]"
87
- @click="async () => {await agentStore.sendMessage(); autoResize();}"
123
+ @click="sendMessage"
88
124
  :disabled="!agentStore.trimmedUserMessage || agentStore.isResponseInProgress"
89
125
  >
90
126
  <IconArrowUpOutline
@@ -101,8 +137,8 @@
101
137
 
102
138
  <script setup lang="ts">
103
139
  import { IconChatBubbleLeft20Solid, IconSparklesSolid } from '@iconify-prerendered/vue-heroicons';
104
- import { IconCloseOutline, IconBarsOutline, IconArrowUpOutline, IconCloseSidebarSolid, IconOpenSidebarSolid } from '@iconify-prerendered/vue-flowbite';
105
- import { useTemplateRef, onMounted, ref, computed } from 'vue';
140
+ import { IconCloseOutline, IconBarsOutline, IconArrowUpOutline, IconCloseSidebarSolid, IconOpenSidebarSolid, IconBrainOutline } from '@iconify-prerendered/vue-flowbite';
141
+ import { useTemplateRef, onMounted, ref } from 'vue';
106
142
  import { onClickOutside } from '@vueuse/core'
107
143
  import ConversationArea from './ConversationArea.vue';
108
144
  import { useAgentStore } from './useAgentStore';
@@ -112,13 +148,19 @@ import { useCoreStore } from '@/stores/core';
112
148
  const props = defineProps<{
113
149
  meta: {
114
150
  pluginInstanceId: string;
151
+ modes: Array<{
152
+ name: string;
153
+ }>;
154
+ defaultModeName: string | null;
115
155
  }
116
156
  }>();
117
157
 
118
158
  const chatSurface = useTemplateRef('chatSurface');
119
159
  const textInput = useTemplateRef('textInput');
160
+ const modeMenu = useTemplateRef('modeMenu');
120
161
  const agentStore = useAgentStore();
121
162
  const coreStore = useCoreStore();
163
+ const isModeMenuOpen = ref(false);
122
164
 
123
165
  const MAX_WIDTH = 800;
124
166
  const MIN_WIDTH = 382; //w-96
@@ -158,8 +200,10 @@ const stopResize = () => {
158
200
  }
159
201
 
160
202
  onClickOutside(chatSurface, () => {if (!agentStore.isTeleportedToBody) agentStore.setIsChatOpen(false);});
203
+ onClickOutside(modeMenu, () => { isModeMenuOpen.value = false; });
161
204
 
162
205
  onMounted(async () => {
206
+ agentStore.setAvailableModes(props.meta.modes, props.meta.defaultModeName);
163
207
  agentStore.regisrerTextInput(textInput.value);
164
208
  textInput.value?.focus();
165
209
  await agentStore.fetchSessionsList();
@@ -181,4 +225,19 @@ function autoResize() {
181
225
  }
182
226
  }
183
227
 
184
- </script>
228
+ function toggleModeMenu() {
229
+ isModeMenuOpen.value = !isModeMenuOpen.value;
230
+ }
231
+
232
+ function selectMode(modeName: string) {
233
+ agentStore.setActiveMode(modeName);
234
+ isModeMenuOpen.value = false;
235
+ }
236
+
237
+ async function sendMessage() {
238
+ isModeMenuOpen.value = false;
239
+ await agentStore.sendMessage();
240
+ autoResize();
241
+ }
242
+
243
+ </script>
@@ -207,4 +207,44 @@
207
207
  max-height: 144px;
208
208
  }
209
209
 
210
+ </style>
211
+
212
+ <style lang="scss">
213
+ .incremark a.incremark-link,
214
+ .incremark a.incremark-link:visited {
215
+ display: inline-block;
216
+ text-decoration: underline;
217
+ text-underline-offset: 4px;
218
+ text-decoration-style:dotted;
219
+ color: rgb(0, 0, 0);
220
+ transition: color 0.2s ease;
221
+ }
222
+
223
+ .incremark a.incremark-link:hover {
224
+ color: rgb(74, 74, 255);
225
+ text-decoration: underline;
226
+ text-underline-offset: 4px;
227
+ text-decoration-style:dotted;
228
+ }
229
+
230
+ html[data-theme="dark"] .incremark a.incremark-link,
231
+ html[data-theme="dark"] .incremark a.incremark-link:visited {
232
+ color: rgb(220, 220, 220);
233
+ }
234
+
235
+ html[data-theme="dark"] .incremark a.incremark-link:hover {
236
+ color: rgb(147, 147, 255);
237
+ }
238
+
239
+ a.incremark-link::after {
240
+ content: "";
241
+ display: inline-block;
242
+ width: 16px;
243
+ height: 16px;
244
+ vertical-align: middle;
245
+ rotate: -45deg;
246
+ background-color: currentColor;
247
+ mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24'%3E%3Cpath fill='none' stroke='currentColor' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='M13 12H2m12 0l-4 4m4-4l-4-4'/%3E%3C/svg%3E") no-repeat center;
248
+ mask-size: contain;
249
+ }
210
250
  </style>
@@ -255,6 +255,7 @@ function clearVega() {
255
255
  background:
256
256
  linear-gradient(180deg, rgba(255, 255, 255, 0.94), rgba(248, 250, 252, 0.96));
257
257
  box-shadow: 0 14px 30px -24px rgba(15, 23, 42, 0.45);
258
+ margin: 16px 0 ;
258
259
  }
259
260
 
260
261
  .dark .incremark-shiki-code {
@@ -7,6 +7,10 @@ import { Chat } from './chat';
7
7
  import { DefaultChatTransport } from 'ai';
8
8
  import { useCoreStore } from '@/stores/core';
9
9
 
10
+ type AgentMode = {
11
+ name: string;
12
+ };
13
+
10
14
  export const useAgentStore = defineStore('agent', () => {
11
15
  const activeSessionId = ref<string | null>(null);
12
16
  const currentSession = ref<IAgentSession | null>(null);
@@ -28,6 +32,8 @@ export const useAgentStore = defineStore('agent', () => {
28
32
  const header = ref<HTMLElement | null>(null);
29
33
  const lastSessionId = ref<string | null>(null);
30
34
  const chatWidth = ref(600);
35
+ const availableModes = ref<AgentMode[]>([]);
36
+ const activeModeName = ref<string | null>(null);
31
37
  function setLocalStorageItem(key: string, value: string) {
32
38
  window.localStorage.setItem(`${coreStore.config.brandName || 'adminforth'}-${key}`, value);
33
39
  }
@@ -91,6 +97,24 @@ export const useAgentStore = defineStore('agent', () => {
91
97
  })
92
98
  const chats = new Map<string, Chat<any>>();
93
99
  const currentChat = shallowRef<Chat<any>>();
100
+
101
+ function setAvailableModes(modes: AgentMode[], defaultModeName?: string | null) {
102
+ availableModes.value = modes;
103
+ activeModeName.value =
104
+ modes.find((mode) => mode.name === activeModeName.value)?.name
105
+ ?? defaultModeName
106
+ ?? modes[0]?.name
107
+ ?? null;
108
+ }
109
+
110
+ function setActiveMode(modeName: string) {
111
+ if (!availableModes.value.some((mode) => mode.name === modeName)) {
112
+ return;
113
+ }
114
+
115
+ activeModeName.value = modeName;
116
+ }
117
+
94
118
  function setCurrentChat(sessionId: string) {
95
119
  if (chats.has(sessionId)) {
96
120
  currentChat.value = chats.get(sessionId) || null;
@@ -105,6 +129,7 @@ export const useAgentStore = defineStore('agent', () => {
105
129
  message,
106
130
  sessionId,
107
131
  timeZone: Intl.DateTimeFormat().resolvedOptions().timeZone,
132
+ mode: activeModeName.value,
108
133
  };
109
134
 
110
135
  return {
@@ -368,6 +393,10 @@ export const useAgentStore = defineStore('agent', () => {
368
393
  setIsTeleportedToBody,
369
394
  chatWidth,
370
395
  setChatWidth,
371
- focusTextInput
396
+ focusTextInput,
397
+ availableModes,
398
+ activeModeName,
399
+ setAvailableModes,
400
+ setActiveMode,
372
401
  }
373
- })
402
+ })
@@ -103,17 +103,8 @@ class AgentLlmMetricsLogger extends BaseCallbackHandler {
103
103
  function createAgentLlmMetricsLogger() {
104
104
  return new AgentLlmMetricsLogger();
105
105
  }
106
- function normalizeReasoning(reasoning) {
107
- if (reasoning === "none") {
108
- return undefined;
109
- }
110
- return {
111
- effort: reasoning,
112
- summary: "auto",
113
- };
114
- }
115
106
  export function createAgentChatModel(params) {
116
- var _a, _b, _c, _d;
107
+ var _a, _b, _c, _d, _e;
117
108
  const adapter = params.adapter;
118
109
  const options = (_a = adapter.options) !== null && _a !== void 0 ? _a : {};
119
110
  if (!options.openAiApiKey) {
@@ -121,7 +112,7 @@ export function createAgentChatModel(params) {
121
112
  }
122
113
  const model = (_c = (_b = params.modelName) !== null && _b !== void 0 ? _b : options.model) !== null && _c !== void 0 ? _c : "gpt-5-nano";
123
114
  const baseURL = (_d = options.baseURL) !== null && _d !== void 0 ? _d : options.baseUrl;
124
- const reasoning = normalizeReasoning(params.reasoning);
115
+ const reasoning = (_e = options.extraRequestBodyParameters) === null || _e === void 0 ? void 0 : _e.reasoning;
125
116
  // @ts-ignore
126
117
  return new ChatOpenAI(Object.assign(Object.assign(Object.assign({ apiKey: options.openAiApiKey, model, maxTokens: params.maxTokens, useResponsesApi: true, outputVersion: "v1", promptCacheKey: `adminforth-agent:${model}:system-v1:tools-v1`, promptCacheRetention: "in_memory" }, (reasoning ? { reasoning } : {})), (typeof options.timeoutMs === "number"
127
118
  ? { timeout: options.timeoutMs }
@@ -1,6 +1,6 @@
1
1
  <template>
2
2
  <div
3
- class="relative w-6 h-6 cursor-pointer mr-6 mt-1
3
+ class="relative w-6 h-6 cursor-pointer mr-1 mt-1
4
4
  text-lightNavbarIcons hover:text-lightNavbarIcons/80
5
5
  dark:text-darkNavbarIcons hover:text-darkNavbarIcons/80
6
6
  hover:scale-110 transition-colors duration-200"
@@ -78,13 +78,49 @@
78
78
  v-model="agentStore.userMessageInput"
79
79
  ref="textInput"
80
80
  @input="autoResize"
81
- class="min-h-12 p-4 pr-12 w-full resize-none overflow-hidden border text-lightInputText dark:text-darkInputText rounded-md bg-transparent text-sm bg-gray-50 dark:bg-gray-700 dark:border-gray-600 focus:outline-none"
81
+ :class="[
82
+ 'min-h-12 w-full resize-none overflow-hidden border text-lightInputText dark:text-darkInputText rounded-md bg-transparent text-sm bg-gray-50 dark:bg-gray-700 dark:border-gray-600 focus:outline-none',
83
+ agentStore.availableModes.length > 1 ? 'p-4 pr-12 pb-12' : 'p-4 pr-12',
84
+ ]"
82
85
  placeholder="Type a message..."
83
- @keydown.enter.exact.prevent="async () => {await agentStore.sendMessage(); autoResize();}"
86
+ @keydown.enter.exact.prevent="sendMessage"
84
87
  />
88
+ <div
89
+ v-if="agentStore.availableModes.length > 1"
90
+ ref="modeMenu"
91
+ class="absolute bottom-2 left-4"
92
+ >
93
+ <button
94
+ aria-label="Select mode"
95
+ class="flex h-8 w-8 items-center justify-center rounded-md border border-gray-200 bg-white text-lightNavbarIcons transition-colors duration-200 hover:bg-gray-100 dark:border-gray-600 dark:bg-gray-800 dark:text-darkNavbarIcons dark:hover:bg-gray-700"
96
+ :class="isModeMenuOpen ? 'bg-gray-100 dark:bg-gray-700' : ''"
97
+ :disabled="agentStore.isResponseInProgress"
98
+ title="Select mode"
99
+ type="button"
100
+ @click="toggleModeMenu"
101
+ >
102
+ <IconBrainOutline class="h-4 w-4" />
103
+ </button>
104
+
105
+ <div
106
+ v-if="isModeMenuOpen"
107
+ class="absolute bottom-full left-0 mb-2 min-w-40 overflow-hidden rounded-md border border-gray-200 bg-white shadow-lg dark:border-gray-600 dark:bg-gray-800"
108
+ >
109
+ <button
110
+ v-for="mode in agentStore.availableModes"
111
+ :key="mode.name"
112
+ class="block w-full px-3 py-2 text-left text-sm text-lightInputText transition-colors duration-150 hover:bg-gray-100 dark:text-darkInputText dark:hover:bg-gray-700"
113
+ :class="mode.name === agentStore.activeModeName ? 'bg-gray-100 dark:bg-gray-700' : ''"
114
+ type="button"
115
+ @click="selectMode(mode.name)"
116
+ >
117
+ {{ mode.name }}
118
+ </button>
119
+ </div>
120
+ </div>
85
121
  <Button
86
122
  class="absolute right-4 bottom-2 !p-0 h-[34px] w-[34px]"
87
- @click="async () => {await agentStore.sendMessage(); autoResize();}"
123
+ @click="sendMessage"
88
124
  :disabled="!agentStore.trimmedUserMessage || agentStore.isResponseInProgress"
89
125
  >
90
126
  <IconArrowUpOutline
@@ -101,8 +137,8 @@
101
137
 
102
138
  <script setup lang="ts">
103
139
  import { IconChatBubbleLeft20Solid, IconSparklesSolid } from '@iconify-prerendered/vue-heroicons';
104
- import { IconCloseOutline, IconBarsOutline, IconArrowUpOutline, IconCloseSidebarSolid, IconOpenSidebarSolid } from '@iconify-prerendered/vue-flowbite';
105
- import { useTemplateRef, onMounted, ref, computed } from 'vue';
140
+ import { IconCloseOutline, IconBarsOutline, IconArrowUpOutline, IconCloseSidebarSolid, IconOpenSidebarSolid, IconBrainOutline } from '@iconify-prerendered/vue-flowbite';
141
+ import { useTemplateRef, onMounted, ref } from 'vue';
106
142
  import { onClickOutside } from '@vueuse/core'
107
143
  import ConversationArea from './ConversationArea.vue';
108
144
  import { useAgentStore } from './useAgentStore';
@@ -112,13 +148,19 @@ import { useCoreStore } from '@/stores/core';
112
148
  const props = defineProps<{
113
149
  meta: {
114
150
  pluginInstanceId: string;
151
+ modes: Array<{
152
+ name: string;
153
+ }>;
154
+ defaultModeName: string | null;
115
155
  }
116
156
  }>();
117
157
 
118
158
  const chatSurface = useTemplateRef('chatSurface');
119
159
  const textInput = useTemplateRef('textInput');
160
+ const modeMenu = useTemplateRef('modeMenu');
120
161
  const agentStore = useAgentStore();
121
162
  const coreStore = useCoreStore();
163
+ const isModeMenuOpen = ref(false);
122
164
 
123
165
  const MAX_WIDTH = 800;
124
166
  const MIN_WIDTH = 382; //w-96
@@ -158,8 +200,10 @@ const stopResize = () => {
158
200
  }
159
201
 
160
202
  onClickOutside(chatSurface, () => {if (!agentStore.isTeleportedToBody) agentStore.setIsChatOpen(false);});
203
+ onClickOutside(modeMenu, () => { isModeMenuOpen.value = false; });
161
204
 
162
205
  onMounted(async () => {
206
+ agentStore.setAvailableModes(props.meta.modes, props.meta.defaultModeName);
163
207
  agentStore.regisrerTextInput(textInput.value);
164
208
  textInput.value?.focus();
165
209
  await agentStore.fetchSessionsList();
@@ -181,4 +225,19 @@ function autoResize() {
181
225
  }
182
226
  }
183
227
 
184
- </script>
228
+ function toggleModeMenu() {
229
+ isModeMenuOpen.value = !isModeMenuOpen.value;
230
+ }
231
+
232
+ function selectMode(modeName: string) {
233
+ agentStore.setActiveMode(modeName);
234
+ isModeMenuOpen.value = false;
235
+ }
236
+
237
+ async function sendMessage() {
238
+ isModeMenuOpen.value = false;
239
+ await agentStore.sendMessage();
240
+ autoResize();
241
+ }
242
+
243
+ </script>
@@ -207,4 +207,44 @@
207
207
  max-height: 144px;
208
208
  }
209
209
 
210
+ </style>
211
+
212
+ <style lang="scss">
213
+ .incremark a.incremark-link,
214
+ .incremark a.incremark-link:visited {
215
+ display: inline-block;
216
+ text-decoration: underline;
217
+ text-underline-offset: 4px;
218
+ text-decoration-style:dotted;
219
+ color: rgb(0, 0, 0);
220
+ transition: color 0.2s ease;
221
+ }
222
+
223
+ .incremark a.incremark-link:hover {
224
+ color: rgb(74, 74, 255);
225
+ text-decoration: underline;
226
+ text-underline-offset: 4px;
227
+ text-decoration-style:dotted;
228
+ }
229
+
230
+ html[data-theme="dark"] .incremark a.incremark-link,
231
+ html[data-theme="dark"] .incremark a.incremark-link:visited {
232
+ color: rgb(220, 220, 220);
233
+ }
234
+
235
+ html[data-theme="dark"] .incremark a.incremark-link:hover {
236
+ color: rgb(147, 147, 255);
237
+ }
238
+
239
+ a.incremark-link::after {
240
+ content: "";
241
+ display: inline-block;
242
+ width: 16px;
243
+ height: 16px;
244
+ vertical-align: middle;
245
+ rotate: -45deg;
246
+ background-color: currentColor;
247
+ mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24'%3E%3Cpath fill='none' stroke='currentColor' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='M13 12H2m12 0l-4 4m4-4l-4-4'/%3E%3C/svg%3E") no-repeat center;
248
+ mask-size: contain;
249
+ }
210
250
  </style>
@@ -255,6 +255,7 @@ function clearVega() {
255
255
  background:
256
256
  linear-gradient(180deg, rgba(255, 255, 255, 0.94), rgba(248, 250, 252, 0.96));
257
257
  box-shadow: 0 14px 30px -24px rgba(15, 23, 42, 0.45);
258
+ margin: 16px 0 ;
258
259
  }
259
260
 
260
261
  .dark .incremark-shiki-code {
@@ -7,6 +7,10 @@ import { Chat } from './chat';
7
7
  import { DefaultChatTransport } from 'ai';
8
8
  import { useCoreStore } from '@/stores/core';
9
9
 
10
+ type AgentMode = {
11
+ name: string;
12
+ };
13
+
10
14
  export const useAgentStore = defineStore('agent', () => {
11
15
  const activeSessionId = ref<string | null>(null);
12
16
  const currentSession = ref<IAgentSession | null>(null);
@@ -28,6 +32,8 @@ export const useAgentStore = defineStore('agent', () => {
28
32
  const header = ref<HTMLElement | null>(null);
29
33
  const lastSessionId = ref<string | null>(null);
30
34
  const chatWidth = ref(600);
35
+ const availableModes = ref<AgentMode[]>([]);
36
+ const activeModeName = ref<string | null>(null);
31
37
  function setLocalStorageItem(key: string, value: string) {
32
38
  window.localStorage.setItem(`${coreStore.config.brandName || 'adminforth'}-${key}`, value);
33
39
  }
@@ -91,6 +97,24 @@ export const useAgentStore = defineStore('agent', () => {
91
97
  })
92
98
  const chats = new Map<string, Chat<any>>();
93
99
  const currentChat = shallowRef<Chat<any>>();
100
+
101
+ function setAvailableModes(modes: AgentMode[], defaultModeName?: string | null) {
102
+ availableModes.value = modes;
103
+ activeModeName.value =
104
+ modes.find((mode) => mode.name === activeModeName.value)?.name
105
+ ?? defaultModeName
106
+ ?? modes[0]?.name
107
+ ?? null;
108
+ }
109
+
110
+ function setActiveMode(modeName: string) {
111
+ if (!availableModes.value.some((mode) => mode.name === modeName)) {
112
+ return;
113
+ }
114
+
115
+ activeModeName.value = modeName;
116
+ }
117
+
94
118
  function setCurrentChat(sessionId: string) {
95
119
  if (chats.has(sessionId)) {
96
120
  currentChat.value = chats.get(sessionId) || null;
@@ -105,6 +129,7 @@ export const useAgentStore = defineStore('agent', () => {
105
129
  message,
106
130
  sessionId,
107
131
  timeZone: Intl.DateTimeFormat().resolvedOptions().timeZone,
132
+ mode: activeModeName.value,
108
133
  };
109
134
 
110
135
  return {
@@ -368,6 +393,10 @@ export const useAgentStore = defineStore('agent', () => {
368
393
  setIsTeleportedToBody,
369
394
  chatWidth,
370
395
  setChatWidth,
371
- focusTextInput
396
+ focusTextInput,
397
+ availableModes,
398
+ activeModeName,
399
+ setAvailableModes,
400
+ setActiveMode,
372
401
  }
373
- })
402
+ })
package/dist/index.js CHANGED
@@ -100,7 +100,11 @@ export default class AdminForthAgentPlugin extends AdminForthPlugin {
100
100
  modifyResourceConfig: { get: () => super.modifyResourceConfig }
101
101
  });
102
102
  return __awaiter(this, void 0, void 0, function* () {
103
+ var _a;
103
104
  _super.modifyResourceConfig.call(this, adminforth, resourceConfig);
105
+ if (!((_a = this.options.modes) === null || _a === void 0 ? void 0 : _a.length)) {
106
+ throw new Error("modes is required for AdminForthAgentPlugin");
107
+ }
104
108
  if (!this.adminforth.config.customization.globalInjections.header) {
105
109
  this.adminforth.config.customization.globalInjections.header = [];
106
110
  }
@@ -108,11 +112,10 @@ export default class AdminForthAgentPlugin extends AdminForthPlugin {
108
112
  file: this.componentPath("ChatSurface.vue"),
109
113
  meta: {
110
114
  pluginInstanceId: this.pluginInstanceId,
115
+ modes: this.options.modes.map((mode) => ({ name: mode.name })),
116
+ defaultModeName: this.options.modes[0].name,
111
117
  }
112
118
  });
113
- if (!this.pluginOptions.completionAdapter) {
114
- throw new Error("CompletionAdapter is required for AdminForthAgentPlugin");
115
- }
116
119
  if (!this.pluginOptions.sessionResource) {
117
120
  throw new Error("sessionResource is required for AdminForthAgentPlugin");
118
121
  }
@@ -210,17 +213,14 @@ export default class AdminForthAgentPlugin extends AdminForthPlugin {
210
213
  messageId,
211
214
  });
212
215
  const maxTokens = (_f = this.options.maxTokens) !== null && _f !== void 0 ? _f : 10000;
213
- const reasoning = (_g = this.options.reasoning) !== null && _g !== void 0 ? _g : 'low';
214
- const summaryReasoning = 'low';
216
+ const selectedMode = (_g = this.options.modes.find((mode) => mode.name === body.mode)) !== null && _g !== void 0 ? _g : this.options.modes[0];
215
217
  const model = createAgentChatModel({
216
- adapter: this.options.completionAdapter,
218
+ adapter: selectedMode.completionAdapter,
217
219
  maxTokens,
218
- reasoning,
219
220
  });
220
221
  const summaryModel = createAgentChatModel({
221
- adapter: this.options.completionAdapter,
222
+ adapter: selectedMode.completionAdapter,
222
223
  maxTokens,
223
- reasoning: summaryReasoning,
224
224
  });
225
225
  const systemPrompt = yield this.agentSystemPromptPromise;
226
226
  const stream = yield callAgent({
package/index.ts CHANGED
@@ -1,6 +1,5 @@
1
1
  import type {
2
2
  AdminForthResource,
3
- AdminUser,
4
3
  IAdminForth,
5
4
  IHttpServer
6
5
  } from "adminforth";
@@ -115,6 +114,9 @@ export default class AdminForthAgentPlugin extends AdminForthPlugin {
115
114
 
116
115
  async modifyResourceConfig(adminforth: IAdminForth, resourceConfig: AdminForthResource) {
117
116
  super.modifyResourceConfig(adminforth, resourceConfig);
117
+ if (!this.options.modes?.length) {
118
+ throw new Error("modes is required for AdminForthAgentPlugin");
119
+ }
118
120
  if (!this.adminforth.config.customization.globalInjections.header) {
119
121
  this.adminforth.config.customization.globalInjections.header = [];
120
122
  }
@@ -122,12 +124,10 @@ export default class AdminForthAgentPlugin extends AdminForthPlugin {
122
124
  file: this.componentPath("ChatSurface.vue"),
123
125
  meta: {
124
126
  pluginInstanceId: this.pluginInstanceId,
127
+ modes: this.options.modes.map((mode) => ({ name: mode.name })),
128
+ defaultModeName: this.options.modes[0].name,
125
129
  }
126
130
  });
127
-
128
- if (!this.pluginOptions.completionAdapter) {
129
- throw new Error("CompletionAdapter is required for AdminForthAgentPlugin");
130
- }
131
131
  if (!this.pluginOptions.sessionResource) {
132
132
  throw new Error("sessionResource is required for AdminForthAgentPlugin");
133
133
  }
@@ -244,19 +244,15 @@ export default class AdminForthAgentPlugin extends AdminForthPlugin {
244
244
  });
245
245
 
246
246
  const maxTokens = this.options.maxTokens ?? 10000;
247
- const reasoning = this.options.reasoning ?? 'low';
248
- const summaryReasoning = 'low';
249
-
247
+ const selectedMode = this.options.modes.find((mode) => mode.name === body.mode) ?? this.options.modes[0];
250
248
  const model = createAgentChatModel({
251
- adapter: this.options.completionAdapter,
249
+ adapter: selectedMode.completionAdapter,
252
250
  maxTokens,
253
- reasoning,
254
251
  });
255
252
 
256
253
  const summaryModel = createAgentChatModel({
257
- adapter: this.options.completionAdapter,
254
+ adapter: selectedMode.completionAdapter,
258
255
  maxTokens,
259
- reasoning: summaryReasoning,
260
256
  });
261
257
  const systemPrompt = await this.agentSystemPromptPromise;
262
258
  const stream = await callAgent({
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@adminforth/agent",
3
- "version": "1.7.0",
3
+ "version": "1.8.0",
4
4
  "main": "dist/index.js",
5
5
  "types": "dist/index.d.ts",
6
6
  "type": "module",
package/types.ts CHANGED
@@ -1,5 +1,4 @@
1
- import {type PluginsCommonOptions } from "adminforth";
2
- import { type CompletionAdapter } from "adminforth";
1
+ import { type PluginsCommonOptions, type CompletionAdapter } from "adminforth";
3
2
 
4
3
  interface ISessionResource {
5
4
  resourceId: string;
@@ -22,11 +21,14 @@ interface ITurnResource {
22
21
 
23
22
  export interface PluginOptions extends PluginsCommonOptions {
24
23
  /**
25
- * Adapter instance that will be used to generate responses.
26
- * You can use any adapter that implements the CompletionAdapter interface, for example the OpenAIAdapter included in adminforth,
27
- * or create your own that fetches responses from your custom backend.
24
+ * Modes for the plugin.
25
+ * Each mode can have its own configuration.
26
+ * Each mode uses its own completion adapter instance.
28
27
  */
29
- completionAdapter: CompletionAdapter;
28
+ modes: {
29
+ name: string;
30
+ completionAdapter: CompletionAdapter;
31
+ }[];
30
32
 
31
33
  /**
32
34
  * Max tokens for the generation.
@@ -40,6 +42,13 @@ export interface PluginOptions extends PluginsCommonOptions {
40
42
  */
41
43
  reasoning?: "none" | "minimal" | "low" | "medium" | "high" | "xhigh";
42
44
 
45
+ /**
46
+ * Resource configuration for sessions.
47
+ */
43
48
  sessionResource: ISessionResource;
49
+
50
+ /**
51
+ * Resource configuration for turns.
52
+ */
44
53
  turnResource: ITurnResource;
45
54
  }