|
24 | 24 | from enum import Enum |
25 | 25 |
|
26 | 26 | from .models import ( |
27 | | - ContextLedger, BudgetAllocation, OptimizerStrategy, OptimizationResult |
| 27 | + ContextLedger, BudgetAllocation, OptimizerStrategy, OptimizationResult, |
| 28 | + ContextConfig as ManagerConfig # Deprecated alias for backward compatibility |
28 | 29 | ) |
29 | 30 | from .tokens import estimate_tokens_heuristic, estimate_messages_tokens, estimate_message_tokens |
30 | 31 | from .budgeter import ContextBudgeter |
@@ -288,180 +289,6 @@ def to_dict(self) -> Dict[str, Any]: |
288 | 289 | "budget": self.budget.to_dict() if self.budget else None, |
289 | 290 | } |
290 | 291 |
|
291 | | - |
292 | | -@dataclass |
293 | | -class ManagerConfig: |
294 | | - """ |
295 | | - Complete configuration for ContextManager. |
296 | | - |
297 | | - Consolidates all context management settings with proper precedence. |
298 | | - """ |
299 | | - # Auto-compaction |
300 | | - auto_compact: bool = True |
301 | | - compact_threshold: float = 0.8 |
302 | | - strategy: OptimizerStrategy = OptimizerStrategy.SMART |
303 | | - |
304 | | - # Compression benefit check |
305 | | - compression_min_gain_pct: float = 5.0 |
306 | | - compression_max_attempts: int = 3 |
307 | | - |
308 | | - # Budget |
309 | | - output_reserve: int = 8000 |
310 | | - history_ratio: float = 0.6 |
311 | | - default_tool_output_max: int = 10000 |
312 | | - |
313 | | - # Per-tool budgets |
314 | | - tool_budgets: Dict[str, PerToolBudget] = field(default_factory=dict) |
315 | | - protected_tools: List[str] = field(default_factory=list) |
316 | | - |
317 | | - # LLM-powered summarization |
318 | | - llm_summarize: bool = False # Enable LLM-powered summarization |
319 | | - |
320 | | - # Smart tool output summarization |
321 | | - smart_tool_summarize: bool = True # Summarize large tool outputs using LLM before truncating |
322 | | - tool_summarize_limits: Dict[str, int] = field(default_factory=dict) # Per-tool min_chars_to_summarize |
323 | | - |
324 | | - # Estimation |
325 | | - estimation_mode: EstimationMode = EstimationMode.HEURISTIC |
326 | | - log_estimation_mismatch: bool = False |
327 | | - mismatch_threshold_pct: float = 15.0 |
328 | | - |
329 | | - # Monitoring |
330 | | - monitor_enabled: bool = False |
331 | | - monitor_path: str = "./context.txt" |
332 | | - monitor_format: Literal["human", "json"] = "human" |
333 | | - monitor_frequency: Literal["turn", "tool_call", "manual", "overflow"] = "turn" |
334 | | - monitor_write_mode: Literal["sync", "async"] = "sync" |
335 | | - redact_sensitive: bool = True |
336 | | - snapshot_timing: Literal["pre_optimization", "post_optimization", "both"] = "post_optimization" |
337 | | - |
338 | | - # Path validation |
339 | | - allow_absolute_paths: bool = False |
340 | | - |
341 | | - # Pruning |
342 | | - prune_after_tokens: int = 40000 |
343 | | - keep_recent_turns: int = 5 |
344 | | - |
345 | | - # Source tracking |
346 | | - source: str = "defaults" # defaults, env, config_file, cli |
347 | | - |
348 | | - def to_dict(self) -> Dict[str, Any]: |
349 | | - result = { |
350 | | - "auto_compact": self.auto_compact, |
351 | | - "compact_threshold": self.compact_threshold, |
352 | | - "strategy": self.strategy.value, |
353 | | - "compression_min_gain_pct": self.compression_min_gain_pct, |
354 | | - "compression_max_attempts": self.compression_max_attempts, |
355 | | - "output_reserve": self.output_reserve, |
356 | | - "history_ratio": self.history_ratio, |
357 | | - "default_tool_output_max": self.default_tool_output_max, |
358 | | - "tool_budgets": {k: v.to_dict() for k, v in self.tool_budgets.items()}, |
359 | | - "protected_tools": self.protected_tools, |
360 | | - "estimation_mode": self.estimation_mode.value, |
361 | | - "log_estimation_mismatch": self.log_estimation_mismatch, |
362 | | - "mismatch_threshold_pct": self.mismatch_threshold_pct, |
363 | | - "monitor_enabled": self.monitor_enabled, |
364 | | - "monitor_path": self.monitor_path, |
365 | | - "monitor_format": self.monitor_format, |
366 | | - "monitor_frequency": self.monitor_frequency, |
367 | | - "monitor_write_mode": self.monitor_write_mode, |
368 | | - "redact_sensitive": self.redact_sensitive, |
369 | | - "snapshot_timing": self.snapshot_timing, |
370 | | - "allow_absolute_paths": self.allow_absolute_paths, |
371 | | - "prune_after_tokens": self.prune_after_tokens, |
372 | | - "keep_recent_turns": self.keep_recent_turns, |
373 | | - "llm_summarize": self.llm_summarize, |
374 | | - "smart_tool_summarize": self.smart_tool_summarize, |
375 | | - "tool_summarize_limits": self.tool_summarize_limits, |
376 | | - "source": self.source, |
377 | | - } |
378 | | - return result |
379 | | - |
380 | | - @classmethod |
381 | | - def from_env(cls) -> "ManagerConfig": |
382 | | - """Load config from environment variables.""" |
383 | | - import os |
384 | | - |
385 | | - def get_bool(key: str, default: bool) -> bool: |
386 | | - val = os.getenv(key, str(default)).lower() |
387 | | - return val in ("true", "1", "yes", "on") |
388 | | - |
389 | | - def get_float(key: str, default: float) -> float: |
390 | | - try: |
391 | | - return float(os.getenv(key, str(default))) |
392 | | - except ValueError: |
393 | | - return default |
394 | | - |
395 | | - def get_int(key: str, default: int) -> int: |
396 | | - try: |
397 | | - return int(os.getenv(key, str(default))) |
398 | | - except ValueError: |
399 | | - return default |
400 | | - |
401 | | - strategy_str = os.getenv("PRAISONAI_CONTEXT_STRATEGY", "smart") |
402 | | - try: |
403 | | - strategy = OptimizerStrategy(strategy_str) |
404 | | - except ValueError: |
405 | | - strategy = OptimizerStrategy.SMART |
406 | | - |
407 | | - estimation_str = os.getenv("PRAISONAI_CONTEXT_ESTIMATION_MODE", "heuristic") |
408 | | - try: |
409 | | - estimation_mode = EstimationMode(estimation_str) |
410 | | - except ValueError: |
411 | | - estimation_mode = EstimationMode.HEURISTIC |
412 | | - |
413 | | - return cls( |
414 | | - auto_compact=get_bool("PRAISONAI_CONTEXT_AUTO_COMPACT", True), |
415 | | - compact_threshold=get_float("PRAISONAI_CONTEXT_THRESHOLD", 0.8), |
416 | | - strategy=strategy, |
417 | | - compression_min_gain_pct=get_float("PRAISONAI_CONTEXT_COMPRESSION_MIN_GAIN", 5.0), |
418 | | - compression_max_attempts=get_int("PRAISONAI_CONTEXT_COMPRESSION_MAX_ATTEMPTS", 3), |
419 | | - output_reserve=get_int("PRAISONAI_CONTEXT_OUTPUT_RESERVE", 8000), |
420 | | - default_tool_output_max=get_int("PRAISONAI_CONTEXT_TOOL_OUTPUT_MAX", 10000), |
421 | | - estimation_mode=estimation_mode, |
422 | | - log_estimation_mismatch=get_bool("PRAISONAI_CONTEXT_LOG_MISMATCH", False), |
423 | | - monitor_enabled=get_bool("PRAISONAI_CONTEXT_MONITOR", False), |
424 | | - monitor_path=os.getenv("PRAISONAI_CONTEXT_MONITOR_PATH", "./context.txt"), |
425 | | - monitor_format=os.getenv("PRAISONAI_CONTEXT_MONITOR_FORMAT", "human"), |
426 | | - monitor_frequency=os.getenv("PRAISONAI_CONTEXT_MONITOR_FREQUENCY", "turn"), |
427 | | - monitor_write_mode=os.getenv("PRAISONAI_CONTEXT_MONITOR_WRITE_MODE", "sync"), |
428 | | - redact_sensitive=get_bool("PRAISONAI_CONTEXT_REDACT", True), |
429 | | - source="env", |
430 | | - ) |
431 | | - |
432 | | - def merge(self, **overrides) -> "ManagerConfig": |
433 | | - """Create new config with overrides applied.""" |
434 | | - current = self.to_dict() |
435 | | - |
436 | | - for key, value in overrides.items(): |
437 | | - if value is not None and key in current: |
438 | | - current[key] = value |
439 | | - |
440 | | - # Handle enum conversions |
441 | | - if isinstance(current.get("strategy"), str): |
442 | | - try: |
443 | | - current["strategy"] = OptimizerStrategy(current["strategy"]) |
444 | | - except ValueError: |
445 | | - current["strategy"] = OptimizerStrategy.SMART |
446 | | - |
447 | | - if isinstance(current.get("estimation_mode"), str): |
448 | | - try: |
449 | | - current["estimation_mode"] = EstimationMode(current["estimation_mode"]) |
450 | | - except ValueError: |
451 | | - current["estimation_mode"] = EstimationMode.HEURISTIC |
452 | | - |
453 | | - # Reconstruct tool budgets |
454 | | - tool_budgets = {} |
455 | | - for name, budget_dict in current.get("tool_budgets", {}).items(): |
456 | | - if isinstance(budget_dict, dict): |
457 | | - tool_budgets[name] = PerToolBudget(**budget_dict) |
458 | | - elif isinstance(budget_dict, PerToolBudget): |
459 | | - tool_budgets[name] = budget_dict |
460 | | - current["tool_budgets"] = tool_budgets |
461 | | - |
462 | | - return ManagerConfig(**current) |
463 | | - |
464 | | - |
465 | 292 | class ContextManager: |
466 | 293 | """ |
467 | 294 | Unified facade for context management. |
|
0 commit comments