Skip to content

Commit 25987dd

Browse files
committed
Remove truncation logic for now
1 parent eb8c4cc commit 25987dd

File tree

4 files changed

+46
-156
lines changed

4 files changed

+46
-156
lines changed

.changeset/modern-carrots-applaud.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,4 +2,4 @@
22
"roo-cline": patch
33
---
44

5-
Add the DeepSeek provider along with logic to trim messages when it hits the context window
5+
Add the DeepSeek provider

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ A fork of Cline, an autonomous coding agent, with some additional experimental f
1313
- Includes current time in the system prompt
1414
- Uses a file system watcher to more reliably watch for file system changes
1515
- Language selection for Cline's communication (English, Japanese, Spanish, French, German, and more)
16-
- Support for DeepSeek V3 with logic to trim messages when it hits the context window
16+
- Support for DeepSeek V3
1717
- Support for Meta 3, 3.1, and 3.2 models via AWS Bedrock
1818
- Support for listing models from OpenAI-compatible providers
1919
- Per-tool MCP auto-approval

src/api/providers/__tests__/deepseek.test.ts

Lines changed: 4 additions & 94 deletions
Original file line numberDiff line numberDiff line change
@@ -5,19 +5,6 @@ import { Anthropic } from '@anthropic-ai/sdk'
55

66
// Mock dependencies
77
jest.mock('openai')
8-
jest.mock('../../../shared/api', () => ({
9-
...jest.requireActual('../../../shared/api'),
10-
deepSeekModels: {
11-
'deepseek-chat': {
12-
maxTokens: 1000,
13-
contextWindow: 2000,
14-
supportsImages: false,
15-
supportsPromptCache: false,
16-
inputPrice: 0.014,
17-
outputPrice: 0.28,
18-
}
19-
}
20-
}))
218

229
describe('DeepSeekHandler', () => {
2310

@@ -46,8 +33,8 @@ describe('DeepSeekHandler', () => {
4633
expect(result).toEqual({
4734
id: mockOptions.deepSeekModelId,
4835
info: expect.objectContaining({
49-
maxTokens: 1000,
50-
contextWindow: 2000,
36+
maxTokens: 8192,
37+
contextWindow: 64000,
5138
supportsPromptCache: false,
5239
supportsImages: false,
5340
inputPrice: 0.014,
@@ -61,7 +48,7 @@ describe('DeepSeekHandler', () => {
6148
const result = handler.getModel()
6249

6350
expect(result.id).toBe('deepseek-chat')
64-
expect(result.info.maxTokens).toBe(1000)
51+
expect(result.info.maxTokens).toBe(8192)
6552
})
6653

6754
test('createMessage handles string content correctly', async () => {
@@ -109,7 +96,7 @@ describe('DeepSeekHandler', () => {
10996
],
11097
temperature: 0,
11198
stream: true,
112-
max_tokens: 1000,
99+
max_tokens: 8192,
113100
stream_options: { include_usage: true }
114101
}))
115102
})
@@ -155,83 +142,6 @@ describe('DeepSeekHandler', () => {
155142
}))
156143
})
157144

158-
test('createMessage truncates messages when exceeding context window', async () => {
159-
const handler = new DeepSeekHandler(mockOptions)
160-
const longString = 'a'.repeat(1000) // ~300 tokens
161-
const shortString = 'b'.repeat(100) // ~30 tokens
162-
163-
const systemPrompt = 'test system prompt'
164-
const messages: Anthropic.Messages.MessageParam[] = [
165-
{ role: 'user', content: longString }, // Old message
166-
{ role: 'assistant', content: 'short response' },
167-
{ role: 'user', content: shortString } // Recent message
168-
]
169-
170-
const mockStream = {
171-
async *[Symbol.asyncIterator]() {
172-
yield {
173-
choices: [{
174-
delta: {
175-
content: '(Note: Some earlier messages were truncated to fit within the model\'s context window)\n\n'
176-
}
177-
}]
178-
}
179-
yield {
180-
choices: [{
181-
delta: {
182-
content: 'test response'
183-
}
184-
}]
185-
}
186-
}
187-
}
188-
189-
const mockCreate = jest.fn().mockResolvedValue(mockStream)
190-
;(OpenAI as jest.MockedClass<typeof OpenAI>).prototype.chat = {
191-
completions: { create: mockCreate }
192-
} as any
193-
194-
const generator = handler.createMessage(systemPrompt, messages)
195-
const chunks = []
196-
for await (const chunk of generator) {
197-
chunks.push(chunk)
198-
}
199-
200-
// Should get two chunks: truncation notice and response
201-
expect(chunks).toHaveLength(2)
202-
expect(chunks[0]).toEqual({
203-
type: 'text',
204-
text: expect.stringContaining('truncated')
205-
})
206-
expect(chunks[1]).toEqual({
207-
type: 'text',
208-
text: 'test response'
209-
})
210-
211-
// Verify API call includes system prompt and recent messages, but not old message
212-
expect(mockCreate).toHaveBeenCalledWith(expect.objectContaining({
213-
messages: expect.arrayContaining([
214-
{ role: 'system', content: systemPrompt },
215-
{ role: 'assistant', content: 'short response' },
216-
{ role: 'user', content: shortString }
217-
])
218-
}))
219-
220-
// Verify truncation notice was included
221-
expect(chunks[0]).toEqual({
222-
type: 'text',
223-
text: expect.stringContaining('truncated')
224-
})
225-
226-
// Verify the messages array contains the expected messages
227-
const calledMessages = mockCreate.mock.calls[0][0].messages
228-
expect(calledMessages).toHaveLength(4)
229-
expect(calledMessages[0]).toEqual({ role: 'system', content: systemPrompt })
230-
expect(calledMessages[1]).toEqual({ role: 'user', content: longString })
231-
expect(calledMessages[2]).toEqual({ role: 'assistant', content: 'short response' })
232-
expect(calledMessages[3]).toEqual({ role: 'user', content: shortString })
233-
})
234-
235145
test('createMessage handles API errors', async () => {
236146
const handler = new DeepSeekHandler(mockOptions)
237147
const mockStream = {

src/api/providers/deepseek.ts

Lines changed: 40 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -20,64 +20,36 @@ export class DeepSeekHandler implements ApiHandler {
2020
}
2121

2222
async *createMessage(systemPrompt: string, messages: Anthropic.Messages.MessageParam[]): ApiStream {
23-
// Convert messages to simple format that DeepSeek expects
24-
const formattedMessages = messages.map(msg => {
23+
const modelInfo = deepSeekModels[this.options.deepSeekModelId as keyof typeof deepSeekModels] || deepSeekModels[deepSeekDefaultModelId]
24+
25+
// Format all messages
26+
const messagesToInclude: OpenAI.Chat.ChatCompletionMessageParam[] = [
27+
{ role: 'system' as const, content: systemPrompt }
28+
]
29+
30+
// Add the rest of the messages
31+
for (const msg of messages) {
32+
let messageContent = ""
2533
if (typeof msg.content === "string") {
26-
return { role: msg.role, content: msg.content }
27-
}
28-
// For array content, concatenate text parts
29-
return {
30-
role: msg.role,
31-
content: msg.content.reduce((acc, part) => {
34+
messageContent = msg.content
35+
} else if (Array.isArray(msg.content)) {
36+
messageContent = msg.content.reduce((acc, part) => {
3237
if (part.type === "text") {
3338
return acc + part.text
3439
}
3540
return acc
3641
}, "")
3742
}
38-
})
39-
40-
const openAiMessages: OpenAI.Chat.ChatCompletionMessageParam[] = [
41-
{ role: "system", content: systemPrompt },
42-
...formattedMessages,
43-
]
44-
const modelInfo = deepSeekModels[this.options.deepSeekModelId as keyof typeof deepSeekModels] || deepSeekModels[deepSeekDefaultModelId]
45-
46-
const contextWindow = modelInfo.contextWindow || 64_000
47-
const getTokenCount = (content: string) => Math.ceil(content.length * 0.3)
48-
49-
// Always keep system prompt
50-
const systemMsg = openAiMessages[0]
51-
let availableTokens = contextWindow - getTokenCount(typeof systemMsg.content === 'string' ? systemMsg.content : '')
52-
53-
// Start with most recent messages and work backwards
54-
const userMessages = openAiMessages.slice(1).reverse()
55-
const includedMessages = []
56-
let truncated = false
57-
58-
for (const msg of userMessages) {
59-
const content = typeof msg.content === 'string' ? msg.content : ''
60-
const tokens = getTokenCount(content)
6143

62-
if (tokens <= availableTokens) {
63-
includedMessages.unshift(msg)
64-
availableTokens -= tokens
65-
} else {
66-
truncated = true
67-
break
68-
}
69-
}
70-
71-
if (truncated) {
72-
yield {
73-
type: 'text',
74-
text: '(Note: Some earlier messages were truncated to fit within the model\'s context window)\n\n'
75-
}
44+
messagesToInclude.push({
45+
role: msg.role === 'user' ? 'user' as const : 'assistant' as const,
46+
content: messageContent
47+
})
7648
}
7749

7850
const requestOptions: OpenAI.Chat.ChatCompletionCreateParamsStreaming = {
7951
model: this.options.deepSeekModelId ?? "deepseek-chat",
80-
messages: [systemMsg, ...includedMessages],
52+
messages: messagesToInclude,
8153
temperature: 0,
8254
stream: true,
8355
max_tokens: modelInfo.maxTokens,
@@ -87,22 +59,30 @@ export class DeepSeekHandler implements ApiHandler {
8759
requestOptions.stream_options = { include_usage: true }
8860
}
8961

90-
const stream = await this.client.chat.completions.create(requestOptions)
91-
for await (const chunk of stream) {
92-
const delta = chunk.choices[0]?.delta
93-
if (delta?.content) {
94-
yield {
95-
type: "text",
96-
text: delta.content,
62+
let totalInputTokens = 0;
63+
let totalOutputTokens = 0;
64+
65+
try {
66+
const stream = await this.client.chat.completions.create(requestOptions)
67+
for await (const chunk of stream) {
68+
const delta = chunk.choices[0]?.delta
69+
if (delta?.content) {
70+
yield {
71+
type: "text",
72+
text: delta.content,
73+
}
9774
}
98-
}
99-
if (chunk.usage) {
100-
yield {
101-
type: "usage",
102-
inputTokens: chunk.usage.prompt_tokens || 0,
103-
outputTokens: chunk.usage.completion_tokens || 0,
75+
if (chunk.usage) {
76+
yield {
77+
type: "usage",
78+
inputTokens: chunk.usage.prompt_tokens || 0,
79+
outputTokens: chunk.usage.completion_tokens || 0,
80+
}
10481
}
10582
}
83+
} catch (error) {
84+
console.error("DeepSeek API Error:", error)
85+
throw error
10686
}
10787
}
10888

@@ -113,4 +93,4 @@ export class DeepSeekHandler implements ApiHandler {
11393
info: deepSeekModels[modelId as keyof typeof deepSeekModels] || deepSeekModels[deepSeekDefaultModelId],
11494
}
11595
}
116-
}
96+
}

0 commit comments

Comments
 (0)