@adminforth/agent 1.34.2 → 1.36.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 +5 -0
- package/agent/tools/getUserLocation.ts +45 -0
- package/agent/tools/index.ts +2 -0
- package/build.log +9 -2
- package/custom/ChatSurface.vue +15 -0
- package/custom/composables/agentStore/constants.ts +12 -0
- package/custom/composables/agentStore/pageContext.ts +8 -0
- package/custom/composables/agentStore/useAgentChat.ts +69 -0
- package/custom/composables/agentStore/useAgentPlaceholder.ts +142 -0
- package/custom/composables/agentStore/useAgentSessions.ts +270 -0
- package/custom/composables/useAgentStore.ts +73 -393
- package/custom/conversation_area/ProcessingTimeline.vue +1 -1
- package/custom/env.d.ts +7 -0
- package/dist/agent/simpleAgent.js +3 -1
- package/dist/agent/tools/getUserLocation.js +31 -0
- package/dist/agent/tools/index.js +2 -0
- package/dist/custom/ChatSurface.vue +15 -0
- package/dist/custom/composables/agentStore/constants.ts +12 -0
- package/dist/custom/composables/agentStore/pageContext.ts +8 -0
- package/dist/custom/composables/agentStore/useAgentChat.ts +69 -0
- package/dist/custom/composables/agentStore/useAgentPlaceholder.ts +142 -0
- package/dist/custom/composables/agentStore/useAgentSessions.ts +270 -0
- package/dist/custom/composables/useAgentStore.ts +73 -393
- package/dist/custom/conversation_area/ProcessingTimeline.vue +1 -1
- package/dist/custom/env.d.ts +7 -0
- package/dist/index.js +264 -105
- package/index.ts +295 -103
- package/package.json +2 -2
- package/types.ts +6 -0
package/agent/simpleAgent.ts
CHANGED
|
@@ -19,12 +19,14 @@ import {
|
|
|
19
19
|
} from "./middleware/sequenceDebug.js";
|
|
20
20
|
import type { ApiBasedTool } from "../apiBasedTools.js";
|
|
21
21
|
import type { ToolCallEventSink } from "./toolCallEvents.js";
|
|
22
|
+
import type { CurrentPageContext } from "./tools/getUserLocation.js";
|
|
22
23
|
|
|
23
24
|
export const contextSchema = z.object({
|
|
24
25
|
adminUser: z.custom<AdminUser>(),
|
|
25
26
|
userTimeZone: z.string(),
|
|
26
27
|
sessionId: z.string(),
|
|
27
28
|
turnId: z.string(),
|
|
29
|
+
currentPage: z.custom<CurrentPageContext>().optional(),
|
|
28
30
|
httpExtra: z.custom<Partial<HttpExtra>>().optional(),
|
|
29
31
|
emitToolCallEvent: z.custom<ToolCallEventSink>(),
|
|
30
32
|
});
|
|
@@ -231,6 +233,7 @@ export async function callAgent(params: {
|
|
|
231
233
|
customComponentsDir: string;
|
|
232
234
|
sessionId: string;
|
|
233
235
|
turnId: string;
|
|
236
|
+
currentPage?: CurrentPageContext;
|
|
234
237
|
httpExtra?: Partial<HttpExtra>;
|
|
235
238
|
userTimeZone: string;
|
|
236
239
|
emitToolCallEvent: ToolCallEventSink;
|
|
@@ -249,6 +252,7 @@ export async function callAgent(params: {
|
|
|
249
252
|
customComponentsDir,
|
|
250
253
|
sessionId,
|
|
251
254
|
turnId,
|
|
255
|
+
currentPage,
|
|
252
256
|
httpExtra,
|
|
253
257
|
userTimeZone,
|
|
254
258
|
emitToolCallEvent,
|
|
@@ -293,6 +297,7 @@ export async function callAgent(params: {
|
|
|
293
297
|
userTimeZone,
|
|
294
298
|
sessionId,
|
|
295
299
|
turnId,
|
|
300
|
+
currentPage,
|
|
296
301
|
httpExtra,
|
|
297
302
|
emitToolCallEvent,
|
|
298
303
|
},
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { tool } from "langchain";
|
|
2
|
+
import { z } from "zod";
|
|
3
|
+
|
|
4
|
+
export type CurrentPageContext = {
|
|
5
|
+
path: string;
|
|
6
|
+
fullPath: string;
|
|
7
|
+
title: string;
|
|
8
|
+
url: string;
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
const getUserLocationSchema = z.object({});
|
|
12
|
+
|
|
13
|
+
export function createGetUserLocationTool() {
|
|
14
|
+
return tool(
|
|
15
|
+
async (_input, runtime) => {
|
|
16
|
+
const currentPage = (runtime.context as { currentPage?: CurrentPageContext }).currentPage;
|
|
17
|
+
|
|
18
|
+
if (!currentPage) {
|
|
19
|
+
return JSON.stringify(
|
|
20
|
+
{
|
|
21
|
+
status: 404,
|
|
22
|
+
message: "Current user location is not available.",
|
|
23
|
+
},
|
|
24
|
+
null,
|
|
25
|
+
2,
|
|
26
|
+
);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
return JSON.stringify(
|
|
30
|
+
{
|
|
31
|
+
status: 200,
|
|
32
|
+
location: currentPage,
|
|
33
|
+
},
|
|
34
|
+
null,
|
|
35
|
+
2,
|
|
36
|
+
);
|
|
37
|
+
},
|
|
38
|
+
{
|
|
39
|
+
name: "get_user_location",
|
|
40
|
+
description:
|
|
41
|
+
"Get the user's current location in the AdminForth UI, including current page path, full path, title, and URL. Call this tool when you do not understand what the user is referring to, especially when they say here, this page, current page, opened page, or otherwise rely on page context.",
|
|
42
|
+
schema: getUserLocationSchema,
|
|
43
|
+
},
|
|
44
|
+
);
|
|
45
|
+
}
|
package/agent/tools/index.ts
CHANGED
|
@@ -3,6 +3,7 @@ import { createFetchSkillTool } from "./fetchSkill.js";
|
|
|
3
3
|
import { createFetchToolSchemaTool } from "./fetchToolSchema.js";
|
|
4
4
|
import type { ApiBasedTool } from "../../apiBasedTools.js";
|
|
5
5
|
import { createApiTool } from "./apiTool.js";
|
|
6
|
+
import { createGetUserLocationTool } from "./getUserLocation.js";
|
|
6
7
|
|
|
7
8
|
export const ALWAYS_AVAILABLE_API_TOOL_NAMES = ["get_resource"] as const;
|
|
8
9
|
|
|
@@ -20,6 +21,7 @@ export async function createAgentTools(
|
|
|
20
21
|
|
|
21
22
|
return createApiTool(toolName, apiBasedTool);
|
|
22
23
|
}),
|
|
24
|
+
createGetUserLocationTool(),
|
|
23
25
|
await createFetchSkillTool(customComponentsDir),
|
|
24
26
|
await createFetchToolSchemaTool(apiBasedTools),
|
|
25
27
|
];
|
package/build.log
CHANGED
|
@@ -8,6 +8,7 @@ custom/ChatSurface.vue
|
|
|
8
8
|
custom/CustomAutoScrollContainer.vue
|
|
9
9
|
custom/SessionsHistory.vue
|
|
10
10
|
custom/chat.ts
|
|
11
|
+
custom/env.d.ts
|
|
11
12
|
custom/package.json
|
|
12
13
|
custom/pnpm-lock.yaml
|
|
13
14
|
custom/tsconfig.json
|
|
@@ -16,6 +17,12 @@ custom/utils.ts
|
|
|
16
17
|
custom/composables/
|
|
17
18
|
custom/composables/useAgentStore.ts
|
|
18
19
|
custom/composables/useAgentTransitions.ts
|
|
20
|
+
custom/composables/agentStore/
|
|
21
|
+
custom/composables/agentStore/constants.ts
|
|
22
|
+
custom/composables/agentStore/pageContext.ts
|
|
23
|
+
custom/composables/agentStore/useAgentChat.ts
|
|
24
|
+
custom/composables/agentStore/useAgentPlaceholder.ts
|
|
25
|
+
custom/composables/agentStore/useAgentSessions.ts
|
|
19
26
|
custom/conversation_area/
|
|
20
27
|
custom/conversation_area/ConversationArea.vue
|
|
21
28
|
custom/conversation_area/MessageRenderer.vue
|
|
@@ -40,5 +47,5 @@ custom/skills/fetch_data/SKILL.md
|
|
|
40
47
|
custom/skills/mutate_data/
|
|
41
48
|
custom/skills/mutate_data/SKILL.md
|
|
42
49
|
|
|
43
|
-
sent
|
|
44
|
-
total size is
|
|
50
|
+
sent 216,415 bytes received 699 bytes 434,228.00 bytes/sec
|
|
51
|
+
total size is 213,565 speedup is 0.98
|
package/custom/ChatSurface.vue
CHANGED
|
@@ -170,6 +170,7 @@
|
|
|
170
170
|
</div>
|
|
171
171
|
</div>
|
|
172
172
|
<Button
|
|
173
|
+
v-if="!agentStore.isResponseInProgress"
|
|
173
174
|
class="absolute right-4 bottom-2 !p-0 h-9 w-9"
|
|
174
175
|
@click="sendMessage"
|
|
175
176
|
:disabled="!agentStore.trimmedUserMessage || agentStore.isResponseInProgress"
|
|
@@ -179,6 +180,15 @@
|
|
|
179
180
|
text-white"
|
|
180
181
|
/>
|
|
181
182
|
</Button>
|
|
183
|
+
<Button
|
|
184
|
+
v-else
|
|
185
|
+
class="absolute right-4 bottom-2 !p-0 h-9 w-9"
|
|
186
|
+
@click="stopCurrentRequest"
|
|
187
|
+
>
|
|
188
|
+
<div
|
|
189
|
+
class="w-3 h-3 bg-white rounded-sm"
|
|
190
|
+
/>
|
|
191
|
+
</Button>
|
|
182
192
|
</div>
|
|
183
193
|
</div>
|
|
184
194
|
</div>
|
|
@@ -231,6 +241,7 @@ onClickOutside(modeMenu, () => { isModeMenuOpen.value = false; });
|
|
|
231
241
|
|
|
232
242
|
onMounted(async () => {
|
|
233
243
|
agentStore.setAvailableModes(props.meta.modes, props.meta.defaultModeName);
|
|
244
|
+
agentStore.setCurrentGenerationModeFromLocalStorage();
|
|
234
245
|
agentStore.regisrerTextInput(textInput.value);
|
|
235
246
|
window.addEventListener('resize', updateHeight)
|
|
236
247
|
textInput.value?.focus();
|
|
@@ -316,6 +327,10 @@ async function sendMessage() {
|
|
|
316
327
|
conversationArea.value?.handleSendMessage();
|
|
317
328
|
}
|
|
318
329
|
|
|
330
|
+
function stopCurrentRequest() {
|
|
331
|
+
agentStore.abortCurrentChatRequestAndAddSystemMessage();
|
|
332
|
+
}
|
|
333
|
+
|
|
319
334
|
function updateHeight() {
|
|
320
335
|
dvh.value = Math.round(window.visualViewport?.height || window.innerHeight);
|
|
321
336
|
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export type AgentMode = {
|
|
2
|
+
name: string;
|
|
3
|
+
};
|
|
4
|
+
|
|
5
|
+
export const DEFAULT_CHAT_WIDTH = 30;
|
|
6
|
+
export const MAX_WIDTH = 60;
|
|
7
|
+
export const MIN_WIDTH = 25;
|
|
8
|
+
|
|
9
|
+
export const DEFAULT_TEXTAREA_PLACEHOLDER = 'Type a message...';
|
|
10
|
+
export const PLACEHOLDER_TYPING_DELAY_MS = 60;
|
|
11
|
+
export const PLACEHOLDER_DELETING_DELAY_MS = 35;
|
|
12
|
+
export const PLACEHOLDER_HOLD_DELAY_MS = 3000;
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import { DefaultChatTransport } from 'ai';
|
|
2
|
+
import { shallowRef, type Ref } from 'vue';
|
|
3
|
+
import { Chat } from '../../chat';
|
|
4
|
+
import { getCurrentPageContext } from './pageContext';
|
|
5
|
+
|
|
6
|
+
type AgentImportMeta = ImportMeta & {
|
|
7
|
+
env: {
|
|
8
|
+
VITE_ADMINFORTH_PUBLIC_PATH?: string;
|
|
9
|
+
};
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
type CreateAgentChatManagerOptions = {
|
|
13
|
+
lastMessage: Ref<string>;
|
|
14
|
+
activeModeName: Ref<string | null>;
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
export function createAgentChatManager({
|
|
18
|
+
lastMessage,
|
|
19
|
+
activeModeName,
|
|
20
|
+
}: CreateAgentChatManagerOptions) {
|
|
21
|
+
const chats = new Map<string, Chat<any>>();
|
|
22
|
+
const currentChat = shallowRef<Chat<any> | null>();
|
|
23
|
+
|
|
24
|
+
function setCurrentChat(sessionId: string) {
|
|
25
|
+
if (chats.has(sessionId)) {
|
|
26
|
+
currentChat.value = chats.get(sessionId) || null;
|
|
27
|
+
} else {
|
|
28
|
+
const newChat = new Chat({
|
|
29
|
+
transport: new DefaultChatTransport({
|
|
30
|
+
api: `${(import.meta as AgentImportMeta).env.VITE_ADMINFORTH_PUBLIC_PATH || ''}/adminapi/v1/agent/response`,
|
|
31
|
+
credentials: 'include',
|
|
32
|
+
prepareSendMessagesRequest({ messages }: any) {
|
|
33
|
+
const message = lastMessage.value;
|
|
34
|
+
const body = {
|
|
35
|
+
message,
|
|
36
|
+
sessionId,
|
|
37
|
+
timeZone: Intl.DateTimeFormat().resolvedOptions().timeZone,
|
|
38
|
+
mode: activeModeName.value,
|
|
39
|
+
currentPage: getCurrentPageContext(),
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
return {
|
|
43
|
+
headers: {
|
|
44
|
+
Accept: 'text/event-stream',
|
|
45
|
+
'x-vercel-ai-ui-message-stream': 'v1',
|
|
46
|
+
},
|
|
47
|
+
body
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
}),
|
|
51
|
+
onError(error: unknown) {
|
|
52
|
+
console.error('Chat error:', error);
|
|
53
|
+
},
|
|
54
|
+
});
|
|
55
|
+
chats.set(sessionId, newChat);
|
|
56
|
+
currentChat.value = newChat;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
function abortCurrentChatRequest() {
|
|
61
|
+
currentChat.value?.stop();
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
return {
|
|
65
|
+
currentChat,
|
|
66
|
+
setCurrentChat,
|
|
67
|
+
abortCurrentChatRequest,
|
|
68
|
+
};
|
|
69
|
+
}
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
import { ref, watch, type Ref } from 'vue';
|
|
2
|
+
import { callAdminForthApi } from '@/utils';
|
|
3
|
+
import {
|
|
4
|
+
DEFAULT_TEXTAREA_PLACEHOLDER,
|
|
5
|
+
PLACEHOLDER_DELETING_DELAY_MS,
|
|
6
|
+
PLACEHOLDER_HOLD_DELAY_MS,
|
|
7
|
+
PLACEHOLDER_TYPING_DELAY_MS,
|
|
8
|
+
} from './constants';
|
|
9
|
+
|
|
10
|
+
type CreateAgentPlaceholderControllerOptions = {
|
|
11
|
+
userMessageInput: Ref<unknown>;
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
export function createAgentPlaceholderController({
|
|
15
|
+
userMessageInput,
|
|
16
|
+
}: CreateAgentPlaceholderControllerOptions) {
|
|
17
|
+
const userMessagePlaceholder = ref(DEFAULT_TEXTAREA_PLACEHOLDER);
|
|
18
|
+
const placeholderMessages = ref<string[]>([]);
|
|
19
|
+
const hasTypedMessageInPageSession = ref(false);
|
|
20
|
+
|
|
21
|
+
let placeholderAnimationTimer: ReturnType<typeof setTimeout> | null = null;
|
|
22
|
+
|
|
23
|
+
function clearPlaceholderAnimationTimer() {
|
|
24
|
+
if (placeholderAnimationTimer !== null) {
|
|
25
|
+
clearTimeout(placeholderAnimationTimer);
|
|
26
|
+
placeholderAnimationTimer = null;
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
function resetPlaceholder() {
|
|
31
|
+
clearPlaceholderAnimationTimer();
|
|
32
|
+
userMessagePlaceholder.value = DEFAULT_TEXTAREA_PLACEHOLDER;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
function stopPlaceholderAnimation() {
|
|
36
|
+
resetPlaceholder();
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
function startPlaceholderAnimation(messages: string[]) {
|
|
40
|
+
clearPlaceholderAnimationTimer();
|
|
41
|
+
|
|
42
|
+
if (!messages.length) {
|
|
43
|
+
userMessagePlaceholder.value = DEFAULT_TEXTAREA_PLACEHOLDER;
|
|
44
|
+
return;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
let messageIndex = 0;
|
|
48
|
+
let visibleLength = 0;
|
|
49
|
+
let isDeleting = false;
|
|
50
|
+
|
|
51
|
+
const animate = () => {
|
|
52
|
+
const currentMessage = messages[messageIndex];
|
|
53
|
+
|
|
54
|
+
if (!currentMessage) {
|
|
55
|
+
resetPlaceholder();
|
|
56
|
+
return;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
if (!isDeleting) {
|
|
60
|
+
visibleLength += 1;
|
|
61
|
+
userMessagePlaceholder.value = currentMessage.slice(0, visibleLength);
|
|
62
|
+
|
|
63
|
+
if (visibleLength >= currentMessage.length) {
|
|
64
|
+
isDeleting = true;
|
|
65
|
+
placeholderAnimationTimer = setTimeout(animate, PLACEHOLDER_HOLD_DELAY_MS);
|
|
66
|
+
return;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
placeholderAnimationTimer = setTimeout(animate, PLACEHOLDER_TYPING_DELAY_MS);
|
|
70
|
+
return;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
visibleLength -= 1;
|
|
74
|
+
userMessagePlaceholder.value = currentMessage.slice(0, Math.max(visibleLength, 0));
|
|
75
|
+
|
|
76
|
+
if (visibleLength <= 0) {
|
|
77
|
+
isDeleting = false;
|
|
78
|
+
messageIndex = (messageIndex + 1) % messages.length;
|
|
79
|
+
placeholderAnimationTimer = setTimeout(animate, PLACEHOLDER_TYPING_DELAY_MS);
|
|
80
|
+
return;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
placeholderAnimationTimer = setTimeout(animate, PLACEHOLDER_DELETING_DELAY_MS);
|
|
84
|
+
};
|
|
85
|
+
|
|
86
|
+
animate();
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
async function fetchPlaceholderMessages() {
|
|
90
|
+
if (hasTypedMessageInPageSession.value) {
|
|
91
|
+
stopPlaceholderAnimation();
|
|
92
|
+
return;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
try {
|
|
96
|
+
const res = await callAdminForthApi({
|
|
97
|
+
method: 'POST',
|
|
98
|
+
path: '/agent/get-placeholder-messages',
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
if (res.error) {
|
|
102
|
+
console.error('Error fetching placeholder messages:', res.error);
|
|
103
|
+
placeholderMessages.value = [];
|
|
104
|
+
resetPlaceholder();
|
|
105
|
+
return;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
placeholderMessages.value = Array.isArray(res.messages)
|
|
109
|
+
? res.messages.filter((message: unknown): message is string => typeof message === 'string' && message.length > 0)
|
|
110
|
+
: [];
|
|
111
|
+
|
|
112
|
+
if (!placeholderMessages.value.length) {
|
|
113
|
+
resetPlaceholder();
|
|
114
|
+
return;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
startPlaceholderAnimation(placeholderMessages.value);
|
|
118
|
+
} catch (error) {
|
|
119
|
+
console.error('Error fetching placeholder messages', error);
|
|
120
|
+
placeholderMessages.value = [];
|
|
121
|
+
resetPlaceholder();
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
watch(userMessageInput, (newVal: unknown) => {
|
|
126
|
+
if (hasTypedMessageInPageSession.value) {
|
|
127
|
+
return;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
if (typeof newVal === 'string' && newVal.trim() !== '') {
|
|
131
|
+
hasTypedMessageInPageSession.value = true;
|
|
132
|
+
stopPlaceholderAnimation();
|
|
133
|
+
}
|
|
134
|
+
});
|
|
135
|
+
|
|
136
|
+
return {
|
|
137
|
+
userMessagePlaceholder,
|
|
138
|
+
hasTypedMessageInPageSession,
|
|
139
|
+
fetchPlaceholderMessages,
|
|
140
|
+
stopPlaceholderAnimation,
|
|
141
|
+
};
|
|
142
|
+
}
|
|
@@ -0,0 +1,270 @@
|
|
|
1
|
+
import type { ComputedRef, Ref, ShallowRef } from 'vue';
|
|
2
|
+
import { callAdminForthApi } from '@/utils';
|
|
3
|
+
import type { Chat } from '../../chat';
|
|
4
|
+
import type { IAgentSession, ISessionsListItem, IPart } from '../../types';
|
|
5
|
+
|
|
6
|
+
type AdminforthLike = {
|
|
7
|
+
confirm(options: { message: string; yes: string; no: string }): Promise<boolean>;
|
|
8
|
+
};
|
|
9
|
+
|
|
10
|
+
type CreateAgentSessionManagerOptions = {
|
|
11
|
+
activeSessionId: Ref<string | null>;
|
|
12
|
+
currentSession: Ref<IAgentSession | null>;
|
|
13
|
+
sessionList: Ref<ISessionsListItem[]>;
|
|
14
|
+
sessions: Ref<Record<string, IAgentSession>>;
|
|
15
|
+
currentChat: ShallowRef<Chat<any> | null | undefined>;
|
|
16
|
+
trimmedUserMessage: ComputedRef<string>;
|
|
17
|
+
isResponseInProgress: ComputedRef<boolean>;
|
|
18
|
+
userMessageInput: Ref<any>;
|
|
19
|
+
lastMessage: Ref<string>;
|
|
20
|
+
blockCloseOfChat: Ref<boolean>;
|
|
21
|
+
adminforth: AdminforthLike;
|
|
22
|
+
setCurrentChat: (sessionId: string) => void;
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
export function createAgentSessionManager({
|
|
26
|
+
activeSessionId,
|
|
27
|
+
currentSession,
|
|
28
|
+
sessionList,
|
|
29
|
+
sessions,
|
|
30
|
+
currentChat,
|
|
31
|
+
trimmedUserMessage,
|
|
32
|
+
isResponseInProgress,
|
|
33
|
+
userMessageInput,
|
|
34
|
+
lastMessage,
|
|
35
|
+
blockCloseOfChat,
|
|
36
|
+
adminforth,
|
|
37
|
+
setCurrentChat,
|
|
38
|
+
}: CreateAgentSessionManagerOptions) {
|
|
39
|
+
function sortSessionsListByTimestamp(sessionsListToSort: ISessionsListItem[]) {
|
|
40
|
+
return [...sessionsListToSort].sort((a: ISessionsListItem, b: ISessionsListItem) => b.timestamp.localeCompare(a.timestamp));
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
function saveCurrentSessionInCache() {
|
|
44
|
+
if (currentSession.value) {
|
|
45
|
+
currentSession.value.messages = currentChat.value?.messages.map((m: any) => ({
|
|
46
|
+
role: m.role,
|
|
47
|
+
text: m.parts.map((p: IPart) => p.type === 'text' ? p.text : '').join(''),
|
|
48
|
+
})) || [];
|
|
49
|
+
sessions.value[currentSession.value.sessionId] = currentSession.value;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
async function fetchSession(sessionId: string) {
|
|
54
|
+
try {
|
|
55
|
+
const res = await callAdminForthApi({
|
|
56
|
+
method: 'POST',
|
|
57
|
+
path: '/agent/get-session-info',
|
|
58
|
+
body: { sessionId },
|
|
59
|
+
});
|
|
60
|
+
if (res.error) {
|
|
61
|
+
console.error('Error fetching session:', res.error);
|
|
62
|
+
return;
|
|
63
|
+
}
|
|
64
|
+
sessions.value[sessionId] = res.session;
|
|
65
|
+
setCurrentChat(sessionId);
|
|
66
|
+
} catch (error) {
|
|
67
|
+
console.error('Error fetching session', error);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
async function setActiveSession(sessionId: string) {
|
|
72
|
+
activeSessionId.value = sessionId;
|
|
73
|
+
saveCurrentSessionInCache();
|
|
74
|
+
if (!sessions.value[sessionId]) {
|
|
75
|
+
await fetchSession(sessionId);
|
|
76
|
+
}
|
|
77
|
+
currentSession.value = sessions.value[sessionId];
|
|
78
|
+
setCurrentChat(sessionId);
|
|
79
|
+
if (currentChat.value.messages.length === 0) {
|
|
80
|
+
currentChat.value.messages = currentSession.value?.messages.map((m: any) => ({
|
|
81
|
+
role: m.role,
|
|
82
|
+
parts:[{
|
|
83
|
+
type: 'text',
|
|
84
|
+
text: m.text,
|
|
85
|
+
state: 'done',
|
|
86
|
+
}]
|
|
87
|
+
}));
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
async function deletePreSession() {
|
|
92
|
+
sessionList.value = sessionList.value.filter((s: ISessionsListItem) => s.sessionId !== 'pre-session');
|
|
93
|
+
if (activeSessionId.value === 'pre-session') {
|
|
94
|
+
activeSessionId.value = null;
|
|
95
|
+
currentSession.value = null;
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
async function createNewSession(triggerMessage?: string) {
|
|
100
|
+
try {
|
|
101
|
+
const res = await callAdminForthApi({
|
|
102
|
+
method: 'POST',
|
|
103
|
+
path: '/agent/create-session',
|
|
104
|
+
body: {
|
|
105
|
+
triggerMessage
|
|
106
|
+
},
|
|
107
|
+
});
|
|
108
|
+
if (res.error) {
|
|
109
|
+
console.error('Error creating new session:', res.error);
|
|
110
|
+
return;
|
|
111
|
+
}
|
|
112
|
+
deletePreSession();
|
|
113
|
+
sessions.value[res.sessionId] = res;
|
|
114
|
+
sessionList.value.unshift({
|
|
115
|
+
sessionId: res.sessionId,
|
|
116
|
+
title: res.title,
|
|
117
|
+
timestamp: new Date().toISOString(),
|
|
118
|
+
});
|
|
119
|
+
setActiveSession(res.sessionId);
|
|
120
|
+
} catch (error) {
|
|
121
|
+
console.error('Error creating new session', error);
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
async function sendMessage() {
|
|
126
|
+
const message = trimmedUserMessage.value;
|
|
127
|
+
if (!message || isResponseInProgress.value) {
|
|
128
|
+
return;
|
|
129
|
+
}
|
|
130
|
+
if (!currentSession.value || currentSession.value.sessionId === 'pre-session') {
|
|
131
|
+
await createNewSession(message);
|
|
132
|
+
}
|
|
133
|
+
currentSession.value!.timestamp = new Date().toISOString();
|
|
134
|
+
sessionList.value = sortSessionsListByTimestamp(sessionList.value.map((s: ISessionsListItem) => s.sessionId === currentSession.value?.sessionId ? {
|
|
135
|
+
...s,
|
|
136
|
+
timestamp: currentSession.value?.timestamp || s.timestamp,
|
|
137
|
+
} : s));
|
|
138
|
+
lastMessage.value = message;
|
|
139
|
+
currentChat.value?.sendMessage({
|
|
140
|
+
text: message,
|
|
141
|
+
});
|
|
142
|
+
userMessageInput.value = '';
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
async function createPreSession() {
|
|
146
|
+
saveCurrentSessionInCache();
|
|
147
|
+
if (!sessionList.value.some((s: ISessionsListItem) => s.sessionId === 'pre-session')) {
|
|
148
|
+
sessionList.value.unshift({
|
|
149
|
+
sessionId: 'pre-session',
|
|
150
|
+
title: 'New Session',
|
|
151
|
+
timestamp: new Date().toISOString(),
|
|
152
|
+
});
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
activeSessionId.value = 'pre-session';
|
|
156
|
+
currentSession.value = {
|
|
157
|
+
sessionId: 'pre-session',
|
|
158
|
+
title: 'New Session',
|
|
159
|
+
timestamp: new Date().toISOString(),
|
|
160
|
+
messages: [],
|
|
161
|
+
};
|
|
162
|
+
sessions.value['pre-session'] = currentSession.value;
|
|
163
|
+
setCurrentChat('pre-session');
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
async function deleteSession(sessionId: string) {
|
|
167
|
+
if (sessionId === 'pre-session') {
|
|
168
|
+
deletePreSession();
|
|
169
|
+
return;
|
|
170
|
+
}
|
|
171
|
+
blockCloseOfChat.value = true;
|
|
172
|
+
const isConfirmed = await adminforth.confirm({message: 'Are you sure, that you want to delete this session?', yes: 'Yes', no: 'No'});
|
|
173
|
+
blockCloseOfChat.value = false;
|
|
174
|
+
if (!isConfirmed) {
|
|
175
|
+
return;
|
|
176
|
+
}
|
|
177
|
+
try {
|
|
178
|
+
const res = await callAdminForthApi({
|
|
179
|
+
method: 'POST',
|
|
180
|
+
path: '/agent/delete-session',
|
|
181
|
+
body: { sessionId },
|
|
182
|
+
});
|
|
183
|
+
if (res.error) {
|
|
184
|
+
console.error('Error deleting session:', res.error);
|
|
185
|
+
return;
|
|
186
|
+
}
|
|
187
|
+
delete sessions.value[sessionId];
|
|
188
|
+
sessionList.value = sessionList.value.filter((s: ISessionsListItem) => s.sessionId !== sessionId);
|
|
189
|
+
if (activeSessionId.value === sessionId) {
|
|
190
|
+
activeSessionId.value = null;
|
|
191
|
+
currentSession.value = null;
|
|
192
|
+
}
|
|
193
|
+
} catch (error) {
|
|
194
|
+
console.error('Error deleting session', error);
|
|
195
|
+
}
|
|
196
|
+
if(sessionId === activeSessionId.value) {
|
|
197
|
+
activeSessionId.value = sessionList.value.length > 0 ? sessionList.value[0].sessionId : null;
|
|
198
|
+
if (activeSessionId.value) {
|
|
199
|
+
currentSession.value = sessions.value[activeSessionId.value] || null;
|
|
200
|
+
} else {
|
|
201
|
+
currentSession.value = null;
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
async function fetchSessionsList() {
|
|
207
|
+
try {
|
|
208
|
+
const res = await callAdminForthApi({
|
|
209
|
+
method: 'POST',
|
|
210
|
+
path: '/agent/get-sessions',
|
|
211
|
+
body: {
|
|
212
|
+
limit: 100,
|
|
213
|
+
},
|
|
214
|
+
});
|
|
215
|
+
if (res.error) {
|
|
216
|
+
console.error('Error fetching sessions list:', res.error);
|
|
217
|
+
return;
|
|
218
|
+
}
|
|
219
|
+
sessionList.value = res.sessions;
|
|
220
|
+
} catch (error) {
|
|
221
|
+
console.error('Error fetching sessions list', error);
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
function addDebugMessage(message: string) {
|
|
226
|
+
const debugMessage = {
|
|
227
|
+
role: 'assistant',
|
|
228
|
+
parts: [{
|
|
229
|
+
type: 'text',
|
|
230
|
+
text: message,
|
|
231
|
+
state: 'done',
|
|
232
|
+
}]
|
|
233
|
+
};
|
|
234
|
+
currentChat.value?.messages.push(debugMessage);
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
function addSystemMessage(message: string) {
|
|
238
|
+
const systemMessage = {
|
|
239
|
+
role: 'system',
|
|
240
|
+
parts: [{
|
|
241
|
+
type: 'text',
|
|
242
|
+
text: message,
|
|
243
|
+
state: 'done',
|
|
244
|
+
}]
|
|
245
|
+
};
|
|
246
|
+
currentChat.value?.messages.push(systemMessage);
|
|
247
|
+
try {
|
|
248
|
+
const res = callAdminForthApi({
|
|
249
|
+
method: 'POST',
|
|
250
|
+
path: '/agent/add-system-message-to-turns',
|
|
251
|
+
body: {
|
|
252
|
+
sessionId: activeSessionId.value,
|
|
253
|
+
systemMessage: message,
|
|
254
|
+
},
|
|
255
|
+
});
|
|
256
|
+
} catch (error) {
|
|
257
|
+
console.error('Error adding system message', error);
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
return {
|
|
262
|
+
sendMessage,
|
|
263
|
+
createPreSession,
|
|
264
|
+
setActiveSession,
|
|
265
|
+
fetchSessionsList,
|
|
266
|
+
deleteSession,
|
|
267
|
+
addDebugMessage,
|
|
268
|
+
addSystemMessage,
|
|
269
|
+
};
|
|
270
|
+
}
|