@adminforth/agent 1.21.0 → 1.22.1
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/build.log +12 -6
- package/custom/ChatSurface.vue +3 -2
- package/custom/CustomAutoScrollContainer.vue +127 -0
- package/custom/SessionsHistory.vue +11 -2
- package/custom/composables/useAgentStore.ts +6 -4
- package/custom/conversation_area/ConversationArea.vue +106 -0
- package/custom/conversation_area/MessageRenderer.vue +33 -0
- package/custom/conversation_area/ProcessingTimeline.vue +190 -0
- package/custom/conversation_area/ReasoningRenderer.vue +87 -0
- package/{dist/custom/Message.vue → custom/conversation_area/TextRenderer.vue} +14 -102
- package/custom/conversation_area/ThreeDotsAnimation.vue +35 -0
- package/custom/{ToolRenderer.vue → conversation_area/ToolRenderer.vue} +65 -13
- package/custom/conversation_area/ToolsGroup.vue +63 -0
- package/custom/package.json +2 -1
- package/custom/pnpm-lock.yaml +18 -0
- package/custom/types.ts +11 -1
- package/custom/utils.ts +29 -0
- package/dist/custom/ChatSurface.vue +3 -2
- package/dist/custom/CustomAutoScrollContainer.vue +127 -0
- package/dist/custom/SessionsHistory.vue +11 -2
- package/dist/custom/composables/useAgentStore.ts +6 -4
- package/dist/custom/conversation_area/ConversationArea.vue +106 -0
- package/dist/custom/conversation_area/MessageRenderer.vue +33 -0
- package/dist/custom/conversation_area/ProcessingTimeline.vue +190 -0
- package/dist/custom/conversation_area/ReasoningRenderer.vue +87 -0
- package/{custom/Message.vue → dist/custom/conversation_area/TextRenderer.vue} +14 -102
- package/dist/custom/conversation_area/ThreeDotsAnimation.vue +35 -0
- package/dist/custom/{ToolRenderer.vue → conversation_area/ToolRenderer.vue} +65 -13
- package/dist/custom/conversation_area/ToolsGroup.vue +63 -0
- package/dist/custom/package.json +2 -1
- package/dist/custom/pnpm-lock.yaml +18 -0
- package/dist/custom/types.ts +11 -1
- package/dist/custom/utils.ts +29 -0
- package/package.json +1 -1
- package/custom/ConversationArea.vue +0 -198
- package/custom/ToolsGroup.vue +0 -67
- package/dist/custom/ConversationArea.vue +0 -198
- package/dist/custom/ToolsGroup.vue +0 -67
|
@@ -1,198 +0,0 @@
|
|
|
1
|
-
<template>
|
|
2
|
-
<button @click="scrollContainer.scrollToBottom()">
|
|
3
|
-
<IconArrowDownOutline
|
|
4
|
-
class="absolute z-10 bottom-32 left-1/2 bg-lightPrimary dark:bg-darkPrimary text-white p-2 w-10 h-10 rounded-full transition-opacity duration-100 ease-in"
|
|
5
|
-
:class="showScrollToBottomButton ? 'opacity-100' : 'opacity-0 pointer-events-none'"
|
|
6
|
-
:disabled="!showScrollToBottomButton"
|
|
7
|
-
/>
|
|
8
|
-
</button>
|
|
9
|
-
|
|
10
|
-
<SessionsHistory
|
|
11
|
-
:class="agentStore.isSessionHistoryOpen ? 'translate-x-0' : '-translate-x-full'"
|
|
12
|
-
/>
|
|
13
|
-
<div
|
|
14
|
-
v-if="agentStore.isSessionHistoryOpen"
|
|
15
|
-
@click="agentStore.setSessionHistoryOpen(false)"
|
|
16
|
-
class="absolute bg-black/10 backdrop-blur-md z-10 h-full w-full"
|
|
17
|
-
>
|
|
18
|
-
|
|
19
|
-
</div>
|
|
20
|
-
<AutoScrollContainer
|
|
21
|
-
:enabled="!showScrollToBottomButton"
|
|
22
|
-
class="relative flex flex-col overflow-y-auto translate-x-[-50%] left-1/2"
|
|
23
|
-
ref="scrollContainer"
|
|
24
|
-
:threshold="10"
|
|
25
|
-
behavior="smooth"
|
|
26
|
-
:style="{
|
|
27
|
-
maxWidth: agentStore.isFullScreen ? agentStore.MAX_WIDTH+'rem' : '100%',
|
|
28
|
-
transition: `
|
|
29
|
-
max-width ${agentTransitions.TRANSITION_DURATION}ms ease-in-out,
|
|
30
|
-
transform ${agentTransitions.TRANSITION_DURATION}ms ease-in-out
|
|
31
|
-
`
|
|
32
|
-
}"
|
|
33
|
-
>
|
|
34
|
-
|
|
35
|
-
<div
|
|
36
|
-
v-for="message in props.messages" :key="message.id"
|
|
37
|
-
class="flex flex-col w-full"
|
|
38
|
-
:class="message.role === 'user' ? 'self-end' : 'self-start'"
|
|
39
|
-
>
|
|
40
|
-
<template
|
|
41
|
-
v-for="(part, index) in getParts(message)"
|
|
42
|
-
:key="part.type"
|
|
43
|
-
>
|
|
44
|
-
<Message
|
|
45
|
-
v-if="part.type !== 'data-tool-call'"
|
|
46
|
-
:message="part.text"
|
|
47
|
-
:role="message.role"
|
|
48
|
-
:type="part.type"
|
|
49
|
-
:state="part.state"
|
|
50
|
-
:data="part.data"
|
|
51
|
-
@toggle-thoughts="() => clicks++"
|
|
52
|
-
>
|
|
53
|
-
</Message>
|
|
54
|
-
<ToolsGroup v-else :toolGroup="groupToolCallParts(message, index, part)" />
|
|
55
|
-
</template>
|
|
56
|
-
</div>
|
|
57
|
-
<!-- Show a placeholder message if the last message is not of type 'text' or 'reasoning' -->
|
|
58
|
-
<Message
|
|
59
|
-
v-if="props.messages.length > 0 && showFakeThinkingMessage"
|
|
60
|
-
:message="''"
|
|
61
|
-
:role="props.messages[props.messages.length - 1].role"
|
|
62
|
-
type="reasoning"
|
|
63
|
-
state="streaming"
|
|
64
|
-
/>
|
|
65
|
-
<div
|
|
66
|
-
v-if="props.messages.length === 0"
|
|
67
|
-
class="flex-1 flex flex-col items-center justify-center text-gray-400 tracking-widest text-xl font-medium"
|
|
68
|
-
>
|
|
69
|
-
<p>{{ $t('Start the conversation') }}</p>
|
|
70
|
-
<p class="tracking-normal text-base text">{{ $t('Give any input to begin') }}</p>
|
|
71
|
-
</div>
|
|
72
|
-
</AutoScrollContainer>
|
|
73
|
-
</template>
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
<script setup lang="ts">
|
|
77
|
-
import Message from './Message.vue';
|
|
78
|
-
import type { IMessage, IPart } from './types';
|
|
79
|
-
import { useTemplateRef, ref, defineAsyncComponent, onMounted, onUnmounted, watch, computed } from 'vue';
|
|
80
|
-
import { IconArrowDownOutline } from '@iconify-prerendered/vue-flowbite';
|
|
81
|
-
import SessionsHistory from './SessionsHistory.vue';
|
|
82
|
-
import { useAgentStore } from './composables/useAgentStore';
|
|
83
|
-
import ToolsGroup from './ToolsGroup.vue';
|
|
84
|
-
import { useAgentTransitions } from './composables/useAgentTransitions';
|
|
85
|
-
|
|
86
|
-
const scrollContainer = useTemplateRef('scrollContainer');
|
|
87
|
-
const showScrollToBottomButton = ref(false);
|
|
88
|
-
const innerScrollContainerRef = ref(null);
|
|
89
|
-
const AutoScrollContainer = defineAsyncComponent(() => import('@incremark/vue').then(module => module.AutoScrollContainer))
|
|
90
|
-
const agentStore = useAgentStore();
|
|
91
|
-
const agentTransitions = useAgentTransitions();
|
|
92
|
-
const clicks = ref(0);
|
|
93
|
-
|
|
94
|
-
function recalculateScroll() {
|
|
95
|
-
if (scrollContainer.value) {
|
|
96
|
-
const isScrolledUp = scrollContainer.value.isUserScrolledUp();
|
|
97
|
-
showScrollToBottomButton.value = !!isScrolledUp;
|
|
98
|
-
}
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
onMounted(async () => {
|
|
102
|
-
await import('@incremark/theme/styles.css')
|
|
103
|
-
await agentStore.fetchPlaceholderMessages()
|
|
104
|
-
});
|
|
105
|
-
|
|
106
|
-
onUnmounted(() => {
|
|
107
|
-
agentStore.stopPlaceholderAnimation();
|
|
108
|
-
});
|
|
109
|
-
|
|
110
|
-
watch(scrollContainer, () => {
|
|
111
|
-
if (scrollContainer.value) {
|
|
112
|
-
innerScrollContainerRef.value = scrollContainer.value.container;
|
|
113
|
-
|
|
114
|
-
innerScrollContainerRef.value.addEventListener('scroll', () => {
|
|
115
|
-
recalculateScroll();
|
|
116
|
-
});
|
|
117
|
-
}
|
|
118
|
-
})
|
|
119
|
-
|
|
120
|
-
watch(clicks, () => {
|
|
121
|
-
recalculateScroll();
|
|
122
|
-
})
|
|
123
|
-
|
|
124
|
-
const showFakeThinkingMessage = computed(() => {
|
|
125
|
-
const lastMessage = props.messages[props.messages.length - 1];
|
|
126
|
-
if (!lastMessage) return false;
|
|
127
|
-
const lastPart = getParts(lastMessage)[getParts(lastMessage).length - 1];
|
|
128
|
-
return lastPart?.type !== 'text' && lastPart?.type !== 'reasoning';
|
|
129
|
-
})
|
|
130
|
-
|
|
131
|
-
const getParts = (message: IMessage) => {
|
|
132
|
-
return message.parts?.length
|
|
133
|
-
? message.parts
|
|
134
|
-
: [{ text: '', type: 'reasoning', state: 'streaming' }];
|
|
135
|
-
};
|
|
136
|
-
|
|
137
|
-
const formatToolCallTextPart = ((part: IPart, currentMessage: IMessage) => {
|
|
138
|
-
if (part.type === 'data-tool-call') {
|
|
139
|
-
if (part.data?.phase === 'start') {
|
|
140
|
-
const finishedPart = currentMessage.parts?.find(p => p.type === 'data-tool-call' && p.data?.toolCallId === part.data?.toolCallId && p.data?.phase === 'end');
|
|
141
|
-
return {
|
|
142
|
-
type: 'text',
|
|
143
|
-
toolInfo: {
|
|
144
|
-
toolCallId: part.data!.toolCallId,
|
|
145
|
-
toolName: part.data!.toolName,
|
|
146
|
-
phase: finishedPart ? 'end' : 'start',
|
|
147
|
-
durationMs: finishedPart ? finishedPart.data?.durationMs : undefined,
|
|
148
|
-
input: part.data!.input,
|
|
149
|
-
output: finishedPart ? finishedPart.data!.output : undefined,
|
|
150
|
-
}
|
|
151
|
-
}
|
|
152
|
-
}
|
|
153
|
-
}
|
|
154
|
-
return null;
|
|
155
|
-
});
|
|
156
|
-
|
|
157
|
-
const groupToolCallParts = (message: IMessage, currentPartIndex: number, currentPart: IPart) => {
|
|
158
|
-
const groupedParts: { title: string; groupedTools: IPart[] }[] = [];
|
|
159
|
-
let currentToolName: string | null = null;
|
|
160
|
-
const parts = getParts(message);
|
|
161
|
-
if (!parts) return [];
|
|
162
|
-
const formatedToolParts = parts.map(part => {
|
|
163
|
-
return formatToolCallTextPart(part as IPart, message)
|
|
164
|
-
});
|
|
165
|
-
const currentPartIndexInFormatedParts = formatedToolParts.findIndex(part => part?.toolInfo?.toolCallId === currentPart.data?.toolCallId);
|
|
166
|
-
if (currentPartIndexInFormatedParts === -1) {
|
|
167
|
-
return [];
|
|
168
|
-
}
|
|
169
|
-
for( const[index, part] of formatedToolParts.entries()){
|
|
170
|
-
if ( index < currentPartIndexInFormatedParts - 1 ) {
|
|
171
|
-
continue;
|
|
172
|
-
}
|
|
173
|
-
if(!part || !part.toolInfo) {
|
|
174
|
-
continue;
|
|
175
|
-
}
|
|
176
|
-
currentToolName = part.toolInfo.toolName;
|
|
177
|
-
if (!groupedParts.find(group => group.title === currentToolName)) {
|
|
178
|
-
groupedParts.push({
|
|
179
|
-
title: currentToolName,
|
|
180
|
-
groupedTools: []
|
|
181
|
-
})
|
|
182
|
-
}
|
|
183
|
-
if( formatedToolParts[currentPartIndexInFormatedParts - 1]?.toolInfo.toolName === part.toolInfo.toolName) {
|
|
184
|
-
continue;
|
|
185
|
-
} else if ( formatedToolParts[currentPartIndexInFormatedParts + 1]?.toolInfo.toolName === part.toolInfo.toolName) {
|
|
186
|
-
groupedParts[groupedParts.length - 1].groupedTools.push(formatedToolParts[currentPartIndexInFormatedParts + 1] as IPart);
|
|
187
|
-
} else {
|
|
188
|
-
groupedParts[groupedParts.length - 1].groupedTools.push(part);
|
|
189
|
-
}
|
|
190
|
-
}
|
|
191
|
-
return groupedParts;
|
|
192
|
-
}
|
|
193
|
-
|
|
194
|
-
const props = defineProps<{
|
|
195
|
-
messages: IMessage[]
|
|
196
|
-
}>();
|
|
197
|
-
|
|
198
|
-
</script>
|
|
@@ -1,67 +0,0 @@
|
|
|
1
|
-
<template>
|
|
2
|
-
<template v-for="group in props.toolGroup" :key="group.title">
|
|
3
|
-
<div v-if="group.groupedTools.length > 1" class="flex flex-col">
|
|
4
|
-
<div class="flex items-center gap-2 px-2 m-2 cursor-pointer hover:opacity-75 break-all font-mono text-sm leading-5 text-lightListTableHeadingText dark:text-darkListTableHeadingText" @click="toggleGroup(group.title)">
|
|
5
|
-
<IconCheckOutline class="w-6 h-6 p-1"/>
|
|
6
|
-
{{ group.title }} {{ 'x' + group.groupedTools.length }}
|
|
7
|
-
<IconAngleDownOutline
|
|
8
|
-
class="transition-transform duration-200 hover:scale-105 hover:opacity-75"
|
|
9
|
-
:class="expandedGroups.includes(group.title) ? 'rotate-180' : 'rotate-0'"
|
|
10
|
-
/>
|
|
11
|
-
</div>
|
|
12
|
-
<transition name="expand">
|
|
13
|
-
<div v-show="expandedGroups.includes(group.title)" class="flex flex-col">
|
|
14
|
-
<ToolRenderer v-for="part in group.groupedTools" :key="part.text + part.type" :data="part" class="ml-8"/>
|
|
15
|
-
</div>
|
|
16
|
-
</transition>
|
|
17
|
-
</div>
|
|
18
|
-
<ToolRenderer v-else-if="group.groupedTools.length > 0" :data="group.groupedTools[0]" />
|
|
19
|
-
</template>
|
|
20
|
-
|
|
21
|
-
</template>
|
|
22
|
-
|
|
23
|
-
<script setup lang="ts">
|
|
24
|
-
import ToolRenderer from './ToolRenderer.vue';
|
|
25
|
-
import type { IPart } from './types';
|
|
26
|
-
import { ref } from 'vue';
|
|
27
|
-
import { IconAngleDownOutline, IconCheckOutline } from '@iconify-prerendered/vue-flowbite';
|
|
28
|
-
|
|
29
|
-
const props = defineProps<{
|
|
30
|
-
toolGroup: {
|
|
31
|
-
title: string;
|
|
32
|
-
groupedTools: IPart[];
|
|
33
|
-
}[]
|
|
34
|
-
}>();
|
|
35
|
-
|
|
36
|
-
const expandedGroups = ref<string[]>([]);
|
|
37
|
-
|
|
38
|
-
function toggleGroup(groupTitle: string) {
|
|
39
|
-
if (expandedGroups.value.includes(groupTitle)) {
|
|
40
|
-
expandedGroups.value = expandedGroups.value.filter((title: string) => title !== groupTitle);
|
|
41
|
-
} else {
|
|
42
|
-
expandedGroups.value.push(groupTitle);
|
|
43
|
-
}
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
</script>
|
|
47
|
-
|
|
48
|
-
<style scoped>
|
|
49
|
-
|
|
50
|
-
.expand-enter-active,
|
|
51
|
-
.expand-leave-active {
|
|
52
|
-
transition: all 0.3s ease;
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
.expand-enter-from,
|
|
56
|
-
.expand-leave-to {
|
|
57
|
-
opacity: 0;
|
|
58
|
-
max-height: 0;
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
.expand-enter-to,
|
|
62
|
-
.expand-leave-from {
|
|
63
|
-
opacity: 1;
|
|
64
|
-
max-height: 288px;
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
</style>
|