Forking is expressed as "submit from the parent checkpoint of a specific message". Pick a message, read its parent checkpoint, and submit new input from there.
Learn more: For branching-chat and time-travel UX, see the branching chat and time travel documentation.
Two pieces work together:
useMessageMetadata(stream, msgId) — returns { parentCheckpointId } for the given message, or undefined until it loads. See Selectors.submit(input, { forkFrom }) — dispatches a new run whose initial checkpoint is forkFrom, replacing anything that happened after it on the thread.You pick a message, read its parent checkpoint, and submit from there with new input. The new turn becomes the canonical continuation of the thread — old messages after the fork point are superseded.
<script setup lang="ts">
import { computed, type PropType } from "vue";
import { HumanMessage, type BaseMessage } from "@langchain/core/messages";
import { useMessageMetadata, useStreamContext } from "@langchain/vue";
const props = defineProps({
message: { type: Object as PropType<BaseMessage>, required: true },
newContent: { type: String, required: true },
});
const stream = useStreamContext();
const messageId = computed(() => props.message.id);
const metadata = useMessageMetadata(stream, messageId);
function saveEdit() {
const forkFrom = metadata.value?.parentCheckpointId;
if (!forkFrom) return;
void stream.submit({ messages: [new HumanMessage(props.newContent)] }, { forkFrom });
}
</script>
<template>
<button :disabled="!metadata?.parentCheckpointId" @click="saveEdit">Save edit</button>
</template>
To retry the last AI turn, fork from the parent checkpoint of the preceding human message and re-submit the same input:
<script setup lang="ts">
import { computed } from "vue";
import { useMessageMetadata, useStreamContext } from "@langchain/vue";
const stream = useStreamContext();
const lastHuman = computed(() =>
[...stream.messages.value].reverse().find((m) => m.type === "human"),
);
const metadata = useMessageMetadata(stream, () => lastHuman.value?.id);
function retry() {
const forkFrom = metadata.value?.parentCheckpointId;
if (!forkFrom || !lastHuman.value) return;
void stream.submit({ messages: [lastHuman.value] }, { forkFrom });
}
</script>
<template>
<button :disabled="!metadata?.parentCheckpointId || !lastHuman" @click="retry">Retry</button>
</template>