Skip to content
Draft
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
116 changes: 116 additions & 0 deletions src/core/condense/__tests__/index.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1484,6 +1484,122 @@ describe("summarizeConversation", () => {

expect(result.error).toBeUndefined()
})

it("should return context window exceeded error when API throws context length error", async () => {
const messages: ApiMessage[] = [
{ role: "user", content: "Hello", ts: 1 },
{ role: "assistant", content: "Hi there", ts: 2 },
{ role: "user", content: "How are you?", ts: 3 },
{ role: "assistant", content: "I'm good", ts: 4 },
{ role: "user", content: "What's new?", ts: 5 },
{ role: "assistant", content: "Not much", ts: 6 },
{ role: "user", content: "Tell me more", ts: 7 },
]

// Create a stream that throws a context length exceeded error
// eslint-disable-next-line require-yield
const errorStream = (async function* () {
throw new Error(
"400 This endpoint's maximum context length is 131072 tokens. However, you requested about 145406 tokens.",
)
})()

mockApiHandler.createMessage = vi.fn().mockReturnValue(errorStream) as any

const result = await summarizeConversation(
messages,
mockApiHandler,
defaultSystemPrompt,
taskId,
DEFAULT_PREV_CONTEXT_TOKENS,
)

// Should return original messages with error
expect(result.messages).toEqual(messages)
expect(result.summary).toBe("")
expect(result.error).toBeTruthy()
// The error message comes from the translation key
expect(result.error).toContain("condense_context_window_exceeded")
expect(result.newContextTokens).toBeUndefined()
})

it("should return context window exceeded error for various context length error messages", async () => {
const messages: ApiMessage[] = [
{ role: "user", content: "Hello", ts: 1 },
{ role: "assistant", content: "Hi there", ts: 2 },
{ role: "user", content: "How are you?", ts: 3 },
{ role: "assistant", content: "I'm good", ts: 4 },
{ role: "user", content: "What's new?", ts: 5 },
{ role: "assistant", content: "Not much", ts: 6 },
{ role: "user", content: "Tell me more", ts: 7 },
]

// Test various error message patterns
const errorPatterns = [
"context_length_exceeded",
"maximum context length exceeded",
"prompt is too long",
"too many tokens in your request",
]

for (const errorPattern of errorPatterns) {
vi.clearAllMocks()

// eslint-disable-next-line require-yield
const errorStream = (async function* () {
throw new Error(errorPattern)
})()

mockApiHandler.createMessage = vi.fn().mockReturnValue(errorStream) as any

const result = await summarizeConversation(
messages,
mockApiHandler,
defaultSystemPrompt,
taskId,
DEFAULT_PREV_CONTEXT_TOKENS,
)

expect(result.error).toBeTruthy()
// The error message comes from the translation key
expect(result.error).toContain("condense_context_window_exceeded")
}
})

it("should return generic error for non-context-window API errors", async () => {
const messages: ApiMessage[] = [
{ role: "user", content: "Hello", ts: 1 },
{ role: "assistant", content: "Hi there", ts: 2 },
{ role: "user", content: "How are you?", ts: 3 },
{ role: "assistant", content: "I'm good", ts: 4 },
{ role: "user", content: "What's new?", ts: 5 },
{ role: "assistant", content: "Not much", ts: 6 },
{ role: "user", content: "Tell me more", ts: 7 },
]

// Create a stream that throws a generic API error
// eslint-disable-next-line require-yield
const errorStream = (async function* () {
throw new Error("Internal server error")
})()

mockApiHandler.createMessage = vi.fn().mockReturnValue(errorStream) as any

const result = await summarizeConversation(
messages,
mockApiHandler,
defaultSystemPrompt,
taskId,
DEFAULT_PREV_CONTEXT_TOKENS,
)

// Should return original messages with error
expect(result.messages).toEqual(messages)
expect(result.summary).toBe("")
expect(result.error).toBeTruthy()
expect(result.error).toContain("Internal server error")
expect(result.newContextTokens).toBeUndefined()
})
})

describe("summarizeConversation with custom settings", () => {
Expand Down
44 changes: 35 additions & 9 deletions src/core/condense/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -319,19 +319,45 @@ export async function summarizeConversation(
}
}

const stream = handlerToUse.createMessage(promptToUse, requestMessages)

let summary = ""
let cost = 0
let outputTokens = 0

for await (const chunk of stream) {
if (chunk.type === "text") {
summary += chunk.text
} else if (chunk.type === "usage") {
// Record final usage chunk only
cost = chunk.totalCost ?? 0
outputTokens = chunk.outputTokens ?? 0
try {
const stream = handlerToUse.createMessage(promptToUse, requestMessages)

for await (const chunk of stream) {
if (chunk.type === "text") {
summary += chunk.text
} else if (chunk.type === "usage") {
// Record final usage chunk only
cost = chunk.totalCost ?? 0
outputTokens = chunk.outputTokens ?? 0
}
}
} catch (error) {
const errorMessage = error instanceof Error ? error.message : String(error)

// Check for context window exceeded error
if (
errorMessage.includes("context_length_exceeded") ||
errorMessage.includes("context length") ||
errorMessage.includes("maximum context") ||
errorMessage.includes("too long") ||
errorMessage.includes("too many tokens")
) {
return {
...response,
cost,
error: t("common:errors.condense_context_window_exceeded"),
}
}

// Return generic error for other failures
return {
...response,
cost,
error: `${t("common:errors.condense_failed")}: ${errorMessage}`,
}
}

Expand Down
1 change: 1 addition & 0 deletions src/i18n/locales/ca/common.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions src/i18n/locales/de/common.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions src/i18n/locales/en/common.json
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@
"condensed_recently": "Context was condensed recently; skipping this attempt",
"condense_handler_invalid": "API handler for condensing context is invalid",
"condense_context_grew": "Context size increased during condensing; skipping this attempt",
"condense_context_window_exceeded": "The selected condensing model's context window is too small for the current conversation. Please select a model with a larger context window or manually reduce the conversation size first.",
"url_timeout": "The website took too long to load (timeout). This could be due to a slow connection, heavy website, or the site being temporarily unavailable. You can try again later or check if the URL is correct.",
"url_not_found": "The website address could not be found. Please check if the URL is correct and try again.",
"no_internet": "No internet connection. Please check your network connection and try again.",
Expand Down
1 change: 1 addition & 0 deletions src/i18n/locales/es/common.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions src/i18n/locales/fr/common.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions src/i18n/locales/hi/common.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions src/i18n/locales/id/common.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions src/i18n/locales/it/common.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions src/i18n/locales/ja/common.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions src/i18n/locales/ko/common.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions src/i18n/locales/nl/common.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions src/i18n/locales/pl/common.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading
Loading