Skip to content

Commit 6778937

Browse files
committed
fix: implement edit of steeres messages (only loaded from backend)
AdminForth/1786/ability-to-edit-agent-prompt-a
1 parent 328792d commit 6778937

6 files changed

Lines changed: 54 additions & 24 deletions

File tree

custom/ChatFooter.vue

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -215,9 +215,11 @@ async function sendMessage() {
215215
// While editing a message, the same send action commits the edit (regenerating
216216
// from that turn) instead of starting/steering a turn.
217217
if (agentStore.isEditingMessage) {
218-
await agentStore.submitEditMessage();
219-
autoResize();
220-
props.conversationAreaRef?.handleSendMessage();
218+
const editSubmitted = await agentStore.submitEditMessage();
219+
if (editSubmitted) {
220+
autoResize();
221+
props.conversationAreaRef?.handleSendMessage();
222+
}
221223
return;
222224
}
223225
// Routes to a steer (folded into the running turn) while generating, or a normal

custom/composables/agentStore/useAgentChat.ts

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -100,12 +100,14 @@ export function createAgentChatManager({
100100
}
101101
for (let i = messages.length - 1; i >= 0; i--) {
102102
const message = messages[i];
103-
if (message.role === 'user' && !message.metadata?.steer) {
103+
if (message.role === 'user') {
104104
messages.splice(i, 1, {
105105
...message,
106106
metadata: { ...(message.metadata ?? {}), turnId },
107107
});
108-
return;
108+
if (!message.metadata?.steer) {
109+
return;
110+
}
109111
}
110112
}
111113
}
@@ -310,10 +312,6 @@ export function createAgentChatManager({
310312
if (!chat) {
311313
return;
312314
}
313-
const currentMessage = chat.messages.find((m: any) => m.id === messageId);
314-
if (!currentMessage || currentMessage.parts[0].text.trim() === text.trim()) {
315-
return;
316-
}
317315
await stopActiveTurnAndWaitForIdle();
318316
editingTurnId.value = turnId;
319317
lastMessage.value = text;

custom/composables/agentStore/useAgentSteerQueue.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@ export function createAgentSteerQueue({
5555
return;
5656
}
5757
const steerMessage: IMessage = {
58+
id: crypto.randomUUID(),
5859
role: 'user',
5960
metadata: { steer: true },
6061
parts: [{ type: 'text', text, state: 'done' }],

custom/composables/useAgentStore.ts

Lines changed: 24 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ import { createAgentChatManager } from './agentStore/useAgentChat';
1919
import { createAgentPlaceholderController } from './agentStore/useAgentPlaceholder';
2020
import { createAgentSessionManager } from './agentStore/useAgentSessions';
2121
import { createAgentSteerQueue } from './agentStore/useAgentSteerQueue';
22+
import { STEER_PERSIST_PREFIX } from './agentStore/constants';
2223

2324
export const useAgentStore = defineStore('agent', () => {
2425
const agentTransitions = useAgentTransitions();
@@ -465,6 +466,19 @@ export const useAgentStore = defineStore('agent', () => {
465466
editingEnabled.value = enabled;
466467
}
467468

469+
470+
function getCompiledMessageTextWithSteeredParts(message: any): string {
471+
const currentTurnUserMessages = currentChat.value?.messages.filter((msg: any) => msg.metadata?.turnId === message?.metadata?.turnId && msg.role === 'user');
472+
let messageToCompile = '';
473+
for(const msg of currentTurnUserMessages) {
474+
const text = (msg.parts ?? [])
475+
.filter((part: any) => part.type === 'text')
476+
.map((part: any) => part.text ?? '')
477+
.join('');
478+
messageToCompile += text + ' ';
479+
}
480+
return messageToCompile.trim();
481+
}
468482
// Enter edit mode for a user message: stash the current input, then load the
469483
// message's text into the textarea for the user to amend. Allowed mid-generation —
470484
// committing the edit stops the running turn (see submitEditMessage).
@@ -480,10 +494,7 @@ export const useAgentStore = defineStore('agent', () => {
480494
if (editingMessageId.value === null) {
481495
savedUserInput.value = userMessageInput.value ?? '';
482496
}
483-
const text = (message.parts ?? [])
484-
.filter((part: any) => part.type === 'text')
485-
.map((part: any) => part.text ?? '')
486-
.join('');
497+
const text = getCompiledMessageTextWithSteeredParts(message);
487498
editingMessageId.value = messageId;
488499
editingMessageTurnId.value = turnId;
489500
userMessageInput.value = text;
@@ -506,20 +517,25 @@ export const useAgentStore = defineStore('agent', () => {
506517
nextTick(() => focusTextInput());
507518
}
508519

509-
async function submitEditMessage() {
520+
async function submitEditMessage(): Promise<boolean> {
510521
if (editingMessageId.value === null) {
511-
return;
522+
return false;
512523
}
513524
const text = trimmedUserMessage.value;
514525
if (!text) {
515-
return;
526+
return false;
516527
}
517528
const messageId = editingMessageId.value;
518529
const turnId = editingMessageTurnId.value!;
519530
steerBuffer.clear();
520531
// Restore the stashed input and leave edit mode before the regeneration streams in.
521532
exitEditMode();
533+
const lastMessageText = getCompiledMessageTextWithSteeredParts(currentChat.value?.messages.find((m: any) => m.id === messageId));
534+
if (lastMessageText.trim() === text.trim()) {
535+
return false;
536+
}
522537
await sendEditMessage({ messageId, turnId, text });
538+
return true;
523539
}
524540

525541
function resolveInternalRoute(href: string): string | null {
@@ -612,6 +628,7 @@ export const useAgentStore = defineStore('agent', () => {
612628
setEditingEnabled,
613629
isEditingMessage,
614630
editingMessageId,
631+
editingMessageTurnId,
615632
startEditMessage,
616633
cancelEditMessage,
617634
submitEditMessage,

custom/conversation_area/MessageRenderer.vue

Lines changed: 18 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@
4141
</div>
4242
</Transition>
4343
<div
44-
v-if="message.role === 'user' && message.metadata?.steer"
44+
v-if="message.role === 'user' && message.metadata?.steer && !isBeingEditedTurn"
4545
class="self-end mr-4 mb-0.5 text-xs italic text-gray-400 dark:text-gray-500"
4646
>
4747
{{ $t('Steering instruction') }}
@@ -55,10 +55,10 @@
5555
:key="part.type"
5656
>
5757
<TextRenderer
58-
v-if="part.type === 'text' && !checkIfMessageSystemMessage(part.text ?? '')"
59-
:message="part.text"
58+
v-if="part.type === 'text' && !checkIfMessageSystemMessage(part.text ?? '') && shouldBeShownWhileEditing"
59+
:message="isBeingEditedMessage ? agentStore.userMessageInput : part.text"
6060
:role="props.message.role"
61-
:highlight="isBeingEdited"
61+
:editMode="isBeingEditedMessage"
6262
:state="part.state ?? (props.message.role === 'user' ? 'done' : undefined)"
6363
/>
6464
<ToolApprovalRenderer
@@ -105,15 +105,27 @@
105105
const canEdit = computed(() =>
106106
agentStore.editingEnabled
107107
&& props.message.role === 'user'
108-
&& !props.message.metadata?.steer
108+
// && !props.message.metadata?.steer
109109
&& Boolean(props.message.metadata?.turnId)
110110
&& !agentStore.isEditingMessage
111111
);
112112
113-
const isBeingEdited = computed(() =>
113+
const isBeingEditedMessage = computed(() =>
114114
Boolean(props.message.id) && props.message.id === agentStore.editingMessageId
115115
);
116116
117+
const isBeingEditedTurn = computed(() =>
118+
Boolean(agentStore.editingMessageTurnId && props.message?.metadata?.turnId === agentStore.editingMessageTurnId)
119+
);
120+
121+
const isUserMessage = computed(() => props.message.role === 'user');
122+
123+
// While editing, hide only the *other* user messages of the edited turn (its steer
124+
// sub-messages); the edited message itself and all non-user parts stay visible.
125+
const shouldBeShownWhileEditing = computed(() =>
126+
!(isBeingEditedTurn.value && isUserMessage.value && !isBeingEditedMessage.value)
127+
);
128+
117129
function checkIfMessageSystemMessage(message: IMessage): boolean {
118130
const isReserved = Object.values(RESERVED_SYSTEM_MESSAGE_CONTENT).includes(message as RESERVED_SYSTEM_MESSAGE_CONTENT);
119131
return isReserved;

custom/conversation_area/TextRenderer.vue

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
hasVegaLite ? 'w-full my-2' : 'm-2',
77
props.role === 'user' ? 'bg-lightListTableHeading dark:bg-darkListTableHeading self-end max-w-[80%] mr-4'
88
: 'border-none self-start max-w-full',
9-
props.highlight ? 'ring-2 ring-lightPrimary dark:ring-darkPrimary w-full max-w-[calc(100%-2rem)]' : ''
9+
props.editMode ? 'ring-2 ring-lightPrimary dark:ring-darkPrimary w-full max-w-[calc(100%-2rem)]' : ''
1010
]"
1111
>
1212
<IncremarkContent
@@ -36,7 +36,7 @@
3636
message: string | undefined,
3737
state: string | undefined,
3838
role: IMessage['role'],
39-
highlight?: boolean
39+
editMode?: boolean
4040
}>();
4141
4242
const emit = defineEmits(['toggle-thoughts']);

0 commit comments

Comments
 (0)