@adminforth/agent 1.6.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.
- package/agent/simpleAgent.ts +1 -13
- package/agent/systemPrompt.ts +4 -1
- package/build.log +2 -2
- package/custom/ChatSurface.vue +66 -7
- package/custom/Message.vue +40 -0
- package/custom/incremark_code_renderers/IncremarkShikiCodeBlock.vue +1 -0
- package/custom/skills/fetch_data/SKILL.md +1 -1
- package/custom/skills/mutate_data/SKILL.md +5 -5
- package/custom/useAgentStore.ts +31 -2
- package/dist/agent/simpleAgent.js +2 -11
- package/dist/agent/systemPrompt.js +4 -1
- package/dist/custom/ChatSurface.vue +66 -7
- package/dist/custom/Message.vue +40 -0
- package/dist/custom/incremark_code_renderers/IncremarkShikiCodeBlock.vue +1 -0
- package/dist/custom/skills/fetch_data/SKILL.md +1 -1
- package/dist/custom/skills/mutate_data/SKILL.md +5 -5
- package/dist/custom/useAgentStore.ts +31 -2
- package/dist/index.js +9 -9
- package/index.ts +8 -12
- package/package.json +1 -1
- package/types.ts +15 -6
package/agent/simpleAgent.ts
CHANGED
|
@@ -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 =
|
|
189
|
+
const reasoning = options.extraRequestBodyParameters?.reasoning;
|
|
202
190
|
|
|
203
191
|
// @ts-ignore
|
|
204
192
|
return new ChatOpenAI({
|
package/agent/systemPrompt.ts
CHANGED
|
@@ -57,9 +57,10 @@ export async function buildAgentSystemPrompt(adminforth: IAdminForth) {
|
|
|
57
57
|
listBundledSkillManifests(),
|
|
58
58
|
]);
|
|
59
59
|
const alwaysAvailableTools = ALWAYS_AVAILABLE_API_TOOL_NAMES.join(", ");
|
|
60
|
+
const adminBasePath = adminforth.config.baseUrlSlashed;
|
|
60
61
|
const sections = [
|
|
61
62
|
DEFAULT_AGENT_SYSTEM_PROMPT,
|
|
62
|
-
`
|
|
63
|
+
`ADMIN_BASE_PATH: ${adminBasePath}`,
|
|
63
64
|
`List of resources:\n${formatResources(adminforth.config.resources)}`,
|
|
64
65
|
`You have always-available base tools: ${alwaysAvailableTools}.`,
|
|
65
66
|
primarySkills.length > 0
|
|
@@ -76,6 +77,8 @@ export async function buildAgentSystemPrompt(adminforth: IAdminForth) {
|
|
|
76
77
|
"If a fetched skill lists a non-base tool you need, call fetch_tool_schema for it immediately instead of telling the user the tool is unavailable.",
|
|
77
78
|
"For example: for record creation load mutate_data, read its tool list, call fetch_tool_schema for create_record, and then use create_record after confirmation.",
|
|
78
79
|
"When fetch_tool_schema succeeds, that tool becomes available on the next step.",
|
|
80
|
+
"All admin links must be relative paths and must start with ADMIN_BASE_PATH.",
|
|
81
|
+
"Build record links as ADMIN_BASE_PATH + resource/{resourceId}/show/{primary key}. Do not prepend any extra slash before resource.",
|
|
79
82
|
"Try to call as many tools as possible in parallel in one step.",
|
|
80
83
|
];
|
|
81
84
|
|
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
|
|
33
|
-
total size is
|
|
32
|
+
sent 174,726 bytes received 413 bytes 350,278.00 bytes/sec
|
|
33
|
+
total size is 173,048 speedup is 0.99
|
package/custom/ChatSurface.vue
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
<template>
|
|
2
2
|
<div
|
|
3
|
-
class="relative w-6 h-6 cursor-pointer mr-
|
|
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="
|
|
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="
|
|
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="
|
|
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
|
|
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
|
-
|
|
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>
|
package/custom/Message.vue
CHANGED
|
@@ -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>
|
|
@@ -12,4 +12,4 @@ To find specific data record you should use filters. ILIKE filters are preferred
|
|
|
12
12
|
|
|
13
13
|
For long texts show only several first words and add "..." at the end (only if user did not request this field specifically).
|
|
14
14
|
|
|
15
|
-
Also when you communicate with user about record, add related link to this record.
|
|
15
|
+
Also when you communicate with user about record, add related link to this record. Build it as `{ADMIN_BASE_PATH}resource/{resourceId}/show/{primary key}`. Use _label from `get_resource_data` as anchor text for link (use markdown link). Links should always be relative paths and must start with `ADMIN_BASE_PATH`. Do not add an extra slash after `ADMIN_BASE_PATH`.
|
|
@@ -21,7 +21,7 @@ Use `start_custom_action` and `start_custom_bulk_action` for resource actions.
|
|
|
21
21
|
Before performing any state mutation including action calls edit/delete please fetch record which is going to be edited/deleted and show user record in format field → value (show several most important fields which can help user to understand what exactly record he is going to edit or delete).
|
|
22
22
|
|
|
23
23
|
For field values with long texts show only several first words and add "..." at the end.
|
|
24
|
-
Also please add related link to record with will be changed.
|
|
24
|
+
Also please add related link to record with will be changed. Build it as `{ADMIN_BASE_PATH}resource/{resourceId}/show/{primary key}`. Use _label from `get_resource_data` as anchor text for link (use markdown link). Links should always be relative paths and must start with `ADMIN_BASE_PATH`. Do not add an extra slash after `ADMIN_BASE_PATH`.
|
|
25
25
|
|
|
26
26
|
And in the same message ask user for final confirmation.
|
|
27
27
|
|
|
@@ -62,7 +62,7 @@ If you want to block some user you can confirm that this action by saying:
|
|
|
62
62
|
* IP Country: USA
|
|
63
63
|
* Currently blocked: No // show this field only if it exists in user record
|
|
64
64
|
|
|
65
|
-
View [John Doe](
|
|
65
|
+
View [John Doe]({ADMIN_BASE_PATH}resource/users/show/123)
|
|
66
66
|
Are you sure?
|
|
67
67
|
```
|
|
68
68
|
|
|
@@ -85,7 +85,7 @@ I am going to update user:
|
|
|
85
85
|
* IP Country: USA
|
|
86
86
|
I am going to change email from john_doe@example.com to new_email@example.com
|
|
87
87
|
|
|
88
|
-
View [John Doe](
|
|
88
|
+
View [John Doe]({ADMIN_BASE_PATH}resource/users/show/123)
|
|
89
89
|
|
|
90
90
|
Are you sure?
|
|
91
91
|
```
|
|
@@ -105,7 +105,7 @@ If you gonna delete user record, in confirmation please share full user info (no
|
|
|
105
105
|
* Signed up: 2024 Jan 1
|
|
106
106
|
* IP Country: USA
|
|
107
107
|
|
|
108
|
-
View [John Doe](
|
|
108
|
+
View [John Doe]({ADMIN_BASE_PATH}resource/users/show/123)
|
|
109
109
|
|
|
110
110
|
Are you sure?
|
|
111
111
|
```
|
|
@@ -130,7 +130,7 @@ I am going to create user:
|
|
|
130
130
|
* Username: john_doe
|
|
131
131
|
* Email: john_doe@example.com
|
|
132
132
|
|
|
133
|
-
View [John Doe](
|
|
133
|
+
View [John Doe]({ADMIN_BASE_PATH}resource/users/show/421) # 421 is id of new created record
|
|
134
134
|
|
|
135
135
|
Are you sure?
|
|
136
136
|
```
|
package/custom/useAgentStore.ts
CHANGED
|
@@ -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 =
|
|
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 }
|
|
@@ -52,9 +52,10 @@ export function buildAgentSystemPrompt(adminforth) {
|
|
|
52
52
|
listBundledSkillManifests(),
|
|
53
53
|
]);
|
|
54
54
|
const alwaysAvailableTools = ALWAYS_AVAILABLE_API_TOOL_NAMES.join(", ");
|
|
55
|
+
const adminBasePath = adminforth.config.baseUrlSlashed;
|
|
55
56
|
const sections = [
|
|
56
57
|
DEFAULT_AGENT_SYSTEM_PROMPT,
|
|
57
|
-
`
|
|
58
|
+
`ADMIN_BASE_PATH: ${adminBasePath}`,
|
|
58
59
|
`List of resources:\n${formatResources(adminforth.config.resources)}`,
|
|
59
60
|
`You have always-available base tools: ${alwaysAvailableTools}.`,
|
|
60
61
|
primarySkills.length > 0
|
|
@@ -71,6 +72,8 @@ export function buildAgentSystemPrompt(adminforth) {
|
|
|
71
72
|
"If a fetched skill lists a non-base tool you need, call fetch_tool_schema for it immediately instead of telling the user the tool is unavailable.",
|
|
72
73
|
"For example: for record creation load mutate_data, read its tool list, call fetch_tool_schema for create_record, and then use create_record after confirmation.",
|
|
73
74
|
"When fetch_tool_schema succeeds, that tool becomes available on the next step.",
|
|
75
|
+
"All admin links must be relative paths and must start with ADMIN_BASE_PATH.",
|
|
76
|
+
"Build record links as ADMIN_BASE_PATH + resource/{resourceId}/show/{primary key}. Do not prepend any extra slash before resource.",
|
|
74
77
|
"Try to call as many tools as possible in parallel in one step.",
|
|
75
78
|
];
|
|
76
79
|
return sections.filter(Boolean).join("\n\n");
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
<template>
|
|
2
2
|
<div
|
|
3
|
-
class="relative w-6 h-6 cursor-pointer mr-
|
|
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="
|
|
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="
|
|
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="
|
|
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
|
|
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
|
-
|
|
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>
|
package/dist/custom/Message.vue
CHANGED
|
@@ -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>
|
|
@@ -12,4 +12,4 @@ To find specific data record you should use filters. ILIKE filters are preferred
|
|
|
12
12
|
|
|
13
13
|
For long texts show only several first words and add "..." at the end (only if user did not request this field specifically).
|
|
14
14
|
|
|
15
|
-
Also when you communicate with user about record, add related link to this record.
|
|
15
|
+
Also when you communicate with user about record, add related link to this record. Build it as `{ADMIN_BASE_PATH}resource/{resourceId}/show/{primary key}`. Use _label from `get_resource_data` as anchor text for link (use markdown link). Links should always be relative paths and must start with `ADMIN_BASE_PATH`. Do not add an extra slash after `ADMIN_BASE_PATH`.
|
|
@@ -21,7 +21,7 @@ Use `start_custom_action` and `start_custom_bulk_action` for resource actions.
|
|
|
21
21
|
Before performing any state mutation including action calls edit/delete please fetch record which is going to be edited/deleted and show user record in format field → value (show several most important fields which can help user to understand what exactly record he is going to edit or delete).
|
|
22
22
|
|
|
23
23
|
For field values with long texts show only several first words and add "..." at the end.
|
|
24
|
-
Also please add related link to record with will be changed.
|
|
24
|
+
Also please add related link to record with will be changed. Build it as `{ADMIN_BASE_PATH}resource/{resourceId}/show/{primary key}`. Use _label from `get_resource_data` as anchor text for link (use markdown link). Links should always be relative paths and must start with `ADMIN_BASE_PATH`. Do not add an extra slash after `ADMIN_BASE_PATH`.
|
|
25
25
|
|
|
26
26
|
And in the same message ask user for final confirmation.
|
|
27
27
|
|
|
@@ -62,7 +62,7 @@ If you want to block some user you can confirm that this action by saying:
|
|
|
62
62
|
* IP Country: USA
|
|
63
63
|
* Currently blocked: No // show this field only if it exists in user record
|
|
64
64
|
|
|
65
|
-
View [John Doe](
|
|
65
|
+
View [John Doe]({ADMIN_BASE_PATH}resource/users/show/123)
|
|
66
66
|
Are you sure?
|
|
67
67
|
```
|
|
68
68
|
|
|
@@ -85,7 +85,7 @@ I am going to update user:
|
|
|
85
85
|
* IP Country: USA
|
|
86
86
|
I am going to change email from john_doe@example.com to new_email@example.com
|
|
87
87
|
|
|
88
|
-
View [John Doe](
|
|
88
|
+
View [John Doe]({ADMIN_BASE_PATH}resource/users/show/123)
|
|
89
89
|
|
|
90
90
|
Are you sure?
|
|
91
91
|
```
|
|
@@ -105,7 +105,7 @@ If you gonna delete user record, in confirmation please share full user info (no
|
|
|
105
105
|
* Signed up: 2024 Jan 1
|
|
106
106
|
* IP Country: USA
|
|
107
107
|
|
|
108
|
-
View [John Doe](
|
|
108
|
+
View [John Doe]({ADMIN_BASE_PATH}resource/users/show/123)
|
|
109
109
|
|
|
110
110
|
Are you sure?
|
|
111
111
|
```
|
|
@@ -130,7 +130,7 @@ I am going to create user:
|
|
|
130
130
|
* Username: john_doe
|
|
131
131
|
* Email: john_doe@example.com
|
|
132
132
|
|
|
133
|
-
View [John Doe](
|
|
133
|
+
View [John Doe]({ADMIN_BASE_PATH}resource/users/show/421) # 421 is id of new created record
|
|
134
134
|
|
|
135
135
|
Are you sure?
|
|
136
136
|
```
|
|
@@ -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
|
|
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:
|
|
218
|
+
adapter: selectedMode.completionAdapter,
|
|
217
219
|
maxTokens,
|
|
218
|
-
reasoning,
|
|
219
220
|
});
|
|
220
221
|
const summaryModel = createAgentChatModel({
|
|
221
|
-
adapter:
|
|
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
|
|
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:
|
|
249
|
+
adapter: selectedMode.completionAdapter,
|
|
252
250
|
maxTokens,
|
|
253
|
-
reasoning,
|
|
254
251
|
});
|
|
255
252
|
|
|
256
253
|
const summaryModel = createAgentChatModel({
|
|
257
|
-
adapter:
|
|
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
package/types.ts
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
import
|
|
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
|
-
*
|
|
26
|
-
*
|
|
27
|
-
*
|
|
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
|
-
|
|
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
|
}
|