Coverage for merco/context/recovery.py: 100%

21 statements  

« prev     ^ index     » next       coverage.py v7.15.0, created at 2026-07-07 14:04 +0800

1"""ContextCompressRecovery — compresses context when request body is too large.""" 

2from __future__ import annotations 

3 

4import logging 

5from merco.core.pipeline import Recovery, RecoveryContext 

6 

7logger = logging.getLogger("merco.pipeline") 

8 

9 

10class ContextCompressRecovery(Recovery): 

11 """Compress context: triggered by 413/too-long keywords OR large context.""" 

12 

13 name = "compress_context" 

14 

15 def __init__(self, min_context_bytes: int = 30000): 

16 self.min_context_bytes = min_context_bytes 

17 

18 async def attempt(self, ctx: RecoveryContext) -> bool: 

19 if ctx.compress_count >= ctx.max_compress: 

20 return False 

21 

22 status = getattr(ctx.error, "status_code", None) 

23 body = str(ctx.error).lower() 

24 

25 # Force trigger on explicit too-long errors, even for small contexts. 

26 force = ( 

27 status == 413 

28 or "context length" in body 

29 or "maximum context" in body 

30 or "prompt too long" in body 

31 or ("too long" in body and "context" in body) 

32 ) 

33 

34 if not force: 

35 if ctx.context_tokens > 0 and \ 

36 ctx.context_tokens * 4 < self.min_context_bytes: 

37 return False 

38 

39 logger.info("→ 压缩上下文后重试 LLM(第 %d/%d 次)", 

40 ctx.compress_count + 1, ctx.max_compress) 

41 ctx.compress = True 

42 ctx.extra_wait = max(ctx.extra_wait, 0.5) 

43 return True