Skip to content

Commit 4c62fa5

Browse files
committed
new tests
1 parent 9c8ba3e commit 4c62fa5

File tree

1 file changed

+228
-0
lines changed

1 file changed

+228
-0
lines changed
Lines changed: 228 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,228 @@
1+
import { describe, expect, it, jest, beforeEach } from "@jest/globals"
2+
import { ApiHandler } from "../../../api"
3+
import { ApiMessage } from "../../task-persistence/apiMessages"
4+
import { maybeRemoveImageBlocks } from "../../../api/transform/image-cleaning"
5+
import { summarizeConversation, getMessagesSinceLastSummary, N_MESSAGES_TO_KEEP } from "../index"
6+
7+
// Mock dependencies
8+
jest.mock("../../../api/transform/image-cleaning", () => ({
9+
maybeRemoveImageBlocks: jest.fn((messages: ApiMessage[], _apiHandler: ApiHandler) => [...messages]),
10+
}))
11+
12+
describe("getMessagesSinceLastSummary", () => {
13+
it("should return all messages when there is no summary", () => {
14+
const messages: ApiMessage[] = [
15+
{ role: "user", content: "Hello", ts: 1 },
16+
{ role: "assistant", content: "Hi there", ts: 2 },
17+
{ role: "user", content: "How are you?", ts: 3 },
18+
]
19+
20+
const result = getMessagesSinceLastSummary(messages)
21+
expect(result).toEqual(messages)
22+
})
23+
24+
it("should return messages since the last summary", () => {
25+
const messages: ApiMessage[] = [
26+
{ role: "user", content: "Hello", ts: 1 },
27+
{ role: "assistant", content: "Hi there", ts: 2 },
28+
{ role: "assistant", content: "Summary of conversation", ts: 3, isSummary: true },
29+
{ role: "user", content: "How are you?", ts: 4 },
30+
{ role: "assistant", content: "I'm good", ts: 5 },
31+
]
32+
33+
const result = getMessagesSinceLastSummary(messages)
34+
expect(result).toEqual([
35+
{ role: "assistant", content: "Summary of conversation", ts: 3, isSummary: true },
36+
{ role: "user", content: "How are you?", ts: 4 },
37+
{ role: "assistant", content: "I'm good", ts: 5 },
38+
])
39+
})
40+
41+
it("should handle multiple summary messages and return since the last one", () => {
42+
const messages: ApiMessage[] = [
43+
{ role: "user", content: "Hello", ts: 1 },
44+
{ role: "assistant", content: "First summary", ts: 2, isSummary: true },
45+
{ role: "user", content: "How are you?", ts: 3 },
46+
{ role: "assistant", content: "Second summary", ts: 4, isSummary: true },
47+
{ role: "user", content: "What's new?", ts: 5 },
48+
]
49+
50+
const result = getMessagesSinceLastSummary(messages)
51+
expect(result).toEqual([
52+
{ role: "assistant", content: "Second summary", ts: 4, isSummary: true },
53+
{ role: "user", content: "What's new?", ts: 5 },
54+
])
55+
})
56+
57+
it("should handle empty messages array", () => {
58+
const result = getMessagesSinceLastSummary([])
59+
expect(result).toEqual([])
60+
})
61+
})
62+
63+
describe("summarizeConversation", () => {
64+
// Mock ApiHandler
65+
let mockApiHandler: ApiHandler
66+
let mockStream: AsyncGenerator<any, void, unknown>
67+
68+
beforeEach(() => {
69+
// Reset mocks
70+
jest.clearAllMocks()
71+
72+
// Setup mock stream
73+
mockStream = (async function* () {
74+
yield { type: "text" as const, text: "This is " }
75+
yield { type: "text" as const, text: "a summary" }
76+
})()
77+
78+
// Setup mock API handler
79+
mockApiHandler = {
80+
createMessage: jest.fn().mockReturnValue(mockStream),
81+
countTokens: jest.fn().mockImplementation(() => Promise.resolve(100)),
82+
getModel: jest.fn().mockReturnValue({
83+
id: "test-model",
84+
info: {
85+
contextWindow: 8000,
86+
supportsImages: true,
87+
supportsComputerUse: true,
88+
supportsVision: true,
89+
maxTokens: 4000,
90+
supportsPromptCache: true,
91+
maxCachePoints: 10,
92+
minTokensPerCachePoint: 100,
93+
cachableFields: ["system", "messages"],
94+
},
95+
}),
96+
} as unknown as ApiHandler
97+
})
98+
99+
it("should not summarize when there are not enough messages", async () => {
100+
const messages: ApiMessage[] = [
101+
{ role: "user", content: "Hello", ts: 1 },
102+
{ role: "assistant", content: "Hi there", ts: 2 },
103+
]
104+
105+
const result = await summarizeConversation(messages, mockApiHandler)
106+
expect(result).toEqual(messages)
107+
expect(mockApiHandler.createMessage).not.toHaveBeenCalled()
108+
})
109+
110+
it("should not summarize when there was a recent summary", async () => {
111+
const messages: ApiMessage[] = [
112+
{ role: "user", content: "Hello", ts: 1 },
113+
{ role: "assistant", content: "Hi there", ts: 2 },
114+
{ role: "user", content: "How are you?", ts: 3 },
115+
{ role: "assistant", content: "I'm good", ts: 4 },
116+
{ role: "user", content: "What's new?", ts: 5 },
117+
{ role: "assistant", content: "Not much", ts: 6, isSummary: true }, // Recent summary
118+
{ role: "user", content: "Tell me more", ts: 7 },
119+
]
120+
121+
const result = await summarizeConversation(messages, mockApiHandler)
122+
expect(result).toEqual(messages)
123+
expect(mockApiHandler.createMessage).not.toHaveBeenCalled()
124+
})
125+
126+
it("should summarize conversation and insert summary message", async () => {
127+
const messages: ApiMessage[] = [
128+
{ role: "user", content: "Hello", ts: 1 },
129+
{ role: "assistant", content: "Hi there", ts: 2 },
130+
{ role: "user", content: "How are you?", ts: 3 },
131+
{ role: "assistant", content: "I'm good", ts: 4 },
132+
{ role: "user", content: "What's new?", ts: 5 },
133+
{ role: "assistant", content: "Not much", ts: 6 },
134+
{ role: "user", content: "Tell me more", ts: 7 },
135+
]
136+
137+
const result = await summarizeConversation(messages, mockApiHandler)
138+
139+
// Check that the API was called correctly
140+
expect(mockApiHandler.createMessage).toHaveBeenCalled()
141+
expect(maybeRemoveImageBlocks).toHaveBeenCalled()
142+
143+
// Verify the structure of the result
144+
// The result should be: original messages (except last N) + summary + last N messages
145+
expect(result.length).toBe(messages.length + 1) // Original + summary
146+
147+
// Check that the summary message was inserted correctly
148+
const summaryMessage = result[result.length - N_MESSAGES_TO_KEEP - 1]
149+
expect(summaryMessage.role).toBe("assistant")
150+
expect(summaryMessage.content).toBe("This is a summary")
151+
expect(summaryMessage.isSummary).toBe(true)
152+
153+
// Check that the last N_MESSAGES_TO_KEEP messages are preserved
154+
const lastMessages = messages.slice(-N_MESSAGES_TO_KEEP)
155+
expect(result.slice(-N_MESSAGES_TO_KEEP)).toEqual(lastMessages)
156+
})
157+
158+
it("should handle empty summary response", async () => {
159+
// We need enough messages to trigger summarization
160+
const messages: ApiMessage[] = [
161+
{ role: "user", content: "Hello", ts: 1 },
162+
{ role: "assistant", content: "Hi there", ts: 2 },
163+
{ role: "user", content: "How are you?", ts: 3 },
164+
{ role: "assistant", content: "I'm good", ts: 4 },
165+
{ role: "user", content: "What's new?", ts: 5 },
166+
{ role: "assistant", content: "Not much", ts: 6 },
167+
{ role: "user", content: "Tell me more", ts: 7 },
168+
]
169+
170+
// Mock console.warn before we call the function
171+
const originalWarn = console.warn
172+
const mockWarn = jest.fn()
173+
console.warn = mockWarn
174+
175+
// Setup empty summary response
176+
const emptyStream = (async function* () {
177+
yield { type: "text" as const, text: "" }
178+
})()
179+
180+
// Create a new mock for createMessage that returns empty stream
181+
const createMessageMock = jest.fn().mockReturnValue(emptyStream)
182+
mockApiHandler.createMessage = createMessageMock as any
183+
184+
// We need to mock maybeRemoveImageBlocks to return the expected messages
185+
;(maybeRemoveImageBlocks as jest.Mock).mockImplementationOnce((messages: any) => {
186+
return messages.map(({ role, content }: { role: string; content: any }) => ({ role, content }))
187+
})
188+
189+
const result = await summarizeConversation(messages, mockApiHandler)
190+
191+
// Should return original messages when summary is empty
192+
expect(result).toEqual(messages)
193+
expect(mockWarn).toHaveBeenCalledWith("Received empty summary from API")
194+
195+
// Restore console.warn
196+
console.warn = originalWarn
197+
})
198+
199+
it("should correctly format the request to the API", async () => {
200+
const messages: ApiMessage[] = [
201+
{ role: "user", content: "Hello", ts: 1 },
202+
{ role: "assistant", content: "Hi there", ts: 2 },
203+
{ role: "user", content: "How are you?", ts: 3 },
204+
{ role: "assistant", content: "I'm good", ts: 4 },
205+
{ role: "user", content: "What's new?", ts: 5 },
206+
{ role: "assistant", content: "Not much", ts: 6 },
207+
{ role: "user", content: "Tell me more", ts: 7 },
208+
]
209+
210+
await summarizeConversation(messages, mockApiHandler)
211+
212+
// Verify the final request message
213+
const expectedFinalMessage = {
214+
role: "user",
215+
content: "Summarize the conversation so far, as described in the prompt instructions.",
216+
}
217+
218+
// Verify that createMessage was called with the correct prompt
219+
expect(mockApiHandler.createMessage).toHaveBeenCalledWith(
220+
expect.stringContaining("Your task is to create a detailed summary of the conversation"),
221+
expect.any(Array),
222+
)
223+
224+
// Check that maybeRemoveImageBlocks was called with the correct messages
225+
const mockCallArgs = (maybeRemoveImageBlocks as jest.Mock).mock.calls[0][0] as any[]
226+
expect(mockCallArgs[mockCallArgs.length - 1]).toEqual(expectedFinalMessage)
227+
})
228+
})

0 commit comments

Comments
 (0)