|
3 | 3 | import json |
4 | 4 | import platform |
5 | 5 | import os |
| 6 | +import threading |
6 | 7 | from typing import Optional, Dict, Any, Union, Iterator, List, Literal, cast, TYPE_CHECKING |
7 | 8 |
|
8 | 9 | import requests |
@@ -510,8 +511,11 @@ def _convert_tools_parameter( |
510 | 511 | return None |
511 | 512 |
|
512 | 513 | # Cache the tools configuration for direct calling |
513 | | - self._tools_config = tools |
514 | | - self._mcp_manager = None # Clear cached MCP manager |
| 514 | + self._client._tools_config = tools |
| 515 | + self._client._mcp_manager = None # Clear cached MCP manager |
| 516 | + |
| 517 | + # Also store in global configuration for cross-client access |
| 518 | + self._client._store_global_tools_config(tools) |
515 | 519 |
|
516 | 520 | try: |
517 | 521 | from .tools.compatibility import ensure_openai_format |
@@ -869,6 +873,11 @@ class HAI(BaseClient): |
869 | 873 |
|
870 | 874 | This is the main entry point for interacting with the HelpingAI API. |
871 | 875 | """ |
| 876 | + |
| 877 | + # Global tools configuration storage for cross-client access |
| 878 | + _global_tools_config = None |
| 879 | + _global_config_lock = threading.Lock() |
| 880 | + |
872 | 881 | def __init__( |
873 | 882 | self, |
874 | 883 | api_key: Optional[str] = None, |
@@ -915,6 +924,46 @@ def configure_tools(self, tools: Optional[Union[List[Dict[str, Any]], List, str] |
915 | 924 | # Clear cached MCP manager to force reinitialization |
916 | 925 | self._mcp_manager = None |
917 | 926 |
|
| 927 | + # Also store in global configuration for cross-client access |
| 928 | + self._store_global_tools_config(tools) |
| 929 | + |
| 930 | + def _store_global_tools_config(self, tools: Optional[Union[List[Dict[str, Any]], List, str]]) -> None: |
| 931 | + """Store tools configuration globally for cross-client access. |
| 932 | + |
| 933 | + Args: |
| 934 | + tools: Tools configuration to store globally |
| 935 | + """ |
| 936 | + with self._global_config_lock: |
| 937 | + self.__class__._global_tools_config = tools |
| 938 | + |
| 939 | + def _get_effective_tools_config(self) -> Optional[Union[List[Dict[str, Any]], List, str]]: |
| 940 | + """Get effective tools configuration, checking instance and global storage. |
| 941 | + |
| 942 | + Returns: |
| 943 | + Tools configuration from instance or global storage |
| 944 | + """ |
| 945 | + # First check instance configuration |
| 946 | + if self._tools_config is not None: |
| 947 | + return self._tools_config |
| 948 | + |
| 949 | + # Fall back to global configuration |
| 950 | + with self._global_config_lock: |
| 951 | + return self.__class__._global_tools_config |
| 952 | + |
| 953 | + def _convert_tools_parameter( |
| 954 | + self, |
| 955 | + tools: Optional[Union[List[Dict[str, Any]], List, str]] |
| 956 | + ) -> Optional[List[Dict[str, Any]]]: |
| 957 | + """Convenience method to access ChatCompletions tool conversion. |
| 958 | + |
| 959 | + Args: |
| 960 | + tools: Tools in various formats |
| 961 | + |
| 962 | + Returns: |
| 963 | + List of OpenAI-compatible tool definitions or None |
| 964 | + """ |
| 965 | + return self.chat.completions._convert_tools_parameter(tools) |
| 966 | + |
918 | 967 | def call(self, tool_name: str, arguments: Union[Dict[str, Any], str, set]) -> Any: |
919 | 968 | """ |
920 | 969 | Directly call a tool by name with the given arguments. |
@@ -964,13 +1013,14 @@ def call(self, tool_name: str, arguments: Union[Dict[str, Any], str, set]) -> An |
964 | 1013 | result = fn_tool.call(processed_args) |
965 | 1014 | return result |
966 | 1015 |
|
967 | | - # If not found, check if it's an MCP tool using cached configuration |
| 1016 | + # If not found, check if it's an MCP tool using effective configuration |
968 | 1017 | # MCP tools are named with pattern: {server_name}-{tool_name} |
969 | | - if self._tools_config: |
| 1018 | + effective_tools_config = self._get_effective_tools_config() |
| 1019 | + if effective_tools_config: |
970 | 1020 | try: |
971 | | - # Initialize MCP manager with cached configuration if needed |
| 1021 | + # Initialize MCP manager with effective configuration if needed |
972 | 1022 | if not self._mcp_manager: |
973 | | - self._mcp_manager = self._get_mcp_manager_for_tools() |
| 1023 | + self._mcp_manager = self._get_mcp_manager_for_tools(effective_tools_config) |
974 | 1024 |
|
975 | 1025 | if self._mcp_manager and self._mcp_manager.clients: |
976 | 1026 | # Check if any MCP client has this tool |
@@ -1000,33 +1050,39 @@ def call(self, tool_name: str, arguments: Union[Dict[str, Any], str, set]) -> An |
1000 | 1050 | error_msg = f"Tool '{tool_name}' not found" |
1001 | 1051 |
|
1002 | 1052 | # Check if this looks like an MCP tool name pattern |
1003 | | - if '-' in tool_name and self._tools_config: |
| 1053 | + if '-' in tool_name and effective_tools_config: |
1004 | 1054 | error_msg += f". Tool '{tool_name}' appears to be an MCP tool but MCP servers may not be properly initialized. Check that the MCP server is running and accessible." |
1005 | | - elif '-' in tool_name and not self._tools_config: |
1006 | | - error_msg += f". Tool '{tool_name}' appears to be an MCP tool but no tools are configured. Use client.configure_tools() to set up MCP servers." |
1007 | | - elif not self._tools_config: |
| 1055 | + elif '-' in tool_name and not effective_tools_config: |
| 1056 | + error_msg += f". Tool '{tool_name}' appears to be an MCP tool but no tools are configured. Use client.configure_tools() or pass tools to chat.completions.create()" |
| 1057 | + elif not effective_tools_config: |
1008 | 1058 | error_msg += ". No tools configured - use client.configure_tools() or pass tools to chat.completions.create()" |
1009 | 1059 | else: |
1010 | 1060 | error_msg += " in registry, built-in tools, or configured MCP tools" |
1011 | 1061 |
|
1012 | 1062 | raise ValueError(error_msg) |
1013 | 1063 |
|
1014 | | - def _get_mcp_manager_for_tools(self) -> Optional[Any]: |
1015 | | - """Get or create MCP manager using cached tools configuration. |
| 1064 | + def _get_mcp_manager_for_tools(self, tools_config: Optional[Union[List[Dict[str, Any]], List, str]] = None) -> Optional[Any]: |
| 1065 | + """Get or create MCP manager using specified or cached tools configuration. |
| 1066 | + |
| 1067 | + Args: |
| 1068 | + tools_config: Optional tools configuration to use. If None, uses effective config. |
1016 | 1069 | |
1017 | 1070 | Returns: |
1018 | 1071 | MCPManager instance with tools configured, or None if no MCP config found |
1019 | 1072 | """ |
1020 | | - if not self._tools_config: |
| 1073 | + if tools_config is None: |
| 1074 | + tools_config = self._get_effective_tools_config() |
| 1075 | + |
| 1076 | + if not tools_config: |
1021 | 1077 | return None |
1022 | 1078 |
|
1023 | 1079 | try: |
1024 | 1080 | from .tools.mcp_manager import MCPManager |
1025 | 1081 |
|
1026 | 1082 | # Find MCP server configs in the tools configuration |
1027 | 1083 | mcp_configs = [] |
1028 | | - if isinstance(self._tools_config, list): |
1029 | | - for item in self._tools_config: |
| 1084 | + if isinstance(tools_config, list): |
| 1085 | + for item in tools_config: |
1030 | 1086 | if isinstance(item, dict) and "mcpServers" in item: |
1031 | 1087 | mcp_configs.append(item) |
1032 | 1088 |
|
|
0 commit comments