@adminforth/agent 1.27.2 → 1.27.3
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 +2 -2
- package/custom/SessionsHistory.vue +9 -9
- package/custom/conversation_area/ConversationArea.vue +50 -54
- package/custom/conversation_area/ProcessingTimeline.vue +20 -24
- package/custom/conversation_area/ReasoningRenderer.vue +3 -6
- package/custom/conversation_area/TextRenderer.vue +23 -26
- package/custom/conversation_area/ToolRenderer.vue +44 -50
- package/custom/conversation_area/ToolsGroup.vue +1 -13
- package/custom/incremark_code_renderers/IncremarkShikiCodeBlock.vue +1 -1
- package/dist/custom/SessionsHistory.vue +9 -9
- package/dist/custom/conversation_area/ConversationArea.vue +50 -54
- package/dist/custom/conversation_area/ProcessingTimeline.vue +20 -24
- package/dist/custom/conversation_area/ReasoningRenderer.vue +3 -6
- package/dist/custom/conversation_area/TextRenderer.vue +23 -26
- package/dist/custom/conversation_area/ToolRenderer.vue +44 -50
- package/dist/custom/conversation_area/ToolsGroup.vue +1 -13
- package/dist/custom/incremark_code_renderers/IncremarkShikiCodeBlock.vue +1 -1
- package/package.json +1 -1
package/build.log
CHANGED
|
@@ -38,5 +38,5 @@ custom/skills/fetch_data/SKILL.md
|
|
|
38
38
|
custom/skills/mutate_data/
|
|
39
39
|
custom/skills/mutate_data/SKILL.md
|
|
40
40
|
|
|
41
|
-
sent
|
|
42
|
-
total size is
|
|
41
|
+
sent 207,560 bytes received 562 bytes 416,244.00 bytes/sec
|
|
42
|
+
total size is 205,246 speedup is 0.99
|
|
@@ -55,18 +55,9 @@ import { useAgentStore } from './composables/useAgentStore';
|
|
|
55
55
|
const agentStore = useAgentStore();
|
|
56
56
|
|
|
57
57
|
const h3Style = "text-gray-800 dark:text-gray-200 font-medium text-xl tracking-widest my-2"
|
|
58
|
-
|
|
59
58
|
const dayLabelFormatter = new Intl.DateTimeFormat(undefined, { month: 'short', day: 'numeric' });
|
|
60
59
|
const dayLabelWithYearFormatter = new Intl.DateTimeFormat(undefined, { month: 'short', day: 'numeric', year: 'numeric' });
|
|
61
60
|
|
|
62
|
-
function getLocalDayKey(date: Date) {
|
|
63
|
-
const year = date.getFullYear();
|
|
64
|
-
const month = `${date.getMonth() + 1}`.padStart(2, '0');
|
|
65
|
-
const day = `${date.getDate()}`.padStart(2, '0');
|
|
66
|
-
|
|
67
|
-
return `${year}-${month}-${day}`;
|
|
68
|
-
}
|
|
69
|
-
|
|
70
61
|
const groupedSessions = computed(() => {
|
|
71
62
|
const groups = new Map<string, { dayKey: string; label: string; sessions: ISessionsListItem[] }>();
|
|
72
63
|
|
|
@@ -91,4 +82,13 @@ const groupedSessions = computed(() => {
|
|
|
91
82
|
return Array.from(groups.values());
|
|
92
83
|
});
|
|
93
84
|
|
|
85
|
+
|
|
86
|
+
function getLocalDayKey(date: Date) {
|
|
87
|
+
const year = date.getFullYear();
|
|
88
|
+
const month = `${date.getMonth() + 1}`.padStart(2, '0');
|
|
89
|
+
const day = `${date.getDate()}`.padStart(2, '0');
|
|
90
|
+
|
|
91
|
+
return `${year}-${month}-${day}`;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
94
|
</script>
|
|
@@ -89,9 +89,8 @@ const agentStore = useAgentStore();
|
|
|
89
89
|
const agentTransitions = useAgentTransitions();
|
|
90
90
|
const showScrollContainer = ref(true);
|
|
91
91
|
const chatContainerRef = ref<HTMLElement | null>(null);
|
|
92
|
-
|
|
93
92
|
const messagesRefs = ref<Array<HTMLElement | null>>([]);
|
|
94
|
-
|
|
93
|
+
const useWaitingForHeight = ref(false);
|
|
95
94
|
|
|
96
95
|
/*
|
|
97
96
|
* Whenever user sends a message, it adds a bottom spacer, that takes the remaining height
|
|
@@ -116,15 +115,60 @@ let messageResizeObserver: ResizeObserver | null = null;
|
|
|
116
115
|
let observedLastUserMessageElement: HTMLElement | null = null;
|
|
117
116
|
let observedLastAgentMessageElement: HTMLElement | null = null;
|
|
118
117
|
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
}
|
|
118
|
+
onMounted(async () => {
|
|
119
|
+
messageResizeObserver = new ResizeObserver(() => {
|
|
120
|
+
updateSpacerHeight();
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
await import('@incremark/theme/styles.css')
|
|
124
|
+
await agentStore.fetchPlaceholderMessages()
|
|
125
|
+
await refreshSpacerTracking();
|
|
126
|
+
});
|
|
127
|
+
|
|
128
|
+
onUnmounted(() => {
|
|
129
|
+
if (innerScrollContainerRef.value) {
|
|
130
|
+
innerScrollContainerRef.value.removeEventListener('scroll', recalculateScroll);
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
stopObservingLastMessages();
|
|
134
|
+
messageResizeObserver?.disconnect();
|
|
135
|
+
agentStore.stopPlaceholderAnimation();
|
|
136
|
+
});
|
|
123
137
|
|
|
124
138
|
watch(() => agentStore.activeSessionId, () => {
|
|
125
139
|
resetSpacer();
|
|
126
140
|
});
|
|
127
141
|
|
|
142
|
+
watch(() => agentStore.activeSessionId, async () => {
|
|
143
|
+
showScrollContainer.value = false;
|
|
144
|
+
await nextTick();
|
|
145
|
+
showScrollContainer.value = true;
|
|
146
|
+
await refreshSpacerTracking();
|
|
147
|
+
recalculateScroll();
|
|
148
|
+
});
|
|
149
|
+
|
|
150
|
+
watch(() => props.messages.length, async () => {
|
|
151
|
+
await refreshSpacerTracking();
|
|
152
|
+
});
|
|
153
|
+
|
|
154
|
+
watch(scrollContainer, async () => {
|
|
155
|
+
if (innerScrollContainerRef.value) {
|
|
156
|
+
innerScrollContainerRef.value.removeEventListener('scroll', recalculateScroll);
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
if (scrollContainer.value) {
|
|
160
|
+
innerScrollContainerRef.value = scrollContainer.value.container.scrollEl;
|
|
161
|
+
|
|
162
|
+
innerScrollContainerRef.value.addEventListener('scroll', recalculateScroll);
|
|
163
|
+
await refreshSpacerTracking();
|
|
164
|
+
}
|
|
165
|
+
})
|
|
166
|
+
|
|
167
|
+
function resetSpacer() {
|
|
168
|
+
showBottomSpacer.value = false;
|
|
169
|
+
spacerHeight.value = 0;
|
|
170
|
+
}
|
|
171
|
+
|
|
128
172
|
function getLastMessageElement(role: 'user' | 'assistant') {
|
|
129
173
|
const lastMessageIndex = props.messages.findLastIndex((message: IMessage) => message.role === role);
|
|
130
174
|
return messagesRefs.value[lastMessageIndex] ?? null;
|
|
@@ -156,7 +200,6 @@ async function waitForRealHeight(role: 'user' | 'assistant'): Promise<number> {
|
|
|
156
200
|
});
|
|
157
201
|
}
|
|
158
202
|
|
|
159
|
-
const useWaitingForHeight = ref(false);
|
|
160
203
|
async function updateSpacerHeight() {
|
|
161
204
|
if (!showBottomSpacer.value) {
|
|
162
205
|
return;
|
|
@@ -238,51 +281,4 @@ function recalculateScroll() {
|
|
|
238
281
|
}
|
|
239
282
|
}
|
|
240
283
|
|
|
241
|
-
watch(() => agentStore.activeSessionId, async () => {
|
|
242
|
-
showScrollContainer.value = false;
|
|
243
|
-
await nextTick();
|
|
244
|
-
showScrollContainer.value = true;
|
|
245
|
-
await refreshSpacerTracking();
|
|
246
|
-
recalculateScroll();
|
|
247
|
-
});
|
|
248
|
-
|
|
249
|
-
watch(() => props.messages.length, async () => {
|
|
250
|
-
await refreshSpacerTracking();
|
|
251
|
-
});
|
|
252
|
-
|
|
253
|
-
onMounted(async () => {
|
|
254
|
-
messageResizeObserver = new ResizeObserver(() => {
|
|
255
|
-
updateSpacerHeight();
|
|
256
|
-
});
|
|
257
|
-
|
|
258
|
-
await import('@incremark/theme/styles.css')
|
|
259
|
-
await agentStore.fetchPlaceholderMessages()
|
|
260
|
-
await refreshSpacerTracking();
|
|
261
|
-
});
|
|
262
|
-
|
|
263
|
-
onUnmounted(() => {
|
|
264
|
-
if (innerScrollContainerRef.value) {
|
|
265
|
-
innerScrollContainerRef.value.removeEventListener('scroll', recalculateScroll);
|
|
266
|
-
}
|
|
267
|
-
|
|
268
|
-
stopObservingLastMessages();
|
|
269
|
-
messageResizeObserver?.disconnect();
|
|
270
|
-
agentStore.stopPlaceholderAnimation();
|
|
271
|
-
});
|
|
272
|
-
|
|
273
|
-
watch(scrollContainer, async () => {
|
|
274
|
-
if (innerScrollContainerRef.value) {
|
|
275
|
-
innerScrollContainerRef.value.removeEventListener('scroll', recalculateScroll);
|
|
276
|
-
}
|
|
277
|
-
|
|
278
|
-
if (scrollContainer.value) {
|
|
279
|
-
innerScrollContainerRef.value = scrollContainer.value.container.scrollEl;
|
|
280
|
-
|
|
281
|
-
innerScrollContainerRef.value.addEventListener('scroll', recalculateScroll);
|
|
282
|
-
await refreshSpacerTracking();
|
|
283
|
-
}
|
|
284
|
-
})
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
284
|
</script>
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
class="ml-2 px-4 flex items-center gap-1 cursor-pointer select-none hover:opacity-80 tracking-wide font-medium text-sm text-listTableHeadingText dark:text-darkListTableHeadingText"
|
|
5
5
|
@click="isExpanded = !isExpanded"
|
|
6
6
|
>
|
|
7
|
-
Thoughts
|
|
7
|
+
{{ $t('Thoughts') }}
|
|
8
8
|
<span v-if="thinkingDuration > 0">({{ (thinkingDuration/1000).toFixed(2) }} s)</span>
|
|
9
9
|
<ThreeDotsAnimation v-if="isResponseInProgress || showFakeThinkingMessage" />
|
|
10
10
|
<IconAngleDownOutline
|
|
@@ -59,11 +59,27 @@
|
|
|
59
59
|
const thinkingDuration = ref(0);
|
|
60
60
|
const scrollContainerRef = ref<InstanceType<typeof CustomAutoScrollContainer> | null>(null);
|
|
61
61
|
const innerScrollContainerRef = ref<HTMLElement | null>(null);
|
|
62
|
+
const isExpanded = ref(true);
|
|
63
|
+
const ToolOrReasoningParts = computed(() => {
|
|
64
|
+
return props.message.parts.filter((part: IPart) => part.type === 'data-tool-call' || part.type === 'reasoning');
|
|
65
|
+
});
|
|
66
|
+
const isResponseInProgress = computed(() =>{
|
|
67
|
+
return props.isLastMessageInChat && agentStore.isResponseInProgress;
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
const showFakeThinkingMessage = computed(() => {
|
|
71
|
+
if (props.message.parts.length === 0) return true;
|
|
72
|
+
return false;
|
|
73
|
+
})
|
|
62
74
|
|
|
63
75
|
onMounted(() => {
|
|
64
76
|
thinkingStartTime.value = Date.now();
|
|
65
77
|
})
|
|
66
78
|
|
|
79
|
+
onUnmounted(() => {
|
|
80
|
+
scrollContainerRef.value?.container.scrollEl?.removeEventListener('scroll', handleScroll);
|
|
81
|
+
})
|
|
82
|
+
|
|
67
83
|
watch(scrollContainerRef, async () => {
|
|
68
84
|
if (innerScrollContainerRef.value) {
|
|
69
85
|
innerScrollContainerRef.value.removeEventListener('scroll', handleScroll);
|
|
@@ -75,34 +91,12 @@
|
|
|
75
91
|
}
|
|
76
92
|
})
|
|
77
93
|
|
|
78
|
-
onUnmounted(() => {
|
|
79
|
-
scrollContainerRef.value?.container.scrollEl?.removeEventListener('scroll', handleScroll);
|
|
80
|
-
})
|
|
81
|
-
|
|
82
|
-
function handleScroll() {
|
|
83
|
-
scrollContainerRef.value?.handleScroll();
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
const ToolOrReasoningParts = computed(() => {
|
|
87
|
-
return props.message.parts.filter((part: IPart) => part.type === 'data-tool-call' || part.type === 'reasoning');
|
|
88
|
-
});
|
|
89
|
-
const isExpanded = ref(true);
|
|
90
|
-
|
|
91
|
-
const isResponseInProgress = computed(() =>{
|
|
92
|
-
return props.isLastMessageInChat && agentStore.isResponseInProgress;
|
|
93
|
-
});
|
|
94
|
-
|
|
95
94
|
watch(isResponseInProgress, (newValue: boolean) => {
|
|
96
95
|
if (!newValue) {
|
|
97
96
|
isExpanded.value = false;
|
|
98
97
|
thinkingDuration.value = Date.now() - (thinkingStartTime.value ?? Date.now());
|
|
99
98
|
}
|
|
100
99
|
});
|
|
101
|
-
|
|
102
|
-
const showFakeThinkingMessage = computed(() => {
|
|
103
|
-
if (props.message.parts.length === 0) return true;
|
|
104
|
-
return false;
|
|
105
|
-
})
|
|
106
100
|
|
|
107
101
|
const formatToolCallPart = (part: IPart, currentMessage: IMessage): IFormattedToolCallPart | null => {
|
|
108
102
|
if (part.type !== 'data-tool-call' || part.data?.phase !== 'start') {
|
|
@@ -182,7 +176,9 @@
|
|
|
182
176
|
return groupedParts;
|
|
183
177
|
};
|
|
184
178
|
|
|
185
|
-
|
|
179
|
+
function handleScroll() {
|
|
180
|
+
scrollContainerRef.value?.handleScroll();
|
|
181
|
+
}
|
|
186
182
|
</script>
|
|
187
183
|
|
|
188
184
|
<style scoped>
|
|
@@ -35,17 +35,16 @@ import type { IPart } from '../types';
|
|
|
35
35
|
import { ref, computed, watch, defineAsyncComponent } from 'vue';
|
|
36
36
|
import ThreeDotsAnimation from './ThreeDotsAnimation.vue';
|
|
37
37
|
import { extractTitleAndTextFromReasoning } from '../utils';
|
|
38
|
-
import CustomAutoScrollContainer from '../CustomAutoScrollContainer.vue';
|
|
39
|
-
|
|
40
|
-
const IncremarkContent = defineAsyncComponent(() => import('@incremark/vue').then(module => module.IncremarkContent))
|
|
41
38
|
|
|
42
39
|
const props = defineProps<{
|
|
43
40
|
state?: IPart['state']
|
|
44
41
|
text?: string
|
|
45
42
|
}>();
|
|
46
43
|
|
|
47
|
-
const
|
|
44
|
+
const IncremarkContent = defineAsyncComponent(() => import('@incremark/vue').then(module => module.IncremarkContent))
|
|
45
|
+
|
|
48
46
|
const isExpanded = ref(true);
|
|
47
|
+
const isStreaming = computed(() => props.state === 'streaming');
|
|
49
48
|
const parsedReasoning = computed(() => extractTitleAndTextFromReasoning(props.text ?? ''));
|
|
50
49
|
const reasoningTitle = computed(() => parsedReasoning.value.title ?? '');
|
|
51
50
|
const reasoningText = computed(() => parsedReasoning.value.body);
|
|
@@ -56,8 +55,6 @@ watch(() => props.state, (newValue: IPart['state']) => {
|
|
|
56
55
|
}
|
|
57
56
|
});
|
|
58
57
|
|
|
59
|
-
|
|
60
|
-
|
|
61
58
|
</script>
|
|
62
59
|
|
|
63
60
|
|
|
@@ -17,27 +17,43 @@
|
|
|
17
17
|
:incremark-options="incremarkOptions"
|
|
18
18
|
/>
|
|
19
19
|
<p v-else class="text-red-500 py-2">
|
|
20
|
-
Error occurred
|
|
20
|
+
{{ $t('Error occurred') }}
|
|
21
21
|
</p>
|
|
22
22
|
</div>
|
|
23
23
|
</template>
|
|
24
24
|
|
|
25
|
-
<script setup lang="ts">
|
|
25
|
+
<script setup lang="ts">const isExpanded = ref(true);
|
|
26
|
+
|
|
26
27
|
import { computed, defineAsyncComponent, onMounted, ref, watch } from 'vue';
|
|
27
28
|
import { useRouter } from 'vue-router';
|
|
28
29
|
import { useAgentStore } from '../composables/useAgentStore';
|
|
29
30
|
import { useCoreStore } from '@/stores/core';
|
|
30
31
|
|
|
32
|
+
const props = defineProps<{
|
|
33
|
+
message: string | undefined,
|
|
34
|
+
state: string | undefined,
|
|
35
|
+
role: 'user' | 'assistant'
|
|
36
|
+
}>();
|
|
37
|
+
|
|
38
|
+
const emit = defineEmits(['toggle-thoughts']);
|
|
39
|
+
|
|
31
40
|
const IncremarkContent = defineAsyncComponent(() => import('@incremark/vue').then(module => module.IncremarkContent))
|
|
32
41
|
const ShikiCodeBlock = defineAsyncComponent(() => import('../incremark_code_renderers/IncremarkShikiCodeBlock.vue'))
|
|
33
42
|
|
|
34
43
|
const agentStore = useAgentStore();
|
|
35
44
|
const coreStore = useCoreStore();
|
|
45
|
+
const router = useRouter();
|
|
46
|
+
|
|
47
|
+
const isThoughtsExpanded = ref(true);
|
|
48
|
+
|
|
49
|
+
const content = computed(() => props.message)
|
|
50
|
+
const isFinished = computed(() => props.state === 'done')
|
|
51
|
+
const hasVegaLite = computed(() => props.message?.includes('```vega-lite'))
|
|
52
|
+
const isStateStreaming = computed(() => props.state === 'streaming')
|
|
36
53
|
|
|
37
54
|
const incremarkComponents = {
|
|
38
55
|
code: ShikiCodeBlock,
|
|
39
56
|
};
|
|
40
|
-
|
|
41
57
|
const incremarkOptions = {
|
|
42
58
|
gfm: true,
|
|
43
59
|
math: { tex: true },
|
|
@@ -45,27 +61,10 @@
|
|
|
45
61
|
htmlTree: true,
|
|
46
62
|
};
|
|
47
63
|
|
|
48
|
-
const router = useRouter();
|
|
49
|
-
|
|
50
64
|
onMounted(async () => {
|
|
51
65
|
void import('katex/dist/katex.min.css')
|
|
52
66
|
})
|
|
53
67
|
|
|
54
|
-
const props = defineProps<{
|
|
55
|
-
message: string | undefined,
|
|
56
|
-
state: string | undefined,
|
|
57
|
-
role: 'user' | 'assistant'
|
|
58
|
-
}>();
|
|
59
|
-
|
|
60
|
-
const emit = defineEmits(['toggle-thoughts']);
|
|
61
|
-
|
|
62
|
-
const content = computed(() => props.message)
|
|
63
|
-
const isFinished = computed(() => props.state === 'done')
|
|
64
|
-
const isThoughtsExpanded = ref(true);
|
|
65
|
-
const hasVegaLite = computed(() => props.message?.includes('```vega-lite'))
|
|
66
|
-
|
|
67
|
-
const isStateStreaming = computed(() => props.state === 'streaming')
|
|
68
|
-
|
|
69
68
|
watch(isStateStreaming, (newValue: boolean) => {
|
|
70
69
|
if (!newValue) {
|
|
71
70
|
isThoughtsExpanded.value = false;
|
|
@@ -125,12 +124,6 @@
|
|
|
125
124
|
|
|
126
125
|
</script>
|
|
127
126
|
|
|
128
|
-
<style lang="scss">
|
|
129
|
-
.incremark-paragraph {
|
|
130
|
-
margin: 8px 0;
|
|
131
|
-
}
|
|
132
|
-
</style>
|
|
133
|
-
|
|
134
127
|
<style lang="scss">
|
|
135
128
|
.incremark a.incremark-link,
|
|
136
129
|
.incremark a.incremark-link:visited {
|
|
@@ -173,4 +166,8 @@ a.incremark-link::after {
|
|
|
173
166
|
.incremark-list {
|
|
174
167
|
list-style: disc;
|
|
175
168
|
}
|
|
169
|
+
|
|
170
|
+
.incremark-paragraph {
|
|
171
|
+
margin: 8px 0;
|
|
172
|
+
}
|
|
176
173
|
</style>
|
|
@@ -24,10 +24,6 @@
|
|
|
24
24
|
</div>
|
|
25
25
|
|
|
26
26
|
<div class="min-w-0">
|
|
27
|
-
<!-- <p class="text-xs text-gray-500 dark:text-gray-400 font-bold">
|
|
28
|
-
{{ statusLabel }}
|
|
29
|
-
<span v-if="props.data?.toolInfo?.durationMs" class="text-xs">({{ (props.data.toolInfo.durationMs / 1000).toFixed(2) }}s)</span>
|
|
30
|
-
</p> -->
|
|
31
27
|
<p class="break-all font-mono text-sm leading-5 text-nowrap">
|
|
32
28
|
{{ props.data?.toolInfo?.toolInfo ? props.data.toolInfo.toolInfo : props.data?.toolInfo?.toolName}}
|
|
33
29
|
</p>
|
|
@@ -67,27 +63,57 @@
|
|
|
67
63
|
import { Spinner } from '@/afcl';
|
|
68
64
|
import { IconAngleDownOutline, IconCheckOutline } from '@iconify-prerendered/vue-flowbite';
|
|
69
65
|
|
|
66
|
+
const props = defineProps<{
|
|
67
|
+
data: IFormattedToolCallPart
|
|
68
|
+
}>();
|
|
69
|
+
|
|
70
|
+
interface IToolSection {
|
|
71
|
+
label: string;
|
|
72
|
+
lines: Array<{
|
|
73
|
+
number: number;
|
|
74
|
+
content: string;
|
|
75
|
+
}>;
|
|
76
|
+
}
|
|
77
|
+
|
|
70
78
|
const isInputOutputExpanded = ref(false);
|
|
71
79
|
const activateShrinkedStyle = ref(true);
|
|
72
|
-
const isAnimatingShrinkFinal = ref(false);
|
|
73
80
|
const toolRendererInitialWidth = ref<number | null>(null);
|
|
74
81
|
const toolRendererRef = ref<HTMLElement | null>(null);
|
|
75
82
|
const activateFullWidth = ref(false);
|
|
76
83
|
const blockClicksDuringAnimation = ref(false);
|
|
77
84
|
const ANIMATION_DURATION = 300;
|
|
78
85
|
|
|
86
|
+
const isRunning = computed(() => props.data?.toolInfo?.phase === 'start');
|
|
87
|
+
const toolSections = computed<IToolSection[]>(() => {
|
|
88
|
+
const sections = [
|
|
89
|
+
{
|
|
90
|
+
label: 'Input',
|
|
91
|
+
content: normalizeToolPayload(props.data.toolInfo.input),
|
|
92
|
+
},
|
|
93
|
+
{
|
|
94
|
+
label: 'Output',
|
|
95
|
+
content: normalizeToolPayload(props.data.toolInfo.output),
|
|
96
|
+
},
|
|
97
|
+
];
|
|
98
|
+
|
|
99
|
+
return sections
|
|
100
|
+
.filter((section): section is { label: string; content: string } => Boolean(section.content))
|
|
101
|
+
.map(section => ({
|
|
102
|
+
label: section.label,
|
|
103
|
+
lines: section.content.split('\n').map((content, index) => ({
|
|
104
|
+
number: index + 1,
|
|
105
|
+
content,
|
|
106
|
+
})),
|
|
107
|
+
}));
|
|
108
|
+
});
|
|
109
|
+
const hasToolSections = computed(() => toolSections.value.length > 0);
|
|
110
|
+
|
|
79
111
|
onMounted(() => {
|
|
80
112
|
if (toolRendererRef.value) {
|
|
81
113
|
toolRendererInitialWidth.value = toolRendererRef.value.offsetWidth;
|
|
82
114
|
}
|
|
83
115
|
});
|
|
84
116
|
|
|
85
|
-
function finishTransition() {
|
|
86
|
-
if (!isInputOutputExpanded.value) {
|
|
87
|
-
activateFullWidth.value = false;
|
|
88
|
-
activateShrinkedStyle.value = true;
|
|
89
|
-
}
|
|
90
|
-
}
|
|
91
117
|
watch(isInputOutputExpanded, (newValue) => {
|
|
92
118
|
if (newValue) {
|
|
93
119
|
activateShrinkedStyle.value = false;
|
|
@@ -95,6 +121,13 @@
|
|
|
95
121
|
}
|
|
96
122
|
});
|
|
97
123
|
|
|
124
|
+
function finishTransition() {
|
|
125
|
+
if (!isInputOutputExpanded.value) {
|
|
126
|
+
activateFullWidth.value = false;
|
|
127
|
+
activateShrinkedStyle.value = true;
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
|
|
98
131
|
function toggleInputOutput() {
|
|
99
132
|
if (blockClicksDuringAnimation.value) return;
|
|
100
133
|
isInputOutputExpanded.value = !isInputOutputExpanded.value;
|
|
@@ -104,21 +137,6 @@
|
|
|
104
137
|
}, ANIMATION_DURATION);
|
|
105
138
|
}
|
|
106
139
|
|
|
107
|
-
interface IToolSection {
|
|
108
|
-
label: string;
|
|
109
|
-
lines: Array<{
|
|
110
|
-
number: number;
|
|
111
|
-
content: string;
|
|
112
|
-
}>;
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
const props = defineProps<{
|
|
116
|
-
data: IFormattedToolCallPart
|
|
117
|
-
}>();
|
|
118
|
-
|
|
119
|
-
const isRunning = computed(() => props.data?.toolInfo?.phase === 'start');
|
|
120
|
-
const statusLabel = computed(() => isRunning.value ? 'Running tool' : 'Tool finished');
|
|
121
|
-
|
|
122
140
|
const normalizeToolPayload = (value: unknown): string | null => {
|
|
123
141
|
if (value === null || value === undefined || value === '') {
|
|
124
142
|
return null;
|
|
@@ -131,30 +149,6 @@
|
|
|
131
149
|
return JSON.stringify(value, null, 2);
|
|
132
150
|
};
|
|
133
151
|
|
|
134
|
-
const toolSections = computed<IToolSection[]>(() => {
|
|
135
|
-
const sections = [
|
|
136
|
-
{
|
|
137
|
-
label: 'Input',
|
|
138
|
-
content: normalizeToolPayload(props.data.toolInfo.input),
|
|
139
|
-
},
|
|
140
|
-
{
|
|
141
|
-
label: 'Output',
|
|
142
|
-
content: normalizeToolPayload(props.data.toolInfo.output),
|
|
143
|
-
},
|
|
144
|
-
];
|
|
145
|
-
|
|
146
|
-
return sections
|
|
147
|
-
.filter((section): section is { label: string; content: string } => Boolean(section.content))
|
|
148
|
-
.map(section => ({
|
|
149
|
-
label: section.label,
|
|
150
|
-
lines: section.content.split('\n').map((content, index) => ({
|
|
151
|
-
number: index + 1,
|
|
152
|
-
content,
|
|
153
|
-
})),
|
|
154
|
-
}));
|
|
155
|
-
});
|
|
156
|
-
|
|
157
|
-
const hasToolSections = computed(() => toolSections.value.length > 0);
|
|
158
152
|
</script>
|
|
159
153
|
|
|
160
154
|
|
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
<h3
|
|
10
10
|
class="flex items-center mb-1 text-sm my-2 ml-3 gap-1 text-listTableHeadingText dark:text-darkListTableHeadingText"
|
|
11
11
|
>
|
|
12
|
-
<span class="font-semibold select-none ">Call tools</span>
|
|
12
|
+
<span class="font-semibold select-none ">{{ $t('Call tools') }}</span>
|
|
13
13
|
</h3>
|
|
14
14
|
<div class="flex flex-wrap">
|
|
15
15
|
<template v-for="group in props.toolGroup" :key="group.title">
|
|
@@ -23,24 +23,12 @@
|
|
|
23
23
|
<script setup lang="ts">
|
|
24
24
|
import ToolRenderer from './ToolRenderer.vue';
|
|
25
25
|
import type { IToolGroup } from '../types';
|
|
26
|
-
import { ref } from 'vue';
|
|
27
26
|
import { IconWrenchSolid } from '@iconify-prerendered/vue-heroicons';
|
|
28
27
|
|
|
29
|
-
|
|
30
28
|
const props = defineProps<{
|
|
31
29
|
toolGroup: IToolGroup[]
|
|
32
30
|
}>();
|
|
33
31
|
|
|
34
|
-
const expandedGroups = ref<string[]>([]);
|
|
35
|
-
|
|
36
|
-
function toggleGroup(groupTitle: string) {
|
|
37
|
-
if (expandedGroups.value.includes(groupTitle)) {
|
|
38
|
-
expandedGroups.value = expandedGroups.value.filter((title: string) => title !== groupTitle);
|
|
39
|
-
} else {
|
|
40
|
-
expandedGroups.value.push(groupTitle);
|
|
41
|
-
}
|
|
42
|
-
}
|
|
43
|
-
|
|
44
32
|
</script>
|
|
45
33
|
|
|
46
34
|
<style scoped>
|
|
@@ -55,18 +55,9 @@ import { useAgentStore } from './composables/useAgentStore';
|
|
|
55
55
|
const agentStore = useAgentStore();
|
|
56
56
|
|
|
57
57
|
const h3Style = "text-gray-800 dark:text-gray-200 font-medium text-xl tracking-widest my-2"
|
|
58
|
-
|
|
59
58
|
const dayLabelFormatter = new Intl.DateTimeFormat(undefined, { month: 'short', day: 'numeric' });
|
|
60
59
|
const dayLabelWithYearFormatter = new Intl.DateTimeFormat(undefined, { month: 'short', day: 'numeric', year: 'numeric' });
|
|
61
60
|
|
|
62
|
-
function getLocalDayKey(date: Date) {
|
|
63
|
-
const year = date.getFullYear();
|
|
64
|
-
const month = `${date.getMonth() + 1}`.padStart(2, '0');
|
|
65
|
-
const day = `${date.getDate()}`.padStart(2, '0');
|
|
66
|
-
|
|
67
|
-
return `${year}-${month}-${day}`;
|
|
68
|
-
}
|
|
69
|
-
|
|
70
61
|
const groupedSessions = computed(() => {
|
|
71
62
|
const groups = new Map<string, { dayKey: string; label: string; sessions: ISessionsListItem[] }>();
|
|
72
63
|
|
|
@@ -91,4 +82,13 @@ const groupedSessions = computed(() => {
|
|
|
91
82
|
return Array.from(groups.values());
|
|
92
83
|
});
|
|
93
84
|
|
|
85
|
+
|
|
86
|
+
function getLocalDayKey(date: Date) {
|
|
87
|
+
const year = date.getFullYear();
|
|
88
|
+
const month = `${date.getMonth() + 1}`.padStart(2, '0');
|
|
89
|
+
const day = `${date.getDate()}`.padStart(2, '0');
|
|
90
|
+
|
|
91
|
+
return `${year}-${month}-${day}`;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
94
|
</script>
|
|
@@ -89,9 +89,8 @@ const agentStore = useAgentStore();
|
|
|
89
89
|
const agentTransitions = useAgentTransitions();
|
|
90
90
|
const showScrollContainer = ref(true);
|
|
91
91
|
const chatContainerRef = ref<HTMLElement | null>(null);
|
|
92
|
-
|
|
93
92
|
const messagesRefs = ref<Array<HTMLElement | null>>([]);
|
|
94
|
-
|
|
93
|
+
const useWaitingForHeight = ref(false);
|
|
95
94
|
|
|
96
95
|
/*
|
|
97
96
|
* Whenever user sends a message, it adds a bottom spacer, that takes the remaining height
|
|
@@ -116,15 +115,60 @@ let messageResizeObserver: ResizeObserver | null = null;
|
|
|
116
115
|
let observedLastUserMessageElement: HTMLElement | null = null;
|
|
117
116
|
let observedLastAgentMessageElement: HTMLElement | null = null;
|
|
118
117
|
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
}
|
|
118
|
+
onMounted(async () => {
|
|
119
|
+
messageResizeObserver = new ResizeObserver(() => {
|
|
120
|
+
updateSpacerHeight();
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
await import('@incremark/theme/styles.css')
|
|
124
|
+
await agentStore.fetchPlaceholderMessages()
|
|
125
|
+
await refreshSpacerTracking();
|
|
126
|
+
});
|
|
127
|
+
|
|
128
|
+
onUnmounted(() => {
|
|
129
|
+
if (innerScrollContainerRef.value) {
|
|
130
|
+
innerScrollContainerRef.value.removeEventListener('scroll', recalculateScroll);
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
stopObservingLastMessages();
|
|
134
|
+
messageResizeObserver?.disconnect();
|
|
135
|
+
agentStore.stopPlaceholderAnimation();
|
|
136
|
+
});
|
|
123
137
|
|
|
124
138
|
watch(() => agentStore.activeSessionId, () => {
|
|
125
139
|
resetSpacer();
|
|
126
140
|
});
|
|
127
141
|
|
|
142
|
+
watch(() => agentStore.activeSessionId, async () => {
|
|
143
|
+
showScrollContainer.value = false;
|
|
144
|
+
await nextTick();
|
|
145
|
+
showScrollContainer.value = true;
|
|
146
|
+
await refreshSpacerTracking();
|
|
147
|
+
recalculateScroll();
|
|
148
|
+
});
|
|
149
|
+
|
|
150
|
+
watch(() => props.messages.length, async () => {
|
|
151
|
+
await refreshSpacerTracking();
|
|
152
|
+
});
|
|
153
|
+
|
|
154
|
+
watch(scrollContainer, async () => {
|
|
155
|
+
if (innerScrollContainerRef.value) {
|
|
156
|
+
innerScrollContainerRef.value.removeEventListener('scroll', recalculateScroll);
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
if (scrollContainer.value) {
|
|
160
|
+
innerScrollContainerRef.value = scrollContainer.value.container.scrollEl;
|
|
161
|
+
|
|
162
|
+
innerScrollContainerRef.value.addEventListener('scroll', recalculateScroll);
|
|
163
|
+
await refreshSpacerTracking();
|
|
164
|
+
}
|
|
165
|
+
})
|
|
166
|
+
|
|
167
|
+
function resetSpacer() {
|
|
168
|
+
showBottomSpacer.value = false;
|
|
169
|
+
spacerHeight.value = 0;
|
|
170
|
+
}
|
|
171
|
+
|
|
128
172
|
function getLastMessageElement(role: 'user' | 'assistant') {
|
|
129
173
|
const lastMessageIndex = props.messages.findLastIndex((message: IMessage) => message.role === role);
|
|
130
174
|
return messagesRefs.value[lastMessageIndex] ?? null;
|
|
@@ -156,7 +200,6 @@ async function waitForRealHeight(role: 'user' | 'assistant'): Promise<number> {
|
|
|
156
200
|
});
|
|
157
201
|
}
|
|
158
202
|
|
|
159
|
-
const useWaitingForHeight = ref(false);
|
|
160
203
|
async function updateSpacerHeight() {
|
|
161
204
|
if (!showBottomSpacer.value) {
|
|
162
205
|
return;
|
|
@@ -238,51 +281,4 @@ function recalculateScroll() {
|
|
|
238
281
|
}
|
|
239
282
|
}
|
|
240
283
|
|
|
241
|
-
watch(() => agentStore.activeSessionId, async () => {
|
|
242
|
-
showScrollContainer.value = false;
|
|
243
|
-
await nextTick();
|
|
244
|
-
showScrollContainer.value = true;
|
|
245
|
-
await refreshSpacerTracking();
|
|
246
|
-
recalculateScroll();
|
|
247
|
-
});
|
|
248
|
-
|
|
249
|
-
watch(() => props.messages.length, async () => {
|
|
250
|
-
await refreshSpacerTracking();
|
|
251
|
-
});
|
|
252
|
-
|
|
253
|
-
onMounted(async () => {
|
|
254
|
-
messageResizeObserver = new ResizeObserver(() => {
|
|
255
|
-
updateSpacerHeight();
|
|
256
|
-
});
|
|
257
|
-
|
|
258
|
-
await import('@incremark/theme/styles.css')
|
|
259
|
-
await agentStore.fetchPlaceholderMessages()
|
|
260
|
-
await refreshSpacerTracking();
|
|
261
|
-
});
|
|
262
|
-
|
|
263
|
-
onUnmounted(() => {
|
|
264
|
-
if (innerScrollContainerRef.value) {
|
|
265
|
-
innerScrollContainerRef.value.removeEventListener('scroll', recalculateScroll);
|
|
266
|
-
}
|
|
267
|
-
|
|
268
|
-
stopObservingLastMessages();
|
|
269
|
-
messageResizeObserver?.disconnect();
|
|
270
|
-
agentStore.stopPlaceholderAnimation();
|
|
271
|
-
});
|
|
272
|
-
|
|
273
|
-
watch(scrollContainer, async () => {
|
|
274
|
-
if (innerScrollContainerRef.value) {
|
|
275
|
-
innerScrollContainerRef.value.removeEventListener('scroll', recalculateScroll);
|
|
276
|
-
}
|
|
277
|
-
|
|
278
|
-
if (scrollContainer.value) {
|
|
279
|
-
innerScrollContainerRef.value = scrollContainer.value.container.scrollEl;
|
|
280
|
-
|
|
281
|
-
innerScrollContainerRef.value.addEventListener('scroll', recalculateScroll);
|
|
282
|
-
await refreshSpacerTracking();
|
|
283
|
-
}
|
|
284
|
-
})
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
284
|
</script>
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
class="ml-2 px-4 flex items-center gap-1 cursor-pointer select-none hover:opacity-80 tracking-wide font-medium text-sm text-listTableHeadingText dark:text-darkListTableHeadingText"
|
|
5
5
|
@click="isExpanded = !isExpanded"
|
|
6
6
|
>
|
|
7
|
-
Thoughts
|
|
7
|
+
{{ $t('Thoughts') }}
|
|
8
8
|
<span v-if="thinkingDuration > 0">({{ (thinkingDuration/1000).toFixed(2) }} s)</span>
|
|
9
9
|
<ThreeDotsAnimation v-if="isResponseInProgress || showFakeThinkingMessage" />
|
|
10
10
|
<IconAngleDownOutline
|
|
@@ -59,11 +59,27 @@
|
|
|
59
59
|
const thinkingDuration = ref(0);
|
|
60
60
|
const scrollContainerRef = ref<InstanceType<typeof CustomAutoScrollContainer> | null>(null);
|
|
61
61
|
const innerScrollContainerRef = ref<HTMLElement | null>(null);
|
|
62
|
+
const isExpanded = ref(true);
|
|
63
|
+
const ToolOrReasoningParts = computed(() => {
|
|
64
|
+
return props.message.parts.filter((part: IPart) => part.type === 'data-tool-call' || part.type === 'reasoning');
|
|
65
|
+
});
|
|
66
|
+
const isResponseInProgress = computed(() =>{
|
|
67
|
+
return props.isLastMessageInChat && agentStore.isResponseInProgress;
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
const showFakeThinkingMessage = computed(() => {
|
|
71
|
+
if (props.message.parts.length === 0) return true;
|
|
72
|
+
return false;
|
|
73
|
+
})
|
|
62
74
|
|
|
63
75
|
onMounted(() => {
|
|
64
76
|
thinkingStartTime.value = Date.now();
|
|
65
77
|
})
|
|
66
78
|
|
|
79
|
+
onUnmounted(() => {
|
|
80
|
+
scrollContainerRef.value?.container.scrollEl?.removeEventListener('scroll', handleScroll);
|
|
81
|
+
})
|
|
82
|
+
|
|
67
83
|
watch(scrollContainerRef, async () => {
|
|
68
84
|
if (innerScrollContainerRef.value) {
|
|
69
85
|
innerScrollContainerRef.value.removeEventListener('scroll', handleScroll);
|
|
@@ -75,34 +91,12 @@
|
|
|
75
91
|
}
|
|
76
92
|
})
|
|
77
93
|
|
|
78
|
-
onUnmounted(() => {
|
|
79
|
-
scrollContainerRef.value?.container.scrollEl?.removeEventListener('scroll', handleScroll);
|
|
80
|
-
})
|
|
81
|
-
|
|
82
|
-
function handleScroll() {
|
|
83
|
-
scrollContainerRef.value?.handleScroll();
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
const ToolOrReasoningParts = computed(() => {
|
|
87
|
-
return props.message.parts.filter((part: IPart) => part.type === 'data-tool-call' || part.type === 'reasoning');
|
|
88
|
-
});
|
|
89
|
-
const isExpanded = ref(true);
|
|
90
|
-
|
|
91
|
-
const isResponseInProgress = computed(() =>{
|
|
92
|
-
return props.isLastMessageInChat && agentStore.isResponseInProgress;
|
|
93
|
-
});
|
|
94
|
-
|
|
95
94
|
watch(isResponseInProgress, (newValue: boolean) => {
|
|
96
95
|
if (!newValue) {
|
|
97
96
|
isExpanded.value = false;
|
|
98
97
|
thinkingDuration.value = Date.now() - (thinkingStartTime.value ?? Date.now());
|
|
99
98
|
}
|
|
100
99
|
});
|
|
101
|
-
|
|
102
|
-
const showFakeThinkingMessage = computed(() => {
|
|
103
|
-
if (props.message.parts.length === 0) return true;
|
|
104
|
-
return false;
|
|
105
|
-
})
|
|
106
100
|
|
|
107
101
|
const formatToolCallPart = (part: IPart, currentMessage: IMessage): IFormattedToolCallPart | null => {
|
|
108
102
|
if (part.type !== 'data-tool-call' || part.data?.phase !== 'start') {
|
|
@@ -182,7 +176,9 @@
|
|
|
182
176
|
return groupedParts;
|
|
183
177
|
};
|
|
184
178
|
|
|
185
|
-
|
|
179
|
+
function handleScroll() {
|
|
180
|
+
scrollContainerRef.value?.handleScroll();
|
|
181
|
+
}
|
|
186
182
|
</script>
|
|
187
183
|
|
|
188
184
|
<style scoped>
|
|
@@ -35,17 +35,16 @@ import type { IPart } from '../types';
|
|
|
35
35
|
import { ref, computed, watch, defineAsyncComponent } from 'vue';
|
|
36
36
|
import ThreeDotsAnimation from './ThreeDotsAnimation.vue';
|
|
37
37
|
import { extractTitleAndTextFromReasoning } from '../utils';
|
|
38
|
-
import CustomAutoScrollContainer from '../CustomAutoScrollContainer.vue';
|
|
39
|
-
|
|
40
|
-
const IncremarkContent = defineAsyncComponent(() => import('@incremark/vue').then(module => module.IncremarkContent))
|
|
41
38
|
|
|
42
39
|
const props = defineProps<{
|
|
43
40
|
state?: IPart['state']
|
|
44
41
|
text?: string
|
|
45
42
|
}>();
|
|
46
43
|
|
|
47
|
-
const
|
|
44
|
+
const IncremarkContent = defineAsyncComponent(() => import('@incremark/vue').then(module => module.IncremarkContent))
|
|
45
|
+
|
|
48
46
|
const isExpanded = ref(true);
|
|
47
|
+
const isStreaming = computed(() => props.state === 'streaming');
|
|
49
48
|
const parsedReasoning = computed(() => extractTitleAndTextFromReasoning(props.text ?? ''));
|
|
50
49
|
const reasoningTitle = computed(() => parsedReasoning.value.title ?? '');
|
|
51
50
|
const reasoningText = computed(() => parsedReasoning.value.body);
|
|
@@ -56,8 +55,6 @@ watch(() => props.state, (newValue: IPart['state']) => {
|
|
|
56
55
|
}
|
|
57
56
|
});
|
|
58
57
|
|
|
59
|
-
|
|
60
|
-
|
|
61
58
|
</script>
|
|
62
59
|
|
|
63
60
|
|
|
@@ -17,27 +17,43 @@
|
|
|
17
17
|
:incremark-options="incremarkOptions"
|
|
18
18
|
/>
|
|
19
19
|
<p v-else class="text-red-500 py-2">
|
|
20
|
-
Error occurred
|
|
20
|
+
{{ $t('Error occurred') }}
|
|
21
21
|
</p>
|
|
22
22
|
</div>
|
|
23
23
|
</template>
|
|
24
24
|
|
|
25
|
-
<script setup lang="ts">
|
|
25
|
+
<script setup lang="ts">const isExpanded = ref(true);
|
|
26
|
+
|
|
26
27
|
import { computed, defineAsyncComponent, onMounted, ref, watch } from 'vue';
|
|
27
28
|
import { useRouter } from 'vue-router';
|
|
28
29
|
import { useAgentStore } from '../composables/useAgentStore';
|
|
29
30
|
import { useCoreStore } from '@/stores/core';
|
|
30
31
|
|
|
32
|
+
const props = defineProps<{
|
|
33
|
+
message: string | undefined,
|
|
34
|
+
state: string | undefined,
|
|
35
|
+
role: 'user' | 'assistant'
|
|
36
|
+
}>();
|
|
37
|
+
|
|
38
|
+
const emit = defineEmits(['toggle-thoughts']);
|
|
39
|
+
|
|
31
40
|
const IncremarkContent = defineAsyncComponent(() => import('@incremark/vue').then(module => module.IncremarkContent))
|
|
32
41
|
const ShikiCodeBlock = defineAsyncComponent(() => import('../incremark_code_renderers/IncremarkShikiCodeBlock.vue'))
|
|
33
42
|
|
|
34
43
|
const agentStore = useAgentStore();
|
|
35
44
|
const coreStore = useCoreStore();
|
|
45
|
+
const router = useRouter();
|
|
46
|
+
|
|
47
|
+
const isThoughtsExpanded = ref(true);
|
|
48
|
+
|
|
49
|
+
const content = computed(() => props.message)
|
|
50
|
+
const isFinished = computed(() => props.state === 'done')
|
|
51
|
+
const hasVegaLite = computed(() => props.message?.includes('```vega-lite'))
|
|
52
|
+
const isStateStreaming = computed(() => props.state === 'streaming')
|
|
36
53
|
|
|
37
54
|
const incremarkComponents = {
|
|
38
55
|
code: ShikiCodeBlock,
|
|
39
56
|
};
|
|
40
|
-
|
|
41
57
|
const incremarkOptions = {
|
|
42
58
|
gfm: true,
|
|
43
59
|
math: { tex: true },
|
|
@@ -45,27 +61,10 @@
|
|
|
45
61
|
htmlTree: true,
|
|
46
62
|
};
|
|
47
63
|
|
|
48
|
-
const router = useRouter();
|
|
49
|
-
|
|
50
64
|
onMounted(async () => {
|
|
51
65
|
void import('katex/dist/katex.min.css')
|
|
52
66
|
})
|
|
53
67
|
|
|
54
|
-
const props = defineProps<{
|
|
55
|
-
message: string | undefined,
|
|
56
|
-
state: string | undefined,
|
|
57
|
-
role: 'user' | 'assistant'
|
|
58
|
-
}>();
|
|
59
|
-
|
|
60
|
-
const emit = defineEmits(['toggle-thoughts']);
|
|
61
|
-
|
|
62
|
-
const content = computed(() => props.message)
|
|
63
|
-
const isFinished = computed(() => props.state === 'done')
|
|
64
|
-
const isThoughtsExpanded = ref(true);
|
|
65
|
-
const hasVegaLite = computed(() => props.message?.includes('```vega-lite'))
|
|
66
|
-
|
|
67
|
-
const isStateStreaming = computed(() => props.state === 'streaming')
|
|
68
|
-
|
|
69
68
|
watch(isStateStreaming, (newValue: boolean) => {
|
|
70
69
|
if (!newValue) {
|
|
71
70
|
isThoughtsExpanded.value = false;
|
|
@@ -125,12 +124,6 @@
|
|
|
125
124
|
|
|
126
125
|
</script>
|
|
127
126
|
|
|
128
|
-
<style lang="scss">
|
|
129
|
-
.incremark-paragraph {
|
|
130
|
-
margin: 8px 0;
|
|
131
|
-
}
|
|
132
|
-
</style>
|
|
133
|
-
|
|
134
127
|
<style lang="scss">
|
|
135
128
|
.incremark a.incremark-link,
|
|
136
129
|
.incremark a.incremark-link:visited {
|
|
@@ -173,4 +166,8 @@ a.incremark-link::after {
|
|
|
173
166
|
.incremark-list {
|
|
174
167
|
list-style: disc;
|
|
175
168
|
}
|
|
169
|
+
|
|
170
|
+
.incremark-paragraph {
|
|
171
|
+
margin: 8px 0;
|
|
172
|
+
}
|
|
176
173
|
</style>
|
|
@@ -24,10 +24,6 @@
|
|
|
24
24
|
</div>
|
|
25
25
|
|
|
26
26
|
<div class="min-w-0">
|
|
27
|
-
<!-- <p class="text-xs text-gray-500 dark:text-gray-400 font-bold">
|
|
28
|
-
{{ statusLabel }}
|
|
29
|
-
<span v-if="props.data?.toolInfo?.durationMs" class="text-xs">({{ (props.data.toolInfo.durationMs / 1000).toFixed(2) }}s)</span>
|
|
30
|
-
</p> -->
|
|
31
27
|
<p class="break-all font-mono text-sm leading-5 text-nowrap">
|
|
32
28
|
{{ props.data?.toolInfo?.toolInfo ? props.data.toolInfo.toolInfo : props.data?.toolInfo?.toolName}}
|
|
33
29
|
</p>
|
|
@@ -67,27 +63,57 @@
|
|
|
67
63
|
import { Spinner } from '@/afcl';
|
|
68
64
|
import { IconAngleDownOutline, IconCheckOutline } from '@iconify-prerendered/vue-flowbite';
|
|
69
65
|
|
|
66
|
+
const props = defineProps<{
|
|
67
|
+
data: IFormattedToolCallPart
|
|
68
|
+
}>();
|
|
69
|
+
|
|
70
|
+
interface IToolSection {
|
|
71
|
+
label: string;
|
|
72
|
+
lines: Array<{
|
|
73
|
+
number: number;
|
|
74
|
+
content: string;
|
|
75
|
+
}>;
|
|
76
|
+
}
|
|
77
|
+
|
|
70
78
|
const isInputOutputExpanded = ref(false);
|
|
71
79
|
const activateShrinkedStyle = ref(true);
|
|
72
|
-
const isAnimatingShrinkFinal = ref(false);
|
|
73
80
|
const toolRendererInitialWidth = ref<number | null>(null);
|
|
74
81
|
const toolRendererRef = ref<HTMLElement | null>(null);
|
|
75
82
|
const activateFullWidth = ref(false);
|
|
76
83
|
const blockClicksDuringAnimation = ref(false);
|
|
77
84
|
const ANIMATION_DURATION = 300;
|
|
78
85
|
|
|
86
|
+
const isRunning = computed(() => props.data?.toolInfo?.phase === 'start');
|
|
87
|
+
const toolSections = computed<IToolSection[]>(() => {
|
|
88
|
+
const sections = [
|
|
89
|
+
{
|
|
90
|
+
label: 'Input',
|
|
91
|
+
content: normalizeToolPayload(props.data.toolInfo.input),
|
|
92
|
+
},
|
|
93
|
+
{
|
|
94
|
+
label: 'Output',
|
|
95
|
+
content: normalizeToolPayload(props.data.toolInfo.output),
|
|
96
|
+
},
|
|
97
|
+
];
|
|
98
|
+
|
|
99
|
+
return sections
|
|
100
|
+
.filter((section): section is { label: string; content: string } => Boolean(section.content))
|
|
101
|
+
.map(section => ({
|
|
102
|
+
label: section.label,
|
|
103
|
+
lines: section.content.split('\n').map((content, index) => ({
|
|
104
|
+
number: index + 1,
|
|
105
|
+
content,
|
|
106
|
+
})),
|
|
107
|
+
}));
|
|
108
|
+
});
|
|
109
|
+
const hasToolSections = computed(() => toolSections.value.length > 0);
|
|
110
|
+
|
|
79
111
|
onMounted(() => {
|
|
80
112
|
if (toolRendererRef.value) {
|
|
81
113
|
toolRendererInitialWidth.value = toolRendererRef.value.offsetWidth;
|
|
82
114
|
}
|
|
83
115
|
});
|
|
84
116
|
|
|
85
|
-
function finishTransition() {
|
|
86
|
-
if (!isInputOutputExpanded.value) {
|
|
87
|
-
activateFullWidth.value = false;
|
|
88
|
-
activateShrinkedStyle.value = true;
|
|
89
|
-
}
|
|
90
|
-
}
|
|
91
117
|
watch(isInputOutputExpanded, (newValue) => {
|
|
92
118
|
if (newValue) {
|
|
93
119
|
activateShrinkedStyle.value = false;
|
|
@@ -95,6 +121,13 @@
|
|
|
95
121
|
}
|
|
96
122
|
});
|
|
97
123
|
|
|
124
|
+
function finishTransition() {
|
|
125
|
+
if (!isInputOutputExpanded.value) {
|
|
126
|
+
activateFullWidth.value = false;
|
|
127
|
+
activateShrinkedStyle.value = true;
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
|
|
98
131
|
function toggleInputOutput() {
|
|
99
132
|
if (blockClicksDuringAnimation.value) return;
|
|
100
133
|
isInputOutputExpanded.value = !isInputOutputExpanded.value;
|
|
@@ -104,21 +137,6 @@
|
|
|
104
137
|
}, ANIMATION_DURATION);
|
|
105
138
|
}
|
|
106
139
|
|
|
107
|
-
interface IToolSection {
|
|
108
|
-
label: string;
|
|
109
|
-
lines: Array<{
|
|
110
|
-
number: number;
|
|
111
|
-
content: string;
|
|
112
|
-
}>;
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
const props = defineProps<{
|
|
116
|
-
data: IFormattedToolCallPart
|
|
117
|
-
}>();
|
|
118
|
-
|
|
119
|
-
const isRunning = computed(() => props.data?.toolInfo?.phase === 'start');
|
|
120
|
-
const statusLabel = computed(() => isRunning.value ? 'Running tool' : 'Tool finished');
|
|
121
|
-
|
|
122
140
|
const normalizeToolPayload = (value: unknown): string | null => {
|
|
123
141
|
if (value === null || value === undefined || value === '') {
|
|
124
142
|
return null;
|
|
@@ -131,30 +149,6 @@
|
|
|
131
149
|
return JSON.stringify(value, null, 2);
|
|
132
150
|
};
|
|
133
151
|
|
|
134
|
-
const toolSections = computed<IToolSection[]>(() => {
|
|
135
|
-
const sections = [
|
|
136
|
-
{
|
|
137
|
-
label: 'Input',
|
|
138
|
-
content: normalizeToolPayload(props.data.toolInfo.input),
|
|
139
|
-
},
|
|
140
|
-
{
|
|
141
|
-
label: 'Output',
|
|
142
|
-
content: normalizeToolPayload(props.data.toolInfo.output),
|
|
143
|
-
},
|
|
144
|
-
];
|
|
145
|
-
|
|
146
|
-
return sections
|
|
147
|
-
.filter((section): section is { label: string; content: string } => Boolean(section.content))
|
|
148
|
-
.map(section => ({
|
|
149
|
-
label: section.label,
|
|
150
|
-
lines: section.content.split('\n').map((content, index) => ({
|
|
151
|
-
number: index + 1,
|
|
152
|
-
content,
|
|
153
|
-
})),
|
|
154
|
-
}));
|
|
155
|
-
});
|
|
156
|
-
|
|
157
|
-
const hasToolSections = computed(() => toolSections.value.length > 0);
|
|
158
152
|
</script>
|
|
159
153
|
|
|
160
154
|
|
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
<h3
|
|
10
10
|
class="flex items-center mb-1 text-sm my-2 ml-3 gap-1 text-listTableHeadingText dark:text-darkListTableHeadingText"
|
|
11
11
|
>
|
|
12
|
-
<span class="font-semibold select-none ">Call tools</span>
|
|
12
|
+
<span class="font-semibold select-none ">{{ $t('Call tools') }}</span>
|
|
13
13
|
</h3>
|
|
14
14
|
<div class="flex flex-wrap">
|
|
15
15
|
<template v-for="group in props.toolGroup" :key="group.title">
|
|
@@ -23,24 +23,12 @@
|
|
|
23
23
|
<script setup lang="ts">
|
|
24
24
|
import ToolRenderer from './ToolRenderer.vue';
|
|
25
25
|
import type { IToolGroup } from '../types';
|
|
26
|
-
import { ref } from 'vue';
|
|
27
26
|
import { IconWrenchSolid } from '@iconify-prerendered/vue-heroicons';
|
|
28
27
|
|
|
29
|
-
|
|
30
28
|
const props = defineProps<{
|
|
31
29
|
toolGroup: IToolGroup[]
|
|
32
30
|
}>();
|
|
33
31
|
|
|
34
|
-
const expandedGroups = ref<string[]>([]);
|
|
35
|
-
|
|
36
|
-
function toggleGroup(groupTitle: string) {
|
|
37
|
-
if (expandedGroups.value.includes(groupTitle)) {
|
|
38
|
-
expandedGroups.value = expandedGroups.value.filter((title: string) => title !== groupTitle);
|
|
39
|
-
} else {
|
|
40
|
-
expandedGroups.value.push(groupTitle);
|
|
41
|
-
}
|
|
42
|
-
}
|
|
43
|
-
|
|
44
32
|
</script>
|
|
45
33
|
|
|
46
34
|
<style scoped>
|