From 05ff663a63f2fb3f4ebafffeb095b20f187c06c6 Mon Sep 17 00:00:00 2001 From: autotntfan Date: Sun, 13 Jul 2025 22:38:55 +0800 Subject: [PATCH] fix: fix the issue of bind_tools not working. now, any model with tool tag can use toolkit (if you don't have OpenAI API key, some analysis are ignored. --- tradingagents/agents/utils/safe_bind_tools.py | 55 +++++++++++++++---- 1 file changed, 44 insertions(+), 11 deletions(-) diff --git a/tradingagents/agents/utils/safe_bind_tools.py b/tradingagents/agents/utils/safe_bind_tools.py index e5b5383d..2db6c526 100644 --- a/tradingagents/agents/utils/safe_bind_tools.py +++ b/tradingagents/agents/utils/safe_bind_tools.py @@ -25,15 +25,44 @@ log = logging.getLogger(__name__) def _ollama_has_tools_flag(model_name: str) -> bool: """ - Return True iff `ollama show ` contains `"tools": true`. + Return True iff `ollama show ` contains tools capability. If the command fails (e.g. Windows, sandbox), fall back to False. """ try: output = subprocess.check_output( shlex.split(f"ollama show {model_name}"), text=True ) - return '"tools": true' in output - except (NotImplementedError, AttributeError) as e: + # Check for multiple possible tools indicators + tools_indicators = [ + '"tools": true', # Old format + 'tools ', # New format in Capabilities section + 'tools\n', # Alternative new format + 'tools\t', # Tab-separated format + ] + + # Also check if we're in the Capabilities section + lines = output.split('\n') + in_capabilities = False + for line in lines: + line_stripped = line.strip().lower() + if 'capabilities' in line_stripped: + in_capabilities = True + elif in_capabilities and line_stripped and not line.startswith(' '): + # We've left the capabilities section + in_capabilities = False + elif in_capabilities and 'tools' in line_stripped: + log.debug("Found tools capability for model %s", model_name) + return True + + # Fallback to checking for any tools indicator + for indicator in tools_indicators: + if indicator in output: + log.debug("Found tools indicator '%s' for model %s", indicator, model_name) + return True + + log.debug("No tools capability found for model %s", model_name) + return False + except (NotImplementedError, AttributeError, subprocess.CalledProcessError) as e: log.debug("Could not inspect model %s: %s", model_name, e) return False @@ -61,14 +90,18 @@ def safe_bind_tools( if not hasattr(llm, "bind_tools"): return llm - # Special-case ChatOllama: check the `"tools": true` tag first - if isinstance(llm, BaseChatModel) and not _ollama_has_tools_flag(llm.model): - log.info( - "[safe_bind_tools] Model %s lacks tools support -- skipping.", - llm.model, - ) - return llm - + # Special-case ChatOllama: check for tools capability + if llm.__class__.__name__ == 'ChatOllama': + # Get model name from different possible attributes + model_name = getattr(llm, 'model', None) or getattr(llm, 'model_name', None) + + if model_name and not _ollama_has_tools_flag(model_name): + log.info( + "[safe_bind_tools] Model %s lacks tools support -- skipping.", + model_name, + ) + return llm + # Generic path: try to bind; fall back gracefully on failure try: return llm.bind_tools(tools)