@@ -301,19 +301,30 @@ const READ_WINDOW_LIMIT = 3
301301const SCALED_READ_CALL_CHARACTERS = 40_000
302302
303303/**
304- * Characters one read returns. A live document is one chunk, so a caller that scales the window
305- * gets a longer one when it asks for more chunks than the default, bounded by an even share of
306- * the call's budget across the documents it reads, and never shorter than the standard page. At
307- * or below the default limit, and for every other caller, the window is the standard page.
304+ * Where one read's window ends. A live document is one chunk, so a caller that scales the window
305+ * gets a longer one when it asks for more chunks than the default, bounded by an even share of the
306+ * call's budget across the documents it reads. The share counts the window as the result encodes
307+ * it, since JSON escapes quotes, backslashes and line breaks, so a longer window is shortened until
308+ * its encoded text fits. The window is never shorter than the standard page, which is also what a
309+ * read at or below the default limit, or from any other caller, returns.
308310 */
309- function readWindowCharacters ( {
310- limit,
311- scaledWindow,
312- } : Pick < LiveReadInput , 'limit' | 'scaledWindow' > ) {
313- if ( ! scaledWindow || limit <= READ_WINDOW_LIMIT ) return READ_WINDOW_CHARACTERS
311+ function readWindowEnd (
312+ content : string ,
313+ start : number ,
314+ { limit, scaledWindow } : Pick < LiveReadInput , 'limit' | 'scaledWindow' >
315+ ) : number {
316+ const standardEnd = Math . min ( content . length , start + READ_WINDOW_CHARACTERS )
317+ if ( ! scaledWindow || limit <= READ_WINDOW_LIMIT ) return standardEnd
314318 const scaled = Math . floor ( ( READ_WINDOW_CHARACTERS * limit ) / READ_WINDOW_LIMIT )
315319 const share = Math . floor ( SCALED_READ_CALL_CHARACTERS / Math . max ( 1 , scaledWindow . documentsInCall ) )
316- return Math . max ( READ_WINDOW_CHARACTERS , Math . min ( scaled , share ) )
320+ const budget = Math . min ( scaled , share )
321+ let end = Math . min ( content . length , start + budget )
322+ for ( let attempt = 0 ; attempt < 3 && end > standardEnd ; attempt ++ ) {
323+ const encoded = JSON . stringify ( content . slice ( start , end ) ) . length - 2
324+ if ( encoded <= budget ) return end
325+ end = start + Math . floor ( ( ( end - start ) * budget ) / encoded )
326+ }
327+ return standardEnd
317328}
318329
319330/**
@@ -1008,7 +1019,7 @@ export type LiveReadInput = ResourceOwner & {
10081019 limit : number
10091020 /**
10101021 * Lengthens the window for a limit above the default, sharing one call's budget across the
1011- * documents it reads, as {@link readWindowCharacters } sizes it.
1022+ * documents it reads, as {@link readWindowEnd } sizes it.
10121023 */
10131024 scaledWindow ?: { documentsInCall : number }
10141025 startChunkIndex ?: number
@@ -1156,7 +1167,7 @@ export const readLiveDocument = defineAuthorizedKnowledgeUseCase({
11561167 'validation' ,
11571168 'This document changed. Read again from the beginning'
11581169 )
1159- const end = Math . min ( content . length , start + readWindowCharacters ( input ) )
1170+ const end = readWindowEnd ( content , start , input )
11601171 return {
11611172 documentId : input . documentId ,
11621173 knowledgeBaseId : '' ,
0 commit comments