package multiagent import ( "context" "github.com/cloudwego/eino/adk" "github.com/cloudwego/eino/adk/middlewares/summarization" "go.uber.org/zap" ) // modelInputSoftBudgetMiddleware is the final guard before a normal model call. // It drops oldest complete rounds and truncates oversized tool output in the latest // round, but never fails locally — API context limits are handled by overflow retry. type modelInputSoftBudgetMiddleware struct { adk.BaseChatModelAgentMiddleware maxTokens int toolMaxBytes int counter summarization.TokenCounterFunc logger *zap.Logger phase string } func newModelInputSoftBudgetMiddleware( maxTotalTokens int, toolMaxBytes int, modelName string, logger *zap.Logger, phase string, ) adk.ChatModelAgentMiddleware { if maxTotalTokens <= 0 { maxTotalTokens = 120000 } if toolMaxBytes <= 0 { toolMaxBytes = 12000 } return &modelInputSoftBudgetMiddleware{ maxTokens: maxTotalTokens, toolMaxBytes: toolMaxBytes, counter: einoSummarizationTokenCounter(modelName), logger: logger, phase: phase, } } func (m *modelInputSoftBudgetMiddleware) BeforeModelRewriteState( ctx context.Context, state *adk.ChatModelAgentState, mc *adk.ModelContext, ) (context.Context, *adk.ChatModelAgentState, error) { if m == nil || state == nil || len(state.Messages) == 0 { return ctx, state, nil } compacted, changed := compactMessagesByDroppingRounds(ctx, state.Messages, compactMessagesOpts{ maxTokens: m.maxTokens, counter: m.counter, toolMaxBytes: m.toolMaxBytes, phase: m.phase, logger: m.logger, }) if !changed { return ctx, state, nil } out := *state out.Messages = compacted return ctx, &out, nil }