Skip to content

Commit 276bf05

Browse files
committed
refactor
1 parent 8fae876 commit 276bf05

File tree

3 files changed

+205
-129
lines changed

3 files changed

+205
-129
lines changed

packages/core/src/codewhispererChat/controllers/chat/chatRequest/converter.ts

Lines changed: 178 additions & 93 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ import {
1111
SymbolType,
1212
TextDocument,
1313
} from '@amzn/codewhisperer-streaming'
14-
import { ChatTriggerType, TriggerPayload } from '../model'
14+
import { AdditionalContentEntryAddition, ChatTriggerType, RelevantTextDocumentAddition, TriggerPayload } from '../model'
1515
import { undefinedIfEmpty } from '../../../../shared/utilities/textUtilities'
1616
import { getLogger } from '../../../../shared/logger/logger'
1717

@@ -39,110 +39,47 @@ const filePathSizeLimit = 4_000
3939
const customerMessageSizeLimit = 4_000
4040

4141
export function triggerPayloadToChatRequest(triggerPayload: TriggerPayload): { conversationState: ConversationState } {
42-
// truncate
42+
// Flexible truncation logic
4343
let remainingPayloadSize = 100_000
44-
// Type A context: Preserving userInput as much as possible
45-
if (triggerPayload.message !== undefined) {
46-
if (triggerPayload.message.length <= remainingPayloadSize) {
47-
remainingPayloadSize -= triggerPayload.message.length
48-
} else {
49-
triggerPayload.message = triggerPayload.message.substring(0, remainingPayloadSize)
50-
remainingPayloadSize = 0
51-
}
52-
}
53-
// TODO: send truncation telemetry if needed
54-
getLogger().debug(`current request user input size: ${triggerPayload.message?.length}`)
55-
56-
// Type B1(prompts) context: Preserving prompts as much as possible
57-
let totalPromptSize = 0
58-
if (triggerPayload.additionalContents !== undefined) {
59-
for (const additionalContent of triggerPayload.additionalContents) {
60-
if (additionalContent.type === 'prompt' && additionalContent.innerContext !== undefined) {
61-
if (additionalContent.innerContext.length <= remainingPayloadSize) {
62-
remainingPayloadSize -= additionalContent.innerContext.length
63-
} else {
64-
additionalContent.innerContext = additionalContent.innerContext.substring(0, remainingPayloadSize)
65-
remainingPayloadSize = 0
66-
}
67-
totalPromptSize += additionalContent.innerContext.length
68-
}
69-
}
70-
}
44+
const userInputTruncationInfo = preserveContexts(triggerPayload, remainingPayloadSize, ChatContextType.UserInput)
7145

72-
getLogger().debug(`current request total prompts size: ${totalPromptSize}`)
46+
// Type B1(prompts) context: Preserving @prompt as much as possible
47+
const userSpecificPromptsTruncationInfo = preserveContexts(
48+
triggerPayload,
49+
userInputTruncationInfo.remainingPayloadSize,
50+
ChatContextType.UserSpecificPrompts
51+
)
7352

7453
// Type C context: Preserving current file context as much as possible
75-
// truncate the text to keep texts in the middle instead of blindly truncating the tail
76-
if (triggerPayload.fileText !== undefined) {
77-
if (triggerPayload.fileText.length <= remainingPayloadSize) {
78-
remainingPayloadSize -= triggerPayload.fileText.length
79-
} else {
80-
// Calculate the middle point
81-
const middle = Math.floor(triggerPayload.fileText.length / 2)
82-
// Calculate how much text we can take from each side of the middle
83-
const halfRemaining = Math.floor(remainingPayloadSize / 2)
84-
// Get text from around the middle point
85-
const startPos = middle - halfRemaining
86-
const endPos = middle + halfRemaining
87-
88-
triggerPayload.fileText = triggerPayload.fileText.substring(startPos, endPos)
89-
remainingPayloadSize = 0
90-
}
91-
}
92-
getLogger().debug(`current request file content size: ${triggerPayload.fileText?.length}`)
54+
const currentFileTruncationInfo = preserveContexts(
55+
triggerPayload,
56+
userSpecificPromptsTruncationInfo.remainingPayloadSize,
57+
ChatContextType.CurrentFile
58+
)
9359

9460
// Type B1(rules) context: Preserving rules as much as possible
95-
let totalRulesSize = 0
96-
if (triggerPayload.additionalContents !== undefined) {
97-
for (const additionalContent of triggerPayload.additionalContents) {
98-
if (additionalContent.type === 'rule' && additionalContent.innerContext !== undefined) {
99-
if (additionalContent.innerContext.length <= remainingPayloadSize) {
100-
remainingPayloadSize -= additionalContent.innerContext.length
101-
} else {
102-
additionalContent.innerContext = additionalContent.innerContext.substring(0, remainingPayloadSize)
103-
remainingPayloadSize = 0
104-
}
105-
totalRulesSize += additionalContent.innerContext.length
106-
}
107-
}
108-
}
109-
110-
getLogger().debug(`current request rules size: ${totalRulesSize}`)
61+
const userSpecificRulesTruncationInfo = preserveContexts(
62+
triggerPayload,
63+
currentFileTruncationInfo.remainingPayloadSize,
64+
ChatContextType.UserSpecificRules
65+
)
11166

11267
// Type B2(explicit @files) context: Preserving files as much as possible
113-
if (triggerPayload.additionalContents !== undefined) {
114-
for (const additionalContent of triggerPayload.additionalContents) {
115-
if (additionalContent.type === 'file' && additionalContent.innerContext !== undefined) {
116-
if (additionalContent.innerContext.length <= remainingPayloadSize) {
117-
remainingPayloadSize -= additionalContent.innerContext.length
118-
} else {
119-
additionalContent.innerContext = additionalContent.innerContext.substring(0, remainingPayloadSize)
120-
remainingPayloadSize = 0
121-
}
122-
}
123-
}
124-
}
68+
const userSpecificFilesTruncationInfo = preserveContexts(
69+
triggerPayload,
70+
userSpecificRulesTruncationInfo.remainingPayloadSize,
71+
ChatContextType.UserSpecificFiles
72+
)
12573

12674
// Type B3 @workspace context: Preserving workspace as much as possible
127-
let totalWorkspaceSize = 0
128-
if (triggerPayload.relevantTextDocuments !== undefined) {
129-
for (const relevantDocument of triggerPayload.relevantTextDocuments) {
130-
if (relevantDocument.text !== undefined) {
131-
if (relevantDocument.text.length <= remainingPayloadSize) {
132-
// remainingPayloadSize -= relevantDocument.text.length
133-
} else {
134-
// relevantDocument.text = relevantDocument.text.substring(0, remainingPayloadSize)
135-
// remainingPayloadSize = 0
136-
}
137-
totalWorkspaceSize += relevantDocument.text.length
138-
}
139-
}
140-
}
141-
142-
getLogger().debug(`current request workspace size: ${totalWorkspaceSize}`)
75+
const workspaceTruncationInfo = preserveContexts(
76+
triggerPayload,
77+
userSpecificFilesTruncationInfo.remainingPayloadSize,
78+
ChatContextType.Workspace
79+
)
14380

14481
getLogger().debug(
145-
`current request total payload size: ${(triggerPayload.message?.length ?? 0) + totalPromptSize + (triggerPayload.fileText?.length ?? 0) + totalRulesSize + totalWorkspaceSize}`
82+
`current request total payload size: ${userInputTruncationInfo.sizeAfter + userSpecificFilesTruncationInfo.sizeAfter + currentFileTruncationInfo.sizeAfter + userSpecificFilesTruncationInfo.sizeAfter + workspaceTruncationInfo.sizeAfter}`
14683
)
14784

14885
// Filter out empty innerContext from additionalContents
@@ -247,3 +184,151 @@ export function triggerPayloadToChatRequest(triggerPayload: TriggerPayload): { c
247184
},
248185
}
249186
}
187+
188+
function preserveContexts(
189+
triggerPayload: TriggerPayload,
190+
remainingPayloadSize: number,
191+
contextType: ChatContextType
192+
): FlexibleTruncationInfo {
193+
const typeToContextMap = new Map<
194+
ChatContextType,
195+
string | AdditionalContentEntryAddition[] | RelevantTextDocumentAddition[] | undefined
196+
>([
197+
[ChatContextType.UserInput, triggerPayload.message],
198+
[ChatContextType.CurrentFile, triggerPayload.fileText],
199+
[ChatContextType.UserSpecificPrompts, triggerPayload.additionalContents],
200+
[ChatContextType.UserSpecificRules, triggerPayload.additionalContents],
201+
[ChatContextType.UserSpecificFiles, triggerPayload.additionalContents],
202+
[ChatContextType.Workspace, triggerPayload.relevantTextDocuments],
203+
])
204+
205+
let truncationInfo = {
206+
remainingPayloadSize: remainingPayloadSize,
207+
sizeBefore: 0,
208+
sizeAfter: 0,
209+
textAfter: '',
210+
}
211+
212+
const contexts = typeToContextMap.get(contextType)
213+
if (!contexts) {
214+
getLogger().debug(
215+
`Current request context size: type: ${contextType}, before: ${truncationInfo.sizeBefore}, after: ${truncationInfo.sizeAfter}`
216+
)
217+
return truncationInfo
218+
}
219+
220+
switch (contextType) {
221+
case ChatContextType.UserInput:
222+
truncationInfo = truncate(contexts as string, truncationInfo)
223+
triggerPayload.message = truncationInfo.textAfter
224+
break
225+
case ChatContextType.CurrentFile:
226+
truncationInfo = truncate(contexts as string, truncationInfo)
227+
triggerPayload.fileText = truncationInfo.textAfter
228+
break
229+
case ChatContextType.UserSpecificPrompts:
230+
truncationInfo = truncateUserSpecificContexts(
231+
contexts as AdditionalContentEntryAddition[],
232+
truncationInfo,
233+
'prompt'
234+
)
235+
break
236+
case ChatContextType.UserSpecificRules:
237+
truncationInfo = truncateUserSpecificContexts(
238+
contexts as AdditionalContentEntryAddition[],
239+
truncationInfo,
240+
'rule'
241+
)
242+
break
243+
case ChatContextType.UserSpecificFiles:
244+
truncationInfo = truncateUserSpecificContexts(
245+
contexts as AdditionalContentEntryAddition[],
246+
truncationInfo,
247+
'file'
248+
)
249+
break
250+
case ChatContextType.Workspace:
251+
truncationInfo = truncateWorkspaceContexts(contexts as RelevantTextDocumentAddition[], truncationInfo)
252+
break
253+
default:
254+
getLogger().warn(`Unexpected context type: ${contextType}`)
255+
return truncationInfo
256+
}
257+
258+
getLogger().debug(
259+
`Current request context size: type: ${contextType}, before: ${truncationInfo.sizeBefore}, after: ${truncationInfo.sizeAfter}`
260+
)
261+
return truncationInfo
262+
}
263+
264+
function truncateUserSpecificContexts(
265+
contexts: AdditionalContentEntryAddition[],
266+
truncationInfo: FlexibleTruncationInfo,
267+
type: string
268+
): FlexibleTruncationInfo {
269+
for (const context of contexts) {
270+
if (context.type !== type || !context.innerContext) {
271+
continue
272+
}
273+
truncationInfo = truncate(context.innerContext, truncationInfo)
274+
context.innerContext = truncationInfo.textAfter
275+
}
276+
return truncationInfo
277+
}
278+
279+
function truncateWorkspaceContexts(
280+
contexts: RelevantTextDocumentAddition[],
281+
truncationInfo: FlexibleTruncationInfo
282+
): FlexibleTruncationInfo {
283+
for (const context of contexts) {
284+
if (!context.text) {
285+
continue
286+
}
287+
truncationInfo = truncate(context.text, truncationInfo)
288+
context.text = truncationInfo.textAfter
289+
}
290+
return truncationInfo
291+
}
292+
293+
function truncate(
294+
textBefore: string,
295+
truncationInfo: FlexibleTruncationInfo,
296+
isCurrentFile: Boolean = false
297+
): FlexibleTruncationInfo {
298+
const sizeBefore = truncationInfo.sizeBefore + textBefore.length
299+
300+
// for all other types of contexts, we simply truncate the tail,
301+
// for current file context, since it's expanded from the middle context, we truncate head and tail to preserve middle context
302+
const middle = Math.floor(textBefore.length / 2)
303+
const halfRemaining = Math.floor(truncationInfo.remainingPayloadSize / 2)
304+
const startPos = isCurrentFile ? middle - halfRemaining : 0
305+
const endPos = isCurrentFile
306+
? middle + (truncationInfo.remainingPayloadSize - halfRemaining)
307+
: Math.min(textBefore.length, truncationInfo.remainingPayloadSize)
308+
const textAfter = textBefore.substring(startPos, endPos)
309+
310+
const sizeAfter = truncationInfo.sizeAfter + textAfter.length
311+
const remainingPayloadSize = truncationInfo.remainingPayloadSize - textAfter.length
312+
return {
313+
remainingPayloadSize,
314+
sizeBefore,
315+
sizeAfter,
316+
textAfter,
317+
}
318+
}
319+
320+
type FlexibleTruncationInfo = {
321+
readonly remainingPayloadSize: number
322+
readonly sizeBefore: number
323+
readonly sizeAfter: number
324+
readonly textAfter: string
325+
}
326+
327+
export enum ChatContextType {
328+
UserInput = 'userInput',
329+
CurrentFile = 'currentFile',
330+
UserSpecificPrompts = 'userSpecificPrompts',
331+
UserSpecificRules = 'userSpecificRules',
332+
UserSpecificFiles = 'userSpecificFiles',
333+
Workspace = 'workspace',
334+
}

0 commit comments

Comments
 (0)