@@ -5,25 +5,25 @@ import { RetryLanguageModel } from "../llm";
55import { AgentContext } from "../core/context" ;
66import { uuidv4 , sleep , toFile , getMimeType } from "../common/utils" ;
77import {
8- LLMRequest ,
9- StreamCallbackMessage ,
10- StreamCallback ,
11- HumanCallback ,
12- StreamResult ,
138 Tool ,
9+ LLMRequest ,
1410 ToolResult ,
1511 DialogueTool ,
12+ StreamResult ,
13+ HumanCallback ,
14+ StreamCallback ,
15+ StreamCallbackMessage ,
1616} from "../types" ;
1717import {
18- LanguageModelV2FunctionTool ,
1918 LanguageModelV2Prompt ,
20- LanguageModelV2StreamPart ,
2119 LanguageModelV2TextPart ,
22- LanguageModelV2ToolCallPart ,
20+ SharedV2ProviderOptions ,
2321 LanguageModelV2ToolChoice ,
24- LanguageModelV2ToolResultOutput ,
22+ LanguageModelV2StreamPart ,
23+ LanguageModelV2ToolCallPart ,
24+ LanguageModelV2FunctionTool ,
2525 LanguageModelV2ToolResultPart ,
26- SharedV2ProviderOptions ,
26+ LanguageModelV2ToolResultOutput ,
2727} from "@ai-sdk/provider" ;
2828
2929export function defaultLLMProviderOptions ( ) : SharedV2ProviderOptions {
@@ -90,7 +90,10 @@ export function convertToolResult(
9090 type : "error-text" ,
9191 value : "Error" ,
9292 } ;
93- } else if ( toolResult . content . length == 1 && toolResult . content [ 0 ] . type == "text" ) {
93+ } else if (
94+ toolResult . content . length == 1 &&
95+ toolResult . content [ 0 ] . type == "text"
96+ ) {
9497 let text = toolResult . content [ 0 ] . text ;
9598 result = {
9699 type : "text" ,
@@ -188,7 +191,11 @@ export async function callAgentLLM(
188191 requestHandler ?: ( request : LLMRequest ) => void
189192) : Promise < Array < LanguageModelV2TextPart | LanguageModelV2ToolCallPart > > {
190193 await agentContext . context . checkAborted ( ) ;
191- if ( messages . length >= config . compressThreshold && ! noCompress ) {
194+ if (
195+ ! noCompress &&
196+ ( messages . length >= config . compressThreshold || ( messages . length >= 10 && estimatePromptTokens ( messages , tools ) >= config . compressTokensThreshold ) )
197+ ) {
198+ // Compress messages
192199 await memory . compressAgentMessages ( agentContext , rlm , messages , tools ) ;
193200 }
194201 if ( ! toolChoice ) {
@@ -221,8 +228,7 @@ export async function callAgentLLM(
221228 let thinkStreamId = uuidv4 ( ) ;
222229 let textStreamDone = false ;
223230 const toolParts : LanguageModelV2ToolCallPart [ ] = [ ] ;
224- let reader : ReadableStreamDefaultReader < LanguageModelV2StreamPart > | null =
225- null ;
231+ let reader : ReadableStreamDefaultReader < LanguageModelV2StreamPart > | null = null ;
226232 try {
227233 agentChain . agentRequest = request ;
228234 context . currentStepControllers . add ( stepController ) ;
@@ -509,6 +515,9 @@ export async function callAgentLLM(
509515 await context . checkAborted ( ) ;
510516 if ( retryNum < config . maxRetryNum ) {
511517 await sleep ( 300 * ( retryNum + 1 ) * ( retryNum + 1 ) ) ;
518+ if ( ( e + "" ) . indexOf ( "is too long" ) > - 1 ) {
519+ await memory . compressAgentMessages ( agentContext , rlm , messages , tools ) ;
520+ }
512521 return callAgentLLM (
513522 agentContext ,
514523 rlm ,
@@ -534,6 +543,108 @@ export async function callAgentLLM(
534543 : toolParts ;
535544}
536545
546+ export function estimatePromptTokens (
547+ messages : LanguageModelV2Prompt ,
548+ tools ?: LanguageModelV2FunctionTool [ ]
549+ ) {
550+ let tokens = messages . reduce ( ( total , message ) => {
551+ if ( message . role == "system" ) {
552+ return total + estimateTokens ( message . content ) ;
553+ } else if ( message . role == "user" ) {
554+ return (
555+ total +
556+ estimateTokens (
557+ message . content
558+ . filter ( ( part ) => part . type == "text" )
559+ . map ( ( part ) => part . text )
560+ . join ( "\n" )
561+ )
562+ ) ;
563+ } else if ( message . role == "assistant" ) {
564+ return (
565+ total +
566+ estimateTokens (
567+ message . content
568+ . map ( ( part ) => {
569+ if ( part . type == "text" ) {
570+ return part . text ;
571+ } else if ( part . type == "reasoning" ) {
572+ return part . text ;
573+ } else if ( part . type == "tool-call" ) {
574+ return part . toolName + JSON . stringify ( part . input || { } ) ;
575+ } else if ( part . type == "tool-result" ) {
576+ return part . toolName + JSON . stringify ( part . output || { } ) ;
577+ }
578+ return "" ;
579+ } )
580+ . join ( "" )
581+ )
582+ ) ;
583+ } else if ( message . role == "tool" ) {
584+ return (
585+ total +
586+ estimateTokens (
587+ message . content
588+ . map ( ( part ) => part . toolName + JSON . stringify ( part . output || { } ) )
589+ . join ( "" )
590+ )
591+ ) ;
592+ }
593+ return total ;
594+ } , 0 ) ;
595+ if ( tools ) {
596+ tokens += tools . reduce ( ( total , tool ) => {
597+ return total + estimateTokens ( JSON . stringify ( tool ) ) ;
598+ } , 0 ) ;
599+ }
600+ return tokens ;
601+ }
602+
603+ export function estimateTokens ( text : string ) {
604+ if ( ! text ) {
605+ return 0 ;
606+ }
607+ let tokenCount = 0 ;
608+ for ( let i = 0 ; i < text . length ; i ++ ) {
609+ const char = text [ i ] ;
610+ const code = char . charCodeAt ( 0 ) ;
611+ if (
612+ ( code >= 0x4e00 && code <= 0x9fff ) ||
613+ ( code >= 0x3400 && code <= 0x4dbf ) ||
614+ ( code >= 0x3040 && code <= 0x309f ) ||
615+ ( code >= 0x30a0 && code <= 0x30ff ) ||
616+ ( code >= 0xac00 && code <= 0xd7af )
617+ ) {
618+ tokenCount += 2 ;
619+ } else if ( / \s / . test ( char ) ) {
620+ continue ;
621+ } else if ( / [ a - z A - Z ] / . test ( char ) ) {
622+ let word = "" ;
623+ while ( i < text . length && / [ a - z A - Z ] / . test ( text [ i ] ) ) {
624+ word += text [ i ] ;
625+ i ++ ;
626+ }
627+ i -- ;
628+ if ( word . length <= 4 ) {
629+ tokenCount += 1 ;
630+ } else {
631+ tokenCount += Math . ceil ( word . length / 4 ) ;
632+ }
633+ } else if ( / \d / . test ( char ) ) {
634+ let number = "" ;
635+ while ( i < text . length && / \d / . test ( text [ i ] ) ) {
636+ number += text [ i ] ;
637+ i ++ ;
638+ }
639+ i -- ;
640+ tokenCount += Math . max ( 1 , Math . ceil ( number . length / 3 ) ) ;
641+ } else {
642+ tokenCount += 1 ;
643+ }
644+ }
645+ return Math . max ( 1 , tokenCount ) ;
646+ }
647+
537648function appendUserConversation (
538649 agentContext : AgentContext ,
539650 messages : LanguageModelV2Prompt
0 commit comments