Skip to content

Commit deea96e

Browse files
committed
2 parents a7addbe + 3525d9f commit deea96e

11 files changed

Lines changed: 70 additions & 25 deletions

File tree

agent/simpleAgent.ts

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -165,7 +165,6 @@ function createAgentLlmMetricsLogger() {
165165
export function createAgentChatModel(params: {
166166
adapter: CompletionAdapter;
167167
maxTokens: number;
168-
modelName?: string;
169168
}) {
170169
const adapter = params.adapter as OpenAIBackedCompletionAdapter;
171170
const options = adapter.options ?? {};
@@ -176,7 +175,7 @@ export function createAgentChatModel(params: {
176175
);
177176
}
178177

179-
const model = params.modelName ?? options.model ?? "gpt-5-nano";
178+
const model = options.model ?? "gpt-5-nano";
180179
const baseURL = options.baseURL ?? options.baseUrl;
181180
const reasoning = options.extraRequestBodyParameters
182181
?.reasoning as OpenAiReasoningConfig | undefined;
@@ -195,8 +194,8 @@ export function createAgentChatModel(params: {
195194
useResponsesApi: true,
196195
outputVersion: "v1",
197196
streaming: true,
198-
promptCacheKey: `adminforth-agent:${model}:system-v1:tools-v1`,
199197

198+
promptCacheKey: `adminforth-agent:${model}:system-v1:tools-v1`,
200199
promptCacheRetention: "in_memory",
201200

202201
...(reasoningConfig ? { reasoning: reasoningConfig } : {}),

custom/ChatSurface.vue

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@
2626
:style="{ width: agentStore.chatWidth + 'rem' }"
2727
>
2828
<div
29-
v-if="!coreStore.isMobile"
29+
v-if="!(coreStore.isMobile || agentStore.isFullScreen)"
3030
class="w-2 cursor-ew-resize absolute left-0 top-0 h-full z-30"
3131
@mousedown="startResize"
3232
></div>
@@ -36,7 +36,6 @@
3636
>
3737
<div
3838
class="flex items-center justify-between h-14 border-b border-gray-200 dark:border-gray-700"
39-
:class="{ 'pl-4': agentStore.isFullScreen }"
4039
>
4140
<div
4241
class="flex items-center"
@@ -248,8 +247,11 @@ onMounted(async () => {
248247
agentStore.regisrerTextInput(textInput.value);
249248
textInput.value?.focus();
250249
const isTeleportedToBodyFromLocalStorage = agentStore.getLocalStorageItem('isTeleportedToBody') === 'true';
251-
252-
agentStore.setIsTeleportedToBody(isTeleportedToBodyFromLocalStorage || props.meta.stickByDefault);
250+
if( coreStore.isMobile ) {
251+
agentStore.setIsTeleportedToBody(false);
252+
} else {
253+
agentStore.setIsTeleportedToBody(isTeleportedToBodyFromLocalStorage || props.meta.stickByDefault);
254+
}
253255
await agentStore.fetchSessionsList();
254256
});
255257

custom/ConversationArea.vue

Lines changed: 24 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -37,9 +37,8 @@
3737
class="flex flex-col w-full"
3838
:class="message.role === 'user' ? 'self-end' : 'self-start'"
3939
>
40-
<ToolsGroup :toolGroup="groupToolCallParts(message)" />
4140
<template
42-
v-for="part in getParts(message)"
41+
v-for="(part, index) in getParts(message)"
4342
:key="part.type"
4443
>
4544
<Message
@@ -52,6 +51,7 @@
5251
@toggle-thoughts="() => clicks++"
5352
>
5453
</Message>
54+
<ToolsGroup v-else :toolGroup="groupToolCallParts(message, index, part)" />
5555
</template>
5656
</div>
5757
<!-- Show a placeholder message if the last message is not of type 'text' or 'reasoning' -->
@@ -154,27 +154,39 @@ const formatToolCallTextPart = ((part: IPart, currentMessage: IMessage) => {
154154
return null;
155155
});
156156
157-
const groupToolCallParts = (message: IMessage) => {
158-
const groupedParts = [];
159-
let currentToolName = null;
157+
const groupToolCallParts = (message: IMessage, currentPartIndex: number, currentPart: IPart) => {
158+
const groupedParts: { title: string; groupedTools: IPart[] }[] = [];
159+
let currentToolName: string | null = null;
160160
const parts = getParts(message);
161161
if (!parts) return [];
162162
const formatedToolParts = parts.map(part => {
163163
return formatToolCallTextPart(part as IPart, message)
164164
});
165+
const currentPartIndexInFormatedParts = formatedToolParts.findIndex(part => part?.toolInfo?.toolCallId === currentPart.data?.toolCallId);
166+
if (currentPartIndexInFormatedParts === -1) {
167+
return [];
168+
}
165169
for( const[index, part] of formatedToolParts.entries()){
166-
if(!part?.toolInfo) {
170+
if ( index < currentPartIndexInFormatedParts - 1 ) {
167171
continue;
168172
}
169-
if (part.toolInfo.toolName === currentToolName) {
170-
groupedParts[groupedParts.length - 1].groupedTools.push(part);
173+
if(!part || !part.toolInfo) {
171174
continue;
172175
}
173176
currentToolName = part.toolInfo.toolName;
174-
groupedParts.push({
175-
title: currentToolName,
176-
groupedTools: [part]
177-
});
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+
}
178190
}
179191
return groupedParts;
180192
}

custom/Message.vue

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,10 +58,13 @@
5858
import { useRouter } from 'vue-router';
5959
import { IconAngleDownOutline } from '@iconify-prerendered/vue-flowbite';
6060
import { useAgentStore } from './composables/useAgentStore';
61+
import { useCoreStore } from '@/stores/core';
62+
6163
const IncremarkContent = defineAsyncComponent(() => import('@incremark/vue').then(module => module.IncremarkContent))
6264
const ShikiCodeBlock = defineAsyncComponent(() => import('./incremark_code_renderers/IncremarkShikiCodeBlock.vue'))
6365
6466
const agentStore = useAgentStore();
67+
const coreStore = useCoreStore();
6568
6669
const incremarkComponents = {
6770
code: ShikiCodeBlock,
@@ -134,8 +137,10 @@
134137
135138
const internalRoute = resolveInternalRoute(href);
136139
if (internalRoute !== null) {
137-
if (agentStore.isFullScreen) {
140+
if (agentStore.isFullScreen && !coreStore.isMobile) {
138141
agentStore.setFullScreen(false);
142+
} else if (coreStore.isMobile) {
143+
agentStore.setIsChatOpen(false);
139144
}
140145
void router.push(internalRoute);
141146
return;

custom/SessionsHistory.vue

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,6 @@
3333
:class="{
3434
'bg-lightPrimary/20 hover:bg-lightPrimary/20 dark:bg-darkPrimary/20 dark:hover:bg-darkPrimary/20': agentStore.activeSessionId === session.sessionId,
3535
'cursor-default opacity-50 pointer-events-none': agentStore.isResponseInProgress,
36-
'pl-8': agentStore.isFullScreen
3736
}"
3837
@click="agentStore.setActiveSession(session.sessionId); agentStore.setSessionHistoryOpen(false);"
3938
:disabled="agentStore.isResponseInProgress"

custom/ToolRenderer.vue

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,12 +31,12 @@
3131
:key="section.label"
3232
class="overflow-hidden rounded-xl border border-black/5 bg-white/70 shadow-sm backdrop-blur-sm dark:border-white/10 dark:bg-slate-900/50"
3333
>
34-
<header class="border-b border-black/5 px-3 py-2 text-[11px] font-semibold uppercase tracking-[0.22em] text-gray-500 dark:border-white/10 dark:text-gray-400">
34+
<header class="border-b border-black/5 px-3 py-2 text-[0.7rem] font-semibold uppercase tracking-[0.22em] text-gray-500 dark:border-white/10 dark:text-gray-400">
3535
{{ section.label }}
3636
</header>
3737
<div class="grid grid-cols-[auto,1fr] gap-x-3 gap-y-1 px-3 py-3 font-mono text-xs leading-5 text-gray-700 dark:text-gray-200">
3838
<template v-for="line in section.lines" :key="`${section.label}-${line.number}`">
39-
<span class="select-none text-[11px] text-gray-400 dark:text-gray-500">{{ line.number }}</span>
39+
<span class="select-none text-[0.7rem] text-gray-400 dark:text-gray-500">{{ line.number }}</span>
4040
<span class="whitespace-pre-wrap break-words">{{ line.content || ' ' }}</span>
4141
</template>
4242
</div>

custom/ToolsGroup.vue

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
</div>
1616
</transition>
1717
</div>
18-
<ToolRenderer v-else :data="group.groupedTools[0]" />
18+
<ToolRenderer v-else-if="group.groupedTools.length > 0" :data="group.groupedTools[0]" />
1919
</template>
2020

2121
</template>

custom/composables/useAgentStore.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -129,12 +129,14 @@ export const useAgentStore = defineStore('agent', () => {
129129
function setFullScreen(fullScreen: boolean) {
130130
isFullScreen.value = fullScreen;
131131
if (fullScreen) {
132+
document.body.style.overflow = 'hidden';
132133
setLocalStorageItem('chatWidthBeforeFullScreen', chatWidth.value.toString());
133134
setLocalStorageItem('isTeleportedToBodyBeforeFullScreen', isTeleportedToBody.value ? 'true' : 'false');
134135
setIsTeleportedToBody(false);
135136
useAgentTransitions().setChatSurfaceTransition(false);
136137
setChatWidth(window.innerWidth, false);
137138
} else {
139+
document.body.style.overflow = '';
138140
const lastChatWidth = parseInt(getLocalStorageItem('chatWidthBeforeFullScreen') || DEFAULT_CHAT_WIDTH.toString(), 10);
139141
const isTeleportedBeforeFullScreen = getLocalStorageItem('isTeleportedToBodyBeforeFullScreen') === 'true';
140142
agentTransitions.setAppRootTransition(true);
@@ -315,6 +317,9 @@ export const useAgentStore = defineStore('agent', () => {
315317
}
316318

317319
function closeChat() {
320+
if(isFullScreen.value) {
321+
document.body.style.overflow = '';
322+
}
318323
if (blockCloseOfChat.value) {
319324
return;
320325
}
@@ -323,6 +328,12 @@ export const useAgentStore = defineStore('agent', () => {
323328
}
324329

325330
function openChat() {
331+
if (isFullScreen.value) {
332+
document.body.style.overflow = 'hidden';
333+
}
334+
if (coreStore.isMobile) {
335+
setFullScreen(true);
336+
}
326337
isChatOpen.value = true;
327338
nextTick(() => {
328339
focusTextInput();

custom/skills/fetch_data/SKILL.md

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,18 @@ You can use tool `get_resource_data` it returns one or more records and is capab
1010

1111
To find specific data record you should use filters. ILIKE filters are preferred when we are unsure the input is clear.You can combine filters with OR if you want to search multiple fields.If user queries one record you should try to fetch up to 5 records and if more then one returned return output them all to user and ask to select one. When you communicate about record with user, show its several most important fields.
1212

13+
Every record summary must be based on one exact row returned by `get_resource_data`.
14+
15+
Never combine model/title/name, `_label`, primary key, link, or any field values from different rows.
16+
17+
Never combine fields from different resources in one candidate.
18+
19+
If results come from multiple resources, present them as separate groups with the resource label or resourceId.
20+
21+
If several rows look similar, do not guess which one is "the same" record. Show them separately and ask user to choose.
22+
1323
For long texts show only several first words and add "..." at the end (only if user did not request this field specifically).
1424

1525
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`.
26+
27+
Before sending the link, verify that the `resourceId`, `{primary key}`, `_label`, and shown fields come from the same exact returned row.

custom/skills/mutate_data/SKILL.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,9 +20,13 @@ Use `start_custom_action` and `start_custom_bulk_action` for resource actions.
2020

2121
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).
2222

23+
Every confirmation must describe one exact fetched row. Never combine `_label`, primary key, link, or field values from different rows or different resources in one confirmation.
24+
2325
For field values with long texts show only several first words and add "..." at the end.
2426
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`.
2527

28+
Before sending the confirmation, verify that the `resourceId`, `{primary key}`, `_label`, and all shown fields come from the same exact fetched row.
29+
2630
And in the same message ask user for final confirmation.
2731

2832
When creating new record, show user all data which you gona create and in same message ask for confirmation.

0 commit comments

Comments
 (0)