diff --git a/v2/FuzzingBrain.sh b/v2/FuzzingBrain.sh index 0592e45..e5824e0 100755 --- a/v2/FuzzingBrain.sh +++ b/v2/FuzzingBrain.sh @@ -265,7 +265,7 @@ start_mongodb() { --restart=always \ -p 0.0.0.0:${MONGODB_PORT}:27017 \ -v fuzzingbrain-mongodb-data:/data/db \ - mongo:8.0 > /dev/null + mongo:8.2 > /dev/null # Wait for MongoDB to start print_info "Waiting for MongoDB to start..." diff --git a/v2/docker-compose.yml b/v2/docker-compose.yml index 3e16756..c6366a0 100644 --- a/v2/docker-compose.yml +++ b/v2/docker-compose.yml @@ -1,7 +1,7 @@ # SPDX-License-Identifier: Apache-2.0 services: fb-mongo: - image: mongo:8.0 + image: mongo:8.2 container_name: fb-mongo restart: unless-stopped volumes: diff --git a/v2/fuzzingbrain/fuzzer/seed_tools.py b/v2/fuzzingbrain/fuzzer/seed_tools.py index 63ee779..ebc9c8b 100644 --- a/v2/fuzzingbrain/fuzzer/seed_tools.py +++ b/v2/fuzzingbrain/fuzzer/seed_tools.py @@ -422,7 +422,7 @@ def create_seed_impl( # ============================================================================= -@tools_mcp.tool +@tools_mcp.tool() def create_seed( generator_code: str, num_seeds: int = 5, @@ -517,7 +517,7 @@ def create_seed( ) -@tools_mcp.tool +@tools_mcp.tool() def get_seed_context_info() -> Dict[str, Any]: """ Get current seed generation context information. diff --git a/v2/fuzzingbrain/llms/__init__.py b/v2/fuzzingbrain/llms/__init__.py index 85fcfdf..0b5e217 100644 --- a/v2/fuzzingbrain/llms/__init__.py +++ b/v2/fuzzingbrain/llms/__init__.py @@ -42,6 +42,7 @@ from .models import ( GPT_5_2_INSTANT, GPT_5_2_PRO, GPT_5_2_CODEX, + KIMI_FOR_CODING, GPT_5_MINI, GPT_5, O3, @@ -144,6 +145,7 @@ __all__ = [ "GPT_5_2_INSTANT", "GPT_5_2_PRO", "GPT_5_2_CODEX", + "KIMI_FOR_CODING", "O3", "O3_MINI", "GPT_5_MINI", diff --git a/v2/fuzzingbrain/llms/client.py b/v2/fuzzingbrain/llms/client.py index d9fc2d3..1dd6285 100644 --- a/v2/fuzzingbrain/llms/client.py +++ b/v2/fuzzingbrain/llms/client.py @@ -58,6 +58,10 @@ def _calculate_cost( """ # Try to get model info for pricing model_info = get_model_by_id(model_id) + if model_info is None and "/" in model_id: + # litellm-format IDs carry a provider prefix (openai/kimi-for-coding); + # the registry keys the bare ID. + model_info = get_model_by_id(model_id.split("/")[-1]) if model_info: price_input = model_info.price_input # per million @@ -454,6 +458,11 @@ class LLMClient: if model.provider == Provider.ANTHROPIC: return model.id # litellm uses raw anthropic IDs elif model.provider == Provider.OPENAI: + # kimi-for-coding is served by the OpenAI-compatible Kimi Code + # endpoint; force litellm's openai provider so it honors + # OPENAI_BASE_URL instead of guessing a moonshot route. + if model.id == "kimi-for-coding": + return f"openai/{model.id}" return model.id elif model.provider == Provider.GOOGLE: return f"gemini/{model.id}" @@ -650,6 +659,11 @@ class LLMClient: "timeout": self.config.timeout, } + # The Kimi Code endpoint only accepts temperature=1; clamp to avoid + # 400s when strategies pass lower temperatures. + if model_id == "openai/kimi-for-coding": + params["temperature"] = 1.0 + # Retry transient errors (rate limit / 5xx / timeout) on the same model # with exponential backoff before falling back to another model. litellm # honors num_retries internally and respects Retry-After. Failed attempts diff --git a/v2/fuzzingbrain/llms/models.py b/v2/fuzzingbrain/llms/models.py index cd22c7d..4e1ad27 100644 --- a/v2/fuzzingbrain/llms/models.py +++ b/v2/fuzzingbrain/llms/models.py @@ -199,6 +199,22 @@ GPT_5_1_CODEX_MAX = ModelInfo( max_output=100_000, ) + +# Kimi Code subscription endpoint (OpenAI-compatible, api.kimi.com/coding/v1). +# Subscription quota, not per-token billing: zero prices, and the OpenAI SDK +# reads OPENAI_BASE_URL for routing. +KIMI_FOR_CODING = ModelInfo( + id="kimi-for-coding", + alias="kimi-for-coding", + provider=Provider.OPENAI, + name="Kimi K2.7 Code (subscription)", + description="Kimi Code membership endpoint, coding-tuned, 262k context", + price_input=0.0, + price_output=0.0, + context_window=262_144, + max_output=32_000, +) + OPENAI_MODELS = [ GPT_5_2, GPT_5_2_INSTANT, @@ -210,6 +226,7 @@ OPENAI_MODELS = [ GPT_5, GPT_5_1, GPT_5_1_CODEX_MAX, + KIMI_FOR_CODING, ] diff --git a/v2/fuzzingbrain/tools/analyzer.py b/v2/fuzzingbrain/tools/analyzer.py index a236044..30de5f1 100644 --- a/v2/fuzzingbrain/tools/analyzer.py +++ b/v2/fuzzingbrain/tools/analyzer.py @@ -133,7 +133,7 @@ def _ensure_client() -> Dict[str, Any]: # ============================================================================= -@tools_mcp.tool +@tools_mcp.tool() def analyzer_status() -> Dict[str, Any]: """ Get Analysis Server status. @@ -163,7 +163,7 @@ def analyzer_status() -> Dict[str, Any]: # ============================================================================= -@tools_mcp.tool +@tools_mcp.tool() def get_function(function_name: str) -> Dict[str, Any]: """ Get metadata about a function (file path, line numbers, complexity, parameters). @@ -197,7 +197,7 @@ def get_function(function_name: str) -> Dict[str, Any]: } -@tools_mcp.tool +@tools_mcp.tool() def get_functions_by_file(file_path: str) -> Dict[str, Any]: """ Get all functions defined in a specific file. @@ -229,7 +229,7 @@ def get_functions_by_file(file_path: str) -> Dict[str, Any]: } -@tools_mcp.tool +@tools_mcp.tool() def search_functions(pattern: str, limit: int = 50) -> Dict[str, Any]: """ Search for functions by name pattern. @@ -261,7 +261,7 @@ def search_functions(pattern: str, limit: int = 50) -> Dict[str, Any]: } -@tools_mcp.tool +@tools_mcp.tool() def get_function_source(function_name: str) -> Dict[str, Any]: """ Get the full source code of a function. @@ -304,7 +304,7 @@ def get_function_source(function_name: str) -> Dict[str, Any]: # ============================================================================= -@tools_mcp.tool +@tools_mcp.tool() def get_callers(function_name: str) -> Dict[str, Any]: """ Get all functions that call the specified function (who calls this function?). @@ -339,7 +339,7 @@ def get_callers(function_name: str) -> Dict[str, Any]: } -@tools_mcp.tool +@tools_mcp.tool() def get_callees(function_name: str) -> Dict[str, Any]: """ Get all functions called by the specified function (what does this function call?). @@ -374,7 +374,7 @@ def get_callees(function_name: str) -> Dict[str, Any]: } -@tools_mcp.tool +@tools_mcp.tool() def get_call_graph(fuzzer_name: str, depth: int = 3) -> Dict[str, Any]: """ Get the call graph starting from a fuzzer entry point. @@ -409,7 +409,7 @@ def get_call_graph(fuzzer_name: str, depth: int = 3) -> Dict[str, Any]: } -@tools_mcp.tool +@tools_mcp.tool() def find_all_paths( from_function: str, to_function: str, @@ -455,7 +455,7 @@ def find_all_paths( # ============================================================================= -@tools_mcp.tool +@tools_mcp.tool() def check_reachability(fuzzer_name: str, function_name: str) -> Dict[str, Any]: """ Check if a function is reachable from a fuzzer entry point. @@ -494,7 +494,7 @@ def check_reachability(fuzzer_name: str, function_name: str) -> Dict[str, Any]: } -@tools_mcp.tool +@tools_mcp.tool() def get_reachable_functions(fuzzer_name: str) -> Dict[str, Any]: """ Get all functions reachable from a fuzzer entry point. @@ -528,7 +528,7 @@ def get_reachable_functions(fuzzer_name: str) -> Dict[str, Any]: } -@tools_mcp.tool +@tools_mcp.tool() def get_unreached_functions(fuzzer_name: str) -> Dict[str, Any]: """ Get functions NOT reachable from a fuzzer (coverage gaps). @@ -567,7 +567,7 @@ def get_unreached_functions(fuzzer_name: str) -> Dict[str, Any]: # ============================================================================= -@tools_mcp.tool +@tools_mcp.tool() def get_fuzzers() -> Dict[str, Any]: """ Get list of all built fuzzers. @@ -594,7 +594,7 @@ def get_fuzzers() -> Dict[str, Any]: } -@tools_mcp.tool +@tools_mcp.tool() def get_fuzzer_source(fuzzer_name: str) -> Dict[str, Any]: """ Get the source code of a fuzzer/harness. @@ -633,7 +633,7 @@ def get_fuzzer_source(fuzzer_name: str) -> Dict[str, Any]: } -@tools_mcp.tool +@tools_mcp.tool() def get_build_paths() -> Dict[str, Any]: """ Get build output paths for each sanitizer. diff --git a/v2/fuzzingbrain/tools/code_viewer.py b/v2/fuzzingbrain/tools/code_viewer.py index fb0cad2..8eb704e 100644 --- a/v2/fuzzingbrain/tools/code_viewer.py +++ b/v2/fuzzingbrain/tools/code_viewer.py @@ -129,7 +129,7 @@ def _ensure_context() -> Optional[Dict[str, Any]]: # ============================================================================= -@tools_mcp.tool +@tools_mcp.tool() def get_diff() -> Dict[str, Any]: """ Read the diff file for the current task. @@ -199,7 +199,7 @@ def get_diff_impl() -> Dict[str, Any]: # ============================================================================= -@tools_mcp.tool +@tools_mcp.tool() def get_file_content( file_path: str, start_line: Optional[int] = None, @@ -432,7 +432,7 @@ def get_file_content_impl( # ============================================================================= -@tools_mcp.tool +@tools_mcp.tool() def search_code( pattern: str, file_pattern: Optional[str] = None, @@ -782,7 +782,7 @@ def search_code_impl( # ============================================================================= -@tools_mcp.tool +@tools_mcp.tool() def list_files( directory: str = "", pattern: Optional[str] = None, diff --git a/v2/fuzzingbrain/tools/coverage.py b/v2/fuzzingbrain/tools/coverage.py index 05e452f..c620aa8 100644 --- a/v2/fuzzingbrain/tools/coverage.py +++ b/v2/fuzzingbrain/tools/coverage.py @@ -863,7 +863,7 @@ def _get_context_ranges( # ============================================================================= -@tools_mcp.tool +@tools_mcp.tool() def run_coverage( fuzzer_name: str, input_data_base64: str, @@ -969,7 +969,7 @@ def _run_coverage_in_dir( ) -@tools_mcp.tool +@tools_mcp.tool() def check_pov_reaches_target( fuzzer_name: str, pov_data_base64: str, @@ -1000,7 +1000,7 @@ def check_pov_reaches_target( } -@tools_mcp.tool +@tools_mcp.tool() def list_available_fuzzers() -> Dict[str, Any]: """ List all available coverage-instrumented fuzzers. @@ -1075,7 +1075,7 @@ def list_available_fuzzers() -> Dict[str, Any]: } -@tools_mcp.tool +@tools_mcp.tool() def get_coverage_feedback( fuzzer_name: str, input_data_base64: str, diff --git a/v2/fuzzingbrain/tools/directions.py b/v2/fuzzingbrain/tools/directions.py index de1936a..a9cbb5b 100644 --- a/v2/fuzzingbrain/tools/directions.py +++ b/v2/fuzzingbrain/tools/directions.py @@ -160,7 +160,7 @@ def get_direction_impl(direction_id: str) -> Dict[str, Any]: # ============================================================================= -@tools_mcp.tool +@tools_mcp.tool() def create_direction( name: str, risk_level: str, @@ -243,7 +243,7 @@ def create_direction( } -@tools_mcp.tool +@tools_mcp.tool() def list_directions( status: str = None, ) -> Dict[str, Any]: @@ -283,7 +283,7 @@ def list_directions( } -@tools_mcp.tool +@tools_mcp.tool() def get_direction( direction_id: str, ) -> Dict[str, Any]: diff --git a/v2/fuzzingbrain/tools/mcp_factory.py b/v2/fuzzingbrain/tools/mcp_factory.py index 185501a..b89a4c7 100644 --- a/v2/fuzzingbrain/tools/mcp_factory.py +++ b/v2/fuzzingbrain/tools/mcp_factory.py @@ -125,7 +125,7 @@ def _register_analyzer_tools(mcp: FastMCP) -> None: logger.warning(f"Connection error, invalidated client cache: {e}") return {"success": False, "error": str(e)} - @mcp.tool + @mcp.tool() @async_tool def analyzer_status() -> Dict[str, Any]: """Get Analysis Server status.""" @@ -139,7 +139,7 @@ def _register_analyzer_tools(mcp: FastMCP) -> None: except Exception as e: return _handle_client_error(e) - @mcp.tool + @mcp.tool() @async_tool def get_function(function_name: str) -> Dict[str, Any]: """ @@ -166,7 +166,7 @@ def _register_analyzer_tools(mcp: FastMCP) -> None: except Exception as e: return _handle_client_error(e) - @mcp.tool + @mcp.tool() @async_tool def get_functions_by_file(file_path: str) -> Dict[str, Any]: """ @@ -192,7 +192,7 @@ def _register_analyzer_tools(mcp: FastMCP) -> None: except Exception as e: return _handle_client_error(e) - @mcp.tool + @mcp.tool() @async_tool def search_functions(pattern: str, limit: int = 50) -> Dict[str, Any]: """ @@ -217,7 +217,7 @@ def _register_analyzer_tools(mcp: FastMCP) -> None: except Exception as e: return _handle_client_error(e) - @mcp.tool + @mcp.tool() @async_tool def get_function_source(function_name: str) -> Dict[str, Any]: """ @@ -247,7 +247,7 @@ def _register_analyzer_tools(mcp: FastMCP) -> None: except Exception as e: return _handle_client_error(e) - @mcp.tool + @mcp.tool() @async_tool def get_callers(function_name: str) -> Dict[str, Any]: """ @@ -279,7 +279,7 @@ def _register_analyzer_tools(mcp: FastMCP) -> None: except Exception as e: return _handle_client_error(e) - @mcp.tool + @mcp.tool() @async_tool def get_callees(function_name: str) -> Dict[str, Any]: """ @@ -311,7 +311,7 @@ def _register_analyzer_tools(mcp: FastMCP) -> None: except Exception as e: return _handle_client_error(e) - @mcp.tool + @mcp.tool() @async_tool def get_call_graph(fuzzer_name: str, depth: int = 3) -> Dict[str, Any]: """ @@ -342,7 +342,7 @@ def _register_analyzer_tools(mcp: FastMCP) -> None: except Exception as e: return _handle_client_error(e) - @mcp.tool + @mcp.tool() @async_tool def find_all_paths( from_function: str, to_function: str, max_depth: int = 10, max_paths: int = 20 @@ -374,7 +374,7 @@ def _register_analyzer_tools(mcp: FastMCP) -> None: except Exception as e: return _handle_client_error(e) - @mcp.tool + @mcp.tool() @async_tool def check_reachability(fuzzer_name: str, function_name: str) -> Dict[str, Any]: """ @@ -411,7 +411,7 @@ def _register_analyzer_tools(mcp: FastMCP) -> None: # they return multi-MB responses that blow up the LLM context. # The underlying AnalysisClient methods remain available for internal use. - @mcp.tool + @mcp.tool() @async_tool def get_fuzzers() -> Dict[str, Any]: """Get list of all built fuzzers.""" @@ -425,7 +425,7 @@ def _register_analyzer_tools(mcp: FastMCP) -> None: except Exception as e: return _handle_client_error(e) - @mcp.tool + @mcp.tool() @async_tool def get_fuzzer_source(fuzzer_name: str) -> Dict[str, Any]: """ @@ -454,7 +454,7 @@ def _register_analyzer_tools(mcp: FastMCP) -> None: except Exception as e: return _handle_client_error(e) - @mcp.tool + @mcp.tool() @async_tool def get_build_paths() -> Dict[str, Any]: """Get build output paths for each sanitizer.""" @@ -480,7 +480,7 @@ def _register_code_viewer_tools(mcp: FastMCP) -> None: list_files_impl, ) - @mcp.tool + @mcp.tool() @async_tool def get_diff() -> Dict[str, Any]: """ @@ -489,7 +489,7 @@ def _register_code_viewer_tools(mcp: FastMCP) -> None: """ return get_diff_impl() - @mcp.tool + @mcp.tool() @async_tool def get_file_content( file_path: str, start_line: int = None, end_line: int = None @@ -504,7 +504,7 @@ def _register_code_viewer_tools(mcp: FastMCP) -> None: """ return get_file_content_impl(file_path, start_line, end_line) - @mcp.tool + @mcp.tool() @async_tool def search_code( pattern: str = None, @@ -530,7 +530,7 @@ def _register_code_viewer_tools(mcp: FastMCP) -> None: actual_pattern, file_pattern, max_results, context_lines ) - @mcp.tool + @mcp.tool() @async_tool def list_files( directory: str = "", pattern: str = None, recursive: bool = False @@ -549,7 +549,7 @@ def _register_code_viewer_tools(mcp: FastMCP) -> None: def _register_sp_create_tools(mcp: FastMCP) -> None: """Register SP creation tool (only for SP Finding agents).""" - @mcp.tool + @mcp.tool() @async_tool def create_suspicious_point( function_name: str, @@ -578,7 +578,7 @@ def _register_sp_create_tools(mcp: FastMCP) -> None: def _register_sp_read_update_tools(mcp: FastMCP) -> None: """Register SP read/update tools (for all agents that need SP access).""" - @mcp.tool + @mcp.tool() @async_tool def update_suspicious_point( suspicious_point_id: str, @@ -622,7 +622,7 @@ def _register_sp_read_update_tools(mcp: FastMCP) -> None: reachability_reason, ) - @mcp.tool + @mcp.tool() @async_tool def list_suspicious_points() -> Dict[str, Any]: """List all suspicious points for the current task.""" @@ -630,7 +630,7 @@ def _register_sp_read_update_tools(mcp: FastMCP) -> None: return list_suspicious_points_impl() - @mcp.tool + @mcp.tool() @async_tool def get_suspicious_point(suspicious_point_id: str) -> Dict[str, Any]: """ @@ -654,7 +654,7 @@ def _register_suspicious_point_tools(mcp: FastMCP) -> None: def _register_direction_tools(mcp: FastMCP) -> None: """Register direction tools (Full-scan mode).""" - @mcp.tool + @mcp.tool() @async_tool def create_direction( name: str, @@ -681,7 +681,7 @@ def _register_direction_tools(mcp: FastMCP) -> None: name, risk_level, risk_reason, core_functions, entry_functions, code_summary ) - @mcp.tool + @mcp.tool() @async_tool def list_directions() -> Dict[str, Any]: """List all directions for the current task.""" @@ -689,7 +689,7 @@ def _register_direction_tools(mcp: FastMCP) -> None: return list_directions_impl() - @mcp.tool + @mcp.tool() @async_tool def get_direction(direction_id: str) -> Dict[str, Any]: """ @@ -714,7 +714,7 @@ def _register_pov_tools(mcp: FastMCP, worker_id: str = None) -> None: # Capture worker_id in closure - each tool will use this specific worker_id bound_worker_id = worker_id - @mcp.tool + @mcp.tool() @async_tool def get_fuzzer_info() -> Dict[str, Any]: """ @@ -725,7 +725,7 @@ def _register_pov_tools(mcp: FastMCP, worker_id: str = None) -> None: return get_fuzzer_info_impl(worker_id=bound_worker_id) - @mcp.tool + @mcp.tool() @async_tool def create_pov(generator_code: str) -> Dict[str, Any]: """ @@ -738,7 +738,7 @@ def _register_pov_tools(mcp: FastMCP, worker_id: str = None) -> None: return create_pov_impl(generator_code, worker_id=bound_worker_id) - @mcp.tool + @mcp.tool() @async_tool def verify_pov(pov_id: str) -> Dict[str, Any]: """ @@ -751,7 +751,7 @@ def _register_pov_tools(mcp: FastMCP, worker_id: str = None) -> None: return verify_pov_impl(pov_id, worker_id=bound_worker_id) - @mcp.tool + @mcp.tool() @async_tool def trace_pov( generator_code: str, target_functions: list = None, agent_msg: str = None @@ -774,7 +774,7 @@ def _register_pov_tools(mcp: FastMCP, worker_id: str = None) -> None: def _register_coverage_tools(mcp: FastMCP) -> None: """Register coverage analysis tools.""" - @mcp.tool + @mcp.tool() @async_tool def run_coverage( fuzzer_name: str, @@ -797,7 +797,7 @@ def _register_coverage_tools(mcp: FastMCP) -> None: fuzzer_name, input_data_base64, target_functions, target_files ) - @mcp.tool + @mcp.tool() @async_tool def check_pov_reaches_target( fuzzer_name: str, @@ -818,7 +818,7 @@ def _register_coverage_tools(mcp: FastMCP) -> None: fuzzer_name, pov_data_base64, target_function ) - @mcp.tool + @mcp.tool() @async_tool def list_available_fuzzers() -> Dict[str, Any]: """ @@ -828,7 +828,7 @@ def _register_coverage_tools(mcp: FastMCP) -> None: return list_fuzzers_impl() - @mcp.tool + @mcp.tool() @async_tool def get_coverage_feedback( fuzzer_name: str, @@ -861,7 +861,7 @@ def _register_seed_tools(mcp: FastMCP, worker_id: str = None) -> None: # Capture worker_id in closure bound_worker_id = worker_id - @mcp.tool + @mcp.tool() @async_tool def create_seed( generator_code: str, diff --git a/v2/fuzzingbrain/tools/pov.py b/v2/fuzzingbrain/tools/pov.py index 365ce4a..0ed9dff 100644 --- a/v2/fuzzingbrain/tools/pov.py +++ b/v2/fuzzingbrain/tools/pov.py @@ -642,7 +642,7 @@ def trace_pov_impl( # ============================================================================= -@tools_mcp.tool +@tools_mcp.tool() def get_fuzzer_info() -> Dict[str, Any]: """ Get the fuzzer harness source code and current sanitizer type. @@ -1023,7 +1023,7 @@ def _create_pov_core( } -@tools_mcp.tool +@tools_mcp.tool() def create_pov( generator_code: str, description: str = "POV generated by AI agent", @@ -1606,7 +1606,7 @@ def _verify_pov_core(pov_id: str, worker_id: str = None) -> Dict[str, Any]: } -@tools_mcp.tool +@tools_mcp.tool() def verify_pov( pov_id: str, ) -> Dict[str, Any]: @@ -1865,7 +1865,7 @@ def _analyze_trace( return f"Analysis unavailable: {e}" -@tools_mcp.tool +@tools_mcp.tool() def trace_pov( generator_code: str, target_functions: Optional[List[str]] = None, diff --git a/v2/fuzzingbrain/tools/suspicious_points.py b/v2/fuzzingbrain/tools/suspicious_points.py index 2c8ea5a..b093c22 100644 --- a/v2/fuzzingbrain/tools/suspicious_points.py +++ b/v2/fuzzingbrain/tools/suspicious_points.py @@ -277,7 +277,7 @@ def get_suspicious_point_impl(suspicious_point_id: str) -> Dict[str, Any]: # ============================================================================= -@tools_mcp.tool +@tools_mcp.tool() def create_suspicious_point( function_name: str, description: str, @@ -358,7 +358,7 @@ def create_suspicious_point( return {"success": False, "error": str(e)[:100]} -@tools_mcp.tool +@tools_mcp.tool() def update_suspicious_point( suspicious_point_id: str, is_checked: bool = None, @@ -454,7 +454,7 @@ def update_suspicious_point( } -@tools_mcp.tool +@tools_mcp.tool() def list_suspicious_points( filter_unchecked: bool = False, filter_real: bool = False, @@ -499,7 +499,7 @@ def list_suspicious_points( } -@tools_mcp.tool +@tools_mcp.tool() def get_suspicious_point(suspicious_point_id: str) -> Dict[str, Any]: """ Get details of a specific suspicious point. diff --git a/v2/fuzzingbrain/tools/utils.py b/v2/fuzzingbrain/tools/utils.py index d47e10b..647b09e 100644 --- a/v2/fuzzingbrain/tools/utils.py +++ b/v2/fuzzingbrain/tools/utils.py @@ -31,7 +31,7 @@ def async_tool(func: F) -> F: causing all concurrent agents to wait in a queue. Usage: - @mcp.tool + @mcp.tool() @async_tool def get_function_source(function_name: str) -> Dict[str, Any]: # ContextVars are preserved in the thread! diff --git a/v2/fuzzingbrain/worker/pipeline.py b/v2/fuzzingbrain/worker/pipeline.py index 18337cc..4fd05b7 100644 --- a/v2/fuzzingbrain/worker/pipeline.py +++ b/v2/fuzzingbrain/worker/pipeline.py @@ -30,7 +30,7 @@ from ..core.models import SPStatus from ..db import RepositoryManager from ..tools.analyzer import set_analyzer_context from ..fuzzer import FuzzerManager, get_fuzzer_manager -from ..llms import CLAUDE_SONNET_4_5 +from ..llms import KIMI_FOR_CODING @dataclass @@ -301,7 +301,7 @@ class AgentPipeline: fuzzer=self.fuzzer, sanitizer=self.sanitizer, scan_mode=self.scan_mode, # Use pipeline's scan_mode - model=CLAUDE_SONNET_4_5, # Force Sonnet for SP analysis + model=KIMI_FOR_CODING, # Force Sonnet for SP analysis task_id=self.task_id, worker_id=self.worker_id, # Use actual worker_id, not agent_id log_dir=self.log_dir, @@ -521,7 +521,7 @@ class AgentPipeline: pov_agent = POVAgent( fuzzer=self.fuzzer, sanitizer=self.sanitizer, - model=CLAUDE_SONNET_4_5, # Force Sonnet for POV generation + model=KIMI_FOR_CODING, # Force Sonnet for POV generation task_id=self.task_id, worker_id=self.worker_id, # Use actual worker_id, not agent_id output_dir=self.output_dir, diff --git a/v2/fuzzingbrain/worker/strategies/pov_base.py b/v2/fuzzingbrain/worker/strategies/pov_base.py index a56f907..4829c58 100644 --- a/v2/fuzzingbrain/worker/strategies/pov_base.py +++ b/v2/fuzzingbrain/worker/strategies/pov_base.py @@ -16,7 +16,7 @@ from ...core.models import SuspiciousPoint from ...tools.code_viewer import set_code_viewer_context from ...tools.analyzer import set_analyzer_context from ...tools.suspicious_points import set_sp_context -from ...llms import CLAUDE_SONNET_4_5 +from ...llms import KIMI_FOR_CODING class POVBaseStrategy(BaseStrategy): @@ -305,7 +305,7 @@ class POVBaseStrategy(BaseStrategy): fuzzer=self.fuzzer, sanitizer=self.sanitizer, scan_mode=self.scan_mode, # Use strategy's scan_mode for verify prompt - model=CLAUDE_SONNET_4_5, + model=KIMI_FOR_CODING, verbose=True, task_id=self.task_id, worker_id=self.worker_id, diff --git a/v2/fuzzingbrain/worker/strategies/pov_delta.py b/v2/fuzzingbrain/worker/strategies/pov_delta.py index 9d06ea2..8502520 100644 --- a/v2/fuzzingbrain/worker/strategies/pov_delta.py +++ b/v2/fuzzingbrain/worker/strategies/pov_delta.py @@ -25,7 +25,7 @@ from ...analysis.diff_parser import ( ) from ...core.models import SuspiciousPoint from ...agents import DeltaSPGenerator -from ...llms import CLAUDE_SONNET_4_5 +from ...llms import KIMI_FOR_CODING class POVDeltaStrategy(POVBaseStrategy): @@ -61,7 +61,7 @@ class POVDeltaStrategy(POVBaseStrategy): self._sp_generator = DeltaSPGenerator( fuzzer=self.fuzzer, sanitizer=self.sanitizer, - model=CLAUDE_SONNET_4_5, + model=KIMI_FOR_CODING, verbose=True, task_id=self.task_id, worker_id=self.worker_id, diff --git a/v2/fuzzingbrain/worker/strategies/pov_fullscan.py b/v2/fuzzingbrain/worker/strategies/pov_fullscan.py index 7f1ed07..42e08f3 100644 --- a/v2/fuzzingbrain/worker/strategies/pov_fullscan.py +++ b/v2/fuzzingbrain/worker/strategies/pov_fullscan.py @@ -24,7 +24,7 @@ from ...agents import ( FullSPGenerator, LargeFullSPGenerator, ) -from ...llms.models import CLAUDE_OPUS_4_5, CLAUDE_SONNET_4_5 +from ...llms.models import KIMI_FOR_CODING from ...tools.directions import set_direction_context from ...tools.suspicious_points import set_sp_context from ...fuzzer import SeedAgent @@ -143,7 +143,7 @@ class POVFullscanStrategy(POVBaseStrategy): planning_agent = DirectionPlanningAgent( fuzzer=self.fuzzer, sanitizer=self.sanitizer, - model=CLAUDE_OPUS_4_5, # Force Opus for direction planning + model=KIMI_FOR_CODING, # Force Opus for direction planning task_id=self.task_id, worker_id=self.worker_id, log_dir=agent_log_dir, @@ -183,7 +183,7 @@ class POVFullscanStrategy(POVBaseStrategy): planning_agent = DirectionPlanningAgent( fuzzer=self.fuzzer, sanitizer=self.sanitizer, - model=CLAUDE_OPUS_4_5, # Force Opus for direction planning + model=KIMI_FOR_CODING, # Force Opus for direction planning task_id=self.task_id, worker_id=self.worker_id, log_dir=agent_log_dir, @@ -678,7 +678,7 @@ class POVFullscanStrategy(POVBaseStrategy): callees=callees, fuzzer=self.fuzzer, sanitizer=self.sanitizer, - model=CLAUDE_OPUS_4_5, # Force Opus for large function analysis + model=KIMI_FOR_CODING, # Force Opus for large function analysis direction_id=direction_id, task_id=self.task_id, worker_id=self.worker_id, @@ -695,7 +695,7 @@ class POVFullscanStrategy(POVBaseStrategy): callees=callees, fuzzer=self.fuzzer, sanitizer=self.sanitizer, - model=CLAUDE_SONNET_4_5, # Force Sonnet for function analysis + model=KIMI_FOR_CODING, # Force Sonnet for function analysis direction_id=direction_id, task_id=self.task_id, worker_id=self.worker_id, @@ -802,7 +802,7 @@ class POVFullscanStrategy(POVBaseStrategy): worker_id=self.worker_id, # ObjectId for MongoDB linking fuzzer=self.fuzzer, sanitizer=self.sanitizer, - model=CLAUDE_OPUS_4_5, # Force Opus for seed generation + model=KIMI_FOR_CODING, # Force Opus for seed generation fuzzer_manager=fuzzer_manager, repos=self.repos, fuzzer_source=fuzzer_code, diff --git a/v2/fuzzingbrain/worker/strategies/pov_strategy.py b/v2/fuzzingbrain/worker/strategies/pov_strategy.py index 0cee2fd..82fe1d0 100644 --- a/v2/fuzzingbrain/worker/strategies/pov_strategy.py +++ b/v2/fuzzingbrain/worker/strategies/pov_strategy.py @@ -35,7 +35,7 @@ from ...tools.code_viewer import set_code_viewer_context from ...tools.directions import set_direction_context from ...tools.analyzer import set_analyzer_context from ...tools.suspicious_points import set_sp_context -from ...llms import CLAUDE_SONNET_4_5 +from ...llms import KIMI_FOR_CODING class POVStrategy(BaseStrategy): @@ -74,7 +74,7 @@ class POVStrategy(BaseStrategy): self._sp_generator = DeltaSPGenerator( fuzzer=self.fuzzer, sanitizer=self.sanitizer, - model=CLAUDE_SONNET_4_5, + model=KIMI_FOR_CODING, verbose=True, task_id=self.task_id, worker_id=self.worker_id, @@ -86,7 +86,7 @@ class POVStrategy(BaseStrategy): fuzzer=self.fuzzer, sanitizer=self.sanitizer, scan_mode="delta", - model=CLAUDE_SONNET_4_5, + model=KIMI_FOR_CODING, verbose=True, task_id=self.task_id, worker_id=self.worker_id,