Thanks to visit codestin.com
Credit goes to github.com

Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,6 @@ import { createChatStore, selectIsAwaitingFirstStreamChunk } from "./chatStore";
// Helpers
// ---------------------------------------------------------------------------

/** Minimal ChatMessage factory. `created_at` is derived from `id` to make
* ordering deterministic in tests that care about sort order. */
const makeMessage = (
id: number,
role: string,
Expand Down Expand Up @@ -53,19 +51,19 @@ describe("replaceMessages", () => {
expect(state.orderedMessageIDs).toEqual([1, 2]);
});

it("sorts messages by created_at", () => {
it("sorts messages by id when created_at disagrees with append order", () => {
const store = createChatStore();
const older = {
const first = {
...makeMessage(1, "user", "first"),
created_at: "2025-01-01T00:00:01.000Z",
created_at: "2025-01-01T00:00:05.000Z",
} as TypesGen.ChatMessage;
const newer = {
const second = {
...makeMessage(2, "assistant", "second"),
created_at: "2025-01-01T00:00:05.000Z",
created_at: "2025-01-01T00:00:01.000Z",
} as TypesGen.ChatMessage;

// Insert in reverse order.
store.replaceMessages([newer, older]);
store.replaceMessages([second, first]);

expect(store.getSnapshot().orderedMessageIDs).toEqual([1, 2]);
});
Expand Down Expand Up @@ -98,6 +96,31 @@ describe("replaceMessages", () => {
});
});

describe("upsertDurableMessages", () => {
it("orders merged messages by id rather than by arrival", () => {
const store = createChatStore();
const sharedCreatedAt = "2025-01-01T00:00:00.000Z";
const withSharedCreatedAt = (
id: number,
role: string,
): TypesGen.ChatMessage => ({
...makeMessage(id, role, `message-${id}`),
created_at: sharedCreatedAt,
});

store.replaceMessages([
withSharedCreatedAt(3, "assistant"),
withSharedCreatedAt(4, "tool"),
]);
store.upsertDurableMessages([
withSharedCreatedAt(1, "user"),
withSharedCreatedAt(2, "assistant"),
]);

expect(store.getSnapshot().orderedMessageIDs).toEqual([1, 2, 3, 4]);
});
});

// ---------------------------------------------------------------------------
// upsertDurableMessage
// ---------------------------------------------------------------------------
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,15 +7,6 @@ import {
import { applyMessagePartToStreamState } from "./streamState";
import type { ReconnectState, RetryState, StreamState } from "./types";

const byMessageCreatedAt = (
left: TypesGen.ChatMessage,
right: TypesGen.ChatMessage,
): number => {
return (
new Date(left.created_at).getTime() - new Date(right.created_at).getTime()
);
};

const buildMessageMap = (
messages: readonly TypesGen.ChatMessage[],
): Map<number, TypesGen.ChatMessage> =>
Expand All @@ -24,8 +15,8 @@ const buildMessageMap = (
const buildOrderedMessageIDs = (
messages: readonly TypesGen.ChatMessage[],
): readonly number[] => {
const sorted = [...messages];
sorted.sort(byMessageCreatedAt);
// created_at is shared across an insert batch, so only id tracks append order.
const sorted = messages.toSorted((left, right) => left.id - right.id);
// Deduplicate by ID. The input can contain duplicate IDs when
// cross-page duplication occurs in the React Query cache (e.g.
// upsertCacheMessages writes to page 0 while the same message
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -113,3 +113,36 @@ export const HiddenAssistantPlaceholderDoesNotRender: Story = {
expect(rows[1]).toHaveTextContent("Done.");
},
};

export const MergedMessagesRenderInIDOrder: Story = {
render: () => {
const store = createChatStore();
// One created_at for all four, so id is the only ordering signal.
const batchCreatedAt = new Date(FIXTURE_NOW).toISOString();
const batched = (
id: number,
role: TypesGen.ChatMessageRole,
text: string,
): TypesGen.ChatMessage => ({
...buildMessage(id, role, [{ type: "text", text }]),
created_at: batchCreatedAt,
});

store.replaceMessages([
batched(3, "user", "charlie"),
batched(4, "assistant", "delta"),
]);
store.upsertDurableMessages([
batched(1, "user", "alpha"),
batched(2, "assistant", "bravo"),
]);

return <ChatPageTimeline store={store} persistedError={undefined} />;
},
play: async ({ canvasElement }) => {
const canvas = within(canvasElement);
expect(canvas.getByTestId("conversation-timeline")).toHaveTextContent(
/alpha[\s\S]*bravo[\s\S]*charlie[\s\S]*delta/,
);
},
};
Loading