Make sure file modification tools are not available when research only flag is used.

This commit is contained in:
AI Christianson 2024-12-17 10:56:22 -05:00
parent 3be54fac2f
commit f4c7c8f150
3 changed files with 144 additions and 39 deletions

View File

@ -13,6 +13,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Adjust research prompt to make sure related files are related to the base task, not just the research subtask. - Adjust research prompt to make sure related files are related to the base task, not just the research subtask.
- Track tasks by ID and allow them to be deleted. - Track tasks by ID and allow them to be deleted.
- Make one_shot_completed tool available to research agent. - Make one_shot_completed tool available to research agent.
- Make sure file modification tools are not available when research only flag is used.
## [0.6.0] - 2024-12-17 ## [0.6.0] - 2024-12-17

View File

@ -30,17 +30,29 @@ import time
from anthropic import APIError, APITimeoutError, RateLimitError, InternalServerError from anthropic import APIError, APITimeoutError, RateLimitError, InternalServerError
from ra_aid.llm import initialize_llm from ra_aid.llm import initialize_llm
# Common tools used across multiple agents # Read-only tools that don't modify system state
COMMON_TOOLS = [ READ_ONLY_TOOLS = [
emit_related_files, emit_related_files,
emit_key_facts, emit_key_facts,
delete_key_facts, delete_key_facts,
emit_key_snippets, emit_key_snippets,
delete_key_snippets, delete_key_snippets,
read_file_tool, read_file_tool,
write_file_tool,
fuzzy_find_project_files, fuzzy_find_project_files,
ripgrep_search, ripgrep_search
]
# Tools that can modify files or system state
MODIFICATION_TOOLS = [
write_file_tool,
file_str_replace,
run_shell_command,
run_programming_task
]
# Common tools used across multiple agents
COMMON_TOOLS = READ_ONLY_TOOLS + [
write_file_tool,
file_str_replace file_str_replace
] ]
@ -134,56 +146,55 @@ implementation_memory = MemorySaver()
def get_research_tools(research_only: bool = False, expert_enabled: bool = True) -> list: def get_research_tools(research_only: bool = False, expert_enabled: bool = True) -> list:
"""Get the list of research tools based on mode and whether expert is enabled.""" """Get the list of research tools based on mode and whether expert is enabled."""
tools = RESEARCH_TOOLS.copy() # Start with research-specific tools including list_directory_tree # Start with read-only tools
tools = READ_ONLY_TOOLS.copy()
# Add common tools # Add research tools except run_shell_command
tools.extend(COMMON_TOOLS.copy()) research_tools = [t for t in RESEARCH_TOOLS if t != run_shell_command]
tools.extend(research_tools)
# Add modification tools if not research_only
if not research_only:
tools.extend(MODIFICATION_TOOLS)
tools.append(request_complex_implementation)
# Add expert tools if enabled
if expert_enabled: if expert_enabled:
tools.extend(EXPERT_TOOLS) tools.extend(EXPERT_TOOLS)
if not research_only:
tools.append(request_complex_implementation)
return tools return tools
def get_planning_tools(expert_enabled: bool = True) -> list: def get_planning_tools(expert_enabled: bool = True) -> list:
tools = [ """Get the list of planning tools based on whether expert is enabled."""
list_directory_tree, # Start with common tools
tools = COMMON_TOOLS.copy()
# Add planning-specific tools
planning_tools = [
emit_plan, emit_plan,
emit_task, emit_task,
swap_task_order, swap_task_order
emit_related_files,
emit_key_facts,
delete_key_facts,
emit_key_snippets,
delete_key_snippets,
read_file_tool,
fuzzy_find_project_files,
ripgrep_search
] ]
tools.extend(planning_tools)
# Add expert tools if enabled
if expert_enabled: if expert_enabled:
tools.append(ask_expert) tools.extend(EXPERT_TOOLS)
tools.append(emit_expert_context)
return tools return tools
def get_implementation_tools(expert_enabled: bool = True) -> list: def get_implementation_tools(expert_enabled: bool = True) -> list:
tools = [ """Get the list of implementation tools based on whether expert is enabled."""
list_directory_tree, # Start with common tools
run_shell_command, tools = COMMON_TOOLS.copy()
run_programming_task,
emit_related_files, # Add modification tools since it's not research-only
emit_key_facts, tools.extend(MODIFICATION_TOOLS)
delete_key_facts,
emit_key_snippets, # Add expert tools if enabled
delete_key_snippets,
read_file_tool,
fuzzy_find_project_files,
ripgrep_search
]
if expert_enabled: if expert_enabled:
tools.append(ask_expert) tools.extend(EXPERT_TOOLS)
tools.append(emit_expert_context)
return tools return tools
def is_informational_query() -> bool: def is_informational_query() -> bool:

93
ra_aid/config.py Normal file
View File

@ -0,0 +1,93 @@
"""Configuration and environment validation utilities."""
import os
import sys
from dataclasses import dataclass
from typing import Tuple, List
from ra_aid import print_error
@dataclass
class ProviderConfig:
"""Configuration for a provider."""
key_name: str
base_required: bool = False
PROVIDER_CONFIGS = {
"anthropic": ProviderConfig("ANTHROPIC_API_KEY", base_required=True),
"openai": ProviderConfig("OPENAI_API_KEY", base_required=True),
"openrouter": ProviderConfig("OPENROUTER_API_KEY", base_required=True),
"openai-compatible": ProviderConfig("OPENAI_API_KEY", base_required=True),
}
def validate_environment(args) -> Tuple[bool, List[str]]:
"""Validate required environment variables and dependencies.
Args:
args: The parsed command line arguments containing:
- provider: The main LLM provider
- expert_provider: The expert LLM provider
Returns:
Tuple containing:
- bool: Whether expert mode is enabled
- List[str]: List of missing expert configuration items
Raises:
SystemExit: If required base environment variables are missing
"""
missing = []
provider = args.provider
expert_provider = args.expert_provider
# Check API keys based on provider configs
if provider in PROVIDER_CONFIGS:
config = PROVIDER_CONFIGS[provider]
if config.base_required and not os.environ.get(config.key_name):
missing.append(f'{config.key_name} environment variable is not set')
# Special case for openai-compatible needing base URL
if provider == "openai-compatible" and not os.environ.get('OPENAI_API_BASE'):
missing.append('OPENAI_API_BASE environment variable is not set')
expert_missing = []
if expert_provider in PROVIDER_CONFIGS:
config = PROVIDER_CONFIGS[expert_provider]
expert_key = f'EXPERT_{config.key_name}'
expert_key_missing = not os.environ.get(expert_key)
# Try fallback to base key if providers match
fallback_available = expert_provider == provider and os.environ.get(config.key_name)
if expert_key_missing and fallback_available:
os.environ[expert_key] = os.environ[config.key_name]
expert_key_missing = False
if expert_key_missing:
expert_missing.append(f'{expert_key} environment variable is not set')
# Special case for openai-compatible expert needing base URL
if expert_provider == "openai-compatible":
expert_base = 'EXPERT_OPENAI_API_BASE'
base_missing = not os.environ.get(expert_base)
base_fallback = expert_provider == provider and os.environ.get('OPENAI_API_BASE')
if base_missing and base_fallback:
os.environ[expert_base] = os.environ['OPENAI_API_BASE']
base_missing = False
if base_missing:
expert_missing.append(f'{expert_base} environment variable is not set')
# If main keys missing, we must exit immediately
if missing:
print_error("Missing required dependencies:")
for item in missing:
print_error(f"- {item}")
sys.exit(1)
# If expert keys missing, we disable expert tools instead of exiting
expert_enabled = True
if expert_missing:
expert_enabled = False
return expert_enabled, expert_missing