Add Consensus Tool for Multi-Model Perspective Gathering (#67)
* WIP Refactor resolving mode_names, should be done once at MCP call boundary Pass around model context instead Consensus tool allows one to get a consensus from multiple models, optionally assigning one a 'for' or 'against' stance to find nuanced responses. * Deduplication of model resolution, model_context should be available before reaching deeper parts of the code Improved abstraction when building conversations Throw programmer errors early * Guardrails Support for `model:option` format at MCP boundary so future tools can use additional options if needed instead of handling this only for consensus Model name now supports an optional ":option" for future use * Simplified async flow * Improved model for request to support natural language Simplified async flow * Improved model for request to support natural language Simplified async flow * Fix consensus tool async/sync patterns to match codebase standards CRITICAL FIXES: - Converted _get_consensus_responses from async to sync (matches other tools) - Converted store_conversation_turn from async to sync (add_turn is synchronous) - Removed unnecessary asyncio imports and sleep calls - Fixed ClosedResourceError in MCP protocol during long consensus operations PATTERN ALIGNMENT: - Consensus tool now follows same sync patterns as all other tools - Only execute() and prepare_prompt() are async (base class requirement) - All internal operations are synchronous like analyze, chat, debug, etc. TESTING: - MCP simulation test now passes: consensus_stance ✅ - Two-model consensus works correctly in ~35 seconds - Unknown stance handling defaults to neutral with warnings - All 9 unit tests pass (100% success rate) The consensus tool async patterns were anomalous in the codebase. This fix aligns it with the established synchronous patterns used by all other tools while maintaining full functionality. 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com> * Fixed call order and added new test * Cleanup dead comments Docs for the new tool Improved tests --------- Co-authored-by: Claude <noreply@anthropic.com>
This commit is contained in:
committed by
GitHub
parent
9b98df650b
commit
95556ba9ea
@@ -196,9 +196,7 @@ def detect_file_type(file_path: str) -> str:
|
||||
"""
|
||||
Detect file type for appropriate processing strategy.
|
||||
|
||||
NOTE: This function is currently not used for line number auto-detection
|
||||
due to backward compatibility requirements. It is intended for future
|
||||
features requiring specific file type handling (e.g., image processing,
|
||||
This function is intended for specific file type handling (e.g., image processing,
|
||||
binary file analysis, or enhanced file filtering).
|
||||
|
||||
Args:
|
||||
@@ -247,7 +245,7 @@ def should_add_line_numbers(file_path: str, include_line_numbers: Optional[bool]
|
||||
if include_line_numbers is not None:
|
||||
return include_line_numbers
|
||||
|
||||
# Default: DO NOT add line numbers (backwards compatibility)
|
||||
# Default: DO NOT add line numbers
|
||||
# Tools that want line numbers must explicitly request them
|
||||
return False
|
||||
|
||||
@@ -1026,7 +1024,7 @@ def read_file_safely(file_path: str, max_size: int = 10 * 1024 * 1024) -> Option
|
||||
return None
|
||||
|
||||
|
||||
def check_total_file_size(files: list[str], model_name: Optional[str] = None) -> Optional[dict]:
|
||||
def check_total_file_size(files: list[str], model_name: str) -> Optional[dict]:
|
||||
"""
|
||||
Check if total file sizes would exceed token threshold before embedding.
|
||||
|
||||
@@ -1034,9 +1032,12 @@ def check_total_file_size(files: list[str], model_name: Optional[str] = None) ->
|
||||
No partial inclusion - either all files fit or request is rejected.
|
||||
This forces Claude to make better file selection decisions.
|
||||
|
||||
This function MUST be called with the effective model name (after resolution).
|
||||
It should never receive 'auto' or None - model resolution happens earlier.
|
||||
|
||||
Args:
|
||||
files: List of file paths to check
|
||||
model_name: Model name for context-aware thresholds, or None for default
|
||||
model_name: The resolved model name for context-aware thresholds (required)
|
||||
|
||||
Returns:
|
||||
Dict with `code_too_large` response if too large, None if acceptable
|
||||
@@ -1044,17 +1045,14 @@ def check_total_file_size(files: list[str], model_name: Optional[str] = None) ->
|
||||
if not files:
|
||||
return None
|
||||
|
||||
# Get model-specific token allocation (dynamic thresholds)
|
||||
if not model_name:
|
||||
from config import DEFAULT_MODEL
|
||||
# Validate we have a proper model name (not auto or None)
|
||||
if not model_name or model_name.lower() == "auto":
|
||||
raise ValueError(
|
||||
f"check_total_file_size called with unresolved model: '{model_name}'. "
|
||||
"Model must be resolved before file size checking."
|
||||
)
|
||||
|
||||
model_name = DEFAULT_MODEL
|
||||
|
||||
# Handle auto mode gracefully
|
||||
if model_name.lower() == "auto":
|
||||
from providers.registry import ModelProviderRegistry
|
||||
|
||||
model_name = ModelProviderRegistry.get_preferred_fallback_model()
|
||||
logger.info(f"File size check: Using model '{model_name}' for token limit calculation")
|
||||
|
||||
from utils.model_context import ModelContext
|
||||
|
||||
@@ -1091,6 +1089,7 @@ def check_total_file_size(files: list[str], model_name: Optional[str] = None) ->
|
||||
"file_count": file_count,
|
||||
"threshold_percent": threshold_percent,
|
||||
"model_context_window": context_window,
|
||||
"model_name": model_name,
|
||||
"instructions": "Reduce file selection and try again - all files must fit within budget",
|
||||
},
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user