noesium 0.1.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- noesium/core/__init__.py +4 -0
- noesium/core/agent/__init__.py +14 -0
- noesium/core/agent/base.py +227 -0
- noesium/core/consts.py +6 -0
- noesium/core/goalith/conflict/conflict.py +104 -0
- noesium/core/goalith/conflict/detector.py +53 -0
- noesium/core/goalith/decomposer/__init__.py +6 -0
- noesium/core/goalith/decomposer/base.py +46 -0
- noesium/core/goalith/decomposer/callable_decomposer.py +65 -0
- noesium/core/goalith/decomposer/llm_decomposer.py +326 -0
- noesium/core/goalith/decomposer/prompts.py +140 -0
- noesium/core/goalith/decomposer/simple_decomposer.py +61 -0
- noesium/core/goalith/errors.py +22 -0
- noesium/core/goalith/goalgraph/graph.py +526 -0
- noesium/core/goalith/goalgraph/node.py +179 -0
- noesium/core/goalith/replanner/base.py +31 -0
- noesium/core/goalith/replanner/replanner.py +36 -0
- noesium/core/goalith/service.py +26 -0
- noesium/core/llm/__init__.py +154 -0
- noesium/core/llm/base.py +152 -0
- noesium/core/llm/litellm.py +528 -0
- noesium/core/llm/llamacpp.py +487 -0
- noesium/core/llm/message.py +184 -0
- noesium/core/llm/ollama.py +459 -0
- noesium/core/llm/openai.py +520 -0
- noesium/core/llm/openrouter.py +89 -0
- noesium/core/llm/prompt.py +551 -0
- noesium/core/memory/__init__.py +11 -0
- noesium/core/memory/base.py +464 -0
- noesium/core/memory/memu/__init__.py +24 -0
- noesium/core/memory/memu/config/__init__.py +26 -0
- noesium/core/memory/memu/config/activity/config.py +46 -0
- noesium/core/memory/memu/config/event/config.py +46 -0
- noesium/core/memory/memu/config/markdown_config.py +241 -0
- noesium/core/memory/memu/config/profile/config.py +48 -0
- noesium/core/memory/memu/llm_adapter.py +129 -0
- noesium/core/memory/memu/memory/__init__.py +31 -0
- noesium/core/memory/memu/memory/actions/__init__.py +40 -0
- noesium/core/memory/memu/memory/actions/add_activity_memory.py +299 -0
- noesium/core/memory/memu/memory/actions/base_action.py +342 -0
- noesium/core/memory/memu/memory/actions/cluster_memories.py +262 -0
- noesium/core/memory/memu/memory/actions/generate_suggestions.py +198 -0
- noesium/core/memory/memu/memory/actions/get_available_categories.py +66 -0
- noesium/core/memory/memu/memory/actions/link_related_memories.py +515 -0
- noesium/core/memory/memu/memory/actions/run_theory_of_mind.py +254 -0
- noesium/core/memory/memu/memory/actions/update_memory_with_suggestions.py +514 -0
- noesium/core/memory/memu/memory/embeddings.py +130 -0
- noesium/core/memory/memu/memory/file_manager.py +306 -0
- noesium/core/memory/memu/memory/memory_agent.py +578 -0
- noesium/core/memory/memu/memory/recall_agent.py +376 -0
- noesium/core/memory/memu/memory_store.py +628 -0
- noesium/core/memory/models.py +149 -0
- noesium/core/msgbus/__init__.py +12 -0
- noesium/core/msgbus/base.py +395 -0
- noesium/core/orchestrix/__init__.py +0 -0
- noesium/core/py.typed +0 -0
- noesium/core/routing/__init__.py +20 -0
- noesium/core/routing/base.py +66 -0
- noesium/core/routing/router.py +241 -0
- noesium/core/routing/strategies/__init__.py +9 -0
- noesium/core/routing/strategies/dynamic_complexity.py +361 -0
- noesium/core/routing/strategies/self_assessment.py +147 -0
- noesium/core/routing/types.py +38 -0
- noesium/core/toolify/__init__.py +39 -0
- noesium/core/toolify/base.py +360 -0
- noesium/core/toolify/config.py +138 -0
- noesium/core/toolify/mcp_integration.py +275 -0
- noesium/core/toolify/registry.py +214 -0
- noesium/core/toolify/toolkits/__init__.py +1 -0
- noesium/core/tracing/__init__.py +37 -0
- noesium/core/tracing/langgraph_hooks.py +308 -0
- noesium/core/tracing/opik_tracing.py +144 -0
- noesium/core/tracing/token_tracker.py +166 -0
- noesium/core/utils/__init__.py +10 -0
- noesium/core/utils/logging.py +172 -0
- noesium/core/utils/statistics.py +12 -0
- noesium/core/utils/typing.py +17 -0
- noesium/core/vector_store/__init__.py +79 -0
- noesium/core/vector_store/base.py +94 -0
- noesium/core/vector_store/pgvector.py +304 -0
- noesium/core/vector_store/weaviate.py +383 -0
- noesium-0.1.0.dist-info/METADATA +525 -0
- noesium-0.1.0.dist-info/RECORD +86 -0
- noesium-0.1.0.dist-info/WHEEL +5 -0
- noesium-0.1.0.dist-info/licenses/LICENSE +21 -0
- noesium-0.1.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Unified configuration system for noesium tools.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
import os
|
|
6
|
+
from typing import Any, Dict, List, Literal, Optional
|
|
7
|
+
|
|
8
|
+
from pydantic import BaseModel, Field
|
|
9
|
+
|
|
10
|
+
from noesium.core.llm import get_llm_client
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class ToolkitConfig(BaseModel):
|
|
14
|
+
"""
|
|
15
|
+
Unified configuration for noesium toolkits.
|
|
16
|
+
|
|
17
|
+
This configuration system supports both built-in tools and MCP integration,
|
|
18
|
+
providing a consistent interface for all toolkit types.
|
|
19
|
+
"""
|
|
20
|
+
|
|
21
|
+
# Core configuration
|
|
22
|
+
mode: Literal["builtin", "mcp"] = "builtin"
|
|
23
|
+
"""Toolkit mode: 'builtin' for native tools, 'mcp' for Model Context Protocol tools"""
|
|
24
|
+
|
|
25
|
+
name: Optional[str] = None
|
|
26
|
+
"""Toolkit name for identification and logging"""
|
|
27
|
+
|
|
28
|
+
activated_tools: Optional[List[str]] = None
|
|
29
|
+
"""List of specific tools to activate. If None, all tools are activated."""
|
|
30
|
+
|
|
31
|
+
config: Dict[str, Any] = Field(default_factory=dict)
|
|
32
|
+
"""Toolkit-specific configuration parameters"""
|
|
33
|
+
|
|
34
|
+
# LLM Integration
|
|
35
|
+
llm_provider: str = Field(default_factory=lambda: os.getenv("COGENTS_LLM_PROVIDER", "openai"))
|
|
36
|
+
"""LLM provider to use (openrouter, openai, ollama, llamacpp, litellm)"""
|
|
37
|
+
|
|
38
|
+
llm_model: Optional[str] = None
|
|
39
|
+
"""Specific model to use for LLM operations"""
|
|
40
|
+
|
|
41
|
+
llm_config: Dict[str, Any] = Field(default_factory=dict)
|
|
42
|
+
"""Additional LLM configuration parameters"""
|
|
43
|
+
|
|
44
|
+
# MCP Configuration (when mode="mcp")
|
|
45
|
+
mcp_server_path: Optional[str] = None
|
|
46
|
+
"""Path to MCP server executable"""
|
|
47
|
+
|
|
48
|
+
mcp_server_args: List[str] = Field(default_factory=list)
|
|
49
|
+
"""Arguments to pass to MCP server"""
|
|
50
|
+
|
|
51
|
+
mcp_server_env: Dict[str, str] = Field(default_factory=dict)
|
|
52
|
+
"""Environment variables for MCP server"""
|
|
53
|
+
|
|
54
|
+
# Logging Configuration
|
|
55
|
+
log_level: str = Field(default_factory=lambda: os.getenv("LOG_LEVEL", "INFO"))
|
|
56
|
+
"""Logging level for this toolkit"""
|
|
57
|
+
|
|
58
|
+
enable_tracing: bool = Field(default_factory=lambda: os.getenv("COGENTS_ENABLE_TRACING", "false").lower() == "true")
|
|
59
|
+
"""Enable detailed tracing for debugging"""
|
|
60
|
+
|
|
61
|
+
class Config:
|
|
62
|
+
"""Pydantic configuration"""
|
|
63
|
+
|
|
64
|
+
arbitrary_types_allowed = True
|
|
65
|
+
|
|
66
|
+
def get_llm_client(self, **kwargs):
|
|
67
|
+
"""
|
|
68
|
+
Get an LLM client instance configured for this toolkit.
|
|
69
|
+
|
|
70
|
+
Args:
|
|
71
|
+
**kwargs: Additional arguments to pass to the LLM client
|
|
72
|
+
|
|
73
|
+
Returns:
|
|
74
|
+
BaseLLMClient: Configured LLM client instance
|
|
75
|
+
"""
|
|
76
|
+
config = {**self.llm_config, **kwargs}
|
|
77
|
+
if self.llm_model:
|
|
78
|
+
config["chat_model"] = self.llm_model
|
|
79
|
+
|
|
80
|
+
return get_llm_client(provider=self.llm_provider, **config)
|
|
81
|
+
|
|
82
|
+
def get_tool_config(self, tool_name: str) -> Dict[str, Any]:
|
|
83
|
+
"""
|
|
84
|
+
Get configuration for a specific tool.
|
|
85
|
+
|
|
86
|
+
Args:
|
|
87
|
+
tool_name: Name of the tool
|
|
88
|
+
|
|
89
|
+
Returns:
|
|
90
|
+
Dict containing tool-specific configuration
|
|
91
|
+
"""
|
|
92
|
+
return self.config.get(tool_name, {})
|
|
93
|
+
|
|
94
|
+
def is_tool_activated(self, tool_name: str) -> bool:
|
|
95
|
+
"""
|
|
96
|
+
Check if a specific tool is activated.
|
|
97
|
+
|
|
98
|
+
Args:
|
|
99
|
+
tool_name: Name of the tool to check
|
|
100
|
+
|
|
101
|
+
Returns:
|
|
102
|
+
True if the tool is activated, False otherwise
|
|
103
|
+
"""
|
|
104
|
+
if self.activated_tools is None:
|
|
105
|
+
return True
|
|
106
|
+
return tool_name in self.activated_tools
|
|
107
|
+
|
|
108
|
+
def update_config(self, **kwargs) -> "ToolkitConfig":
|
|
109
|
+
"""
|
|
110
|
+
Create a new ToolkitConfig with updated parameters.
|
|
111
|
+
|
|
112
|
+
Args:
|
|
113
|
+
**kwargs: Configuration parameters to update
|
|
114
|
+
|
|
115
|
+
Returns:
|
|
116
|
+
New ToolkitConfig instance with updated parameters
|
|
117
|
+
"""
|
|
118
|
+
data = self.model_dump()
|
|
119
|
+
data.update(kwargs)
|
|
120
|
+
return ToolkitConfig(**data)
|
|
121
|
+
|
|
122
|
+
|
|
123
|
+
def create_toolkit_config(
|
|
124
|
+
name: str, mode: Literal["builtin", "mcp"] = "builtin", activated_tools: Optional[List[str]] = None, **config_params
|
|
125
|
+
) -> ToolkitConfig:
|
|
126
|
+
"""
|
|
127
|
+
Convenience function to create a ToolkitConfig.
|
|
128
|
+
|
|
129
|
+
Args:
|
|
130
|
+
name: Toolkit name
|
|
131
|
+
mode: Toolkit mode ('builtin' or 'mcp')
|
|
132
|
+
activated_tools: List of tools to activate
|
|
133
|
+
**config_params: Additional configuration parameters
|
|
134
|
+
|
|
135
|
+
Returns:
|
|
136
|
+
Configured ToolkitConfig instance
|
|
137
|
+
"""
|
|
138
|
+
return ToolkitConfig(name=name, mode=mode, activated_tools=activated_tools, config=config_params)
|
|
@@ -0,0 +1,275 @@
|
|
|
1
|
+
"""
|
|
2
|
+
MCP (Model Context Protocol) integration for noesium tools.
|
|
3
|
+
|
|
4
|
+
Provides functionality to run external MCP servers and integrate their tools
|
|
5
|
+
into the noesium toolkit system.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from typing import Any, Dict, List, Optional
|
|
9
|
+
|
|
10
|
+
from noesium.core.utils.logging import get_logger
|
|
11
|
+
|
|
12
|
+
from .base import AsyncBaseToolkit, MCPNotAvailableError
|
|
13
|
+
from .config import ToolkitConfig
|
|
14
|
+
from .registry import register_toolkit
|
|
15
|
+
|
|
16
|
+
try:
|
|
17
|
+
import mcp.client.session as mcp_session
|
|
18
|
+
import mcp.client.stdio as mcp_stdio
|
|
19
|
+
import mcp.types as mcp_types
|
|
20
|
+
|
|
21
|
+
MCP_AVAILABLE = True
|
|
22
|
+
except ImportError:
|
|
23
|
+
mcp_session = None
|
|
24
|
+
mcp_stdio = None
|
|
25
|
+
mcp_types = None
|
|
26
|
+
MCP_AVAILABLE = False
|
|
27
|
+
|
|
28
|
+
logger = get_logger(__name__)
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
@register_toolkit("mcp")
|
|
32
|
+
class MCPToolkit(AsyncBaseToolkit):
|
|
33
|
+
"""
|
|
34
|
+
Toolkit for integrating external MCP (Model Context Protocol) servers.
|
|
35
|
+
|
|
36
|
+
This toolkit allows noesium to communicate with external MCP servers,
|
|
37
|
+
enabling integration with a wide variety of tools and services that
|
|
38
|
+
implement the MCP protocol.
|
|
39
|
+
|
|
40
|
+
Configuration:
|
|
41
|
+
- server_path: Path to the MCP server executable
|
|
42
|
+
- server_args: Arguments to pass to the server
|
|
43
|
+
- server_env: Environment variables for the server
|
|
44
|
+
- tools_filter: Optional list of tool names to include (if None, includes all)
|
|
45
|
+
"""
|
|
46
|
+
|
|
47
|
+
def __init__(self, config: ToolkitConfig = None):
|
|
48
|
+
"""
|
|
49
|
+
Initialize the MCP toolkit.
|
|
50
|
+
|
|
51
|
+
Args:
|
|
52
|
+
config: Toolkit configuration containing MCP server details
|
|
53
|
+
"""
|
|
54
|
+
super().__init__(config)
|
|
55
|
+
|
|
56
|
+
if not MCP_AVAILABLE:
|
|
57
|
+
raise MCPNotAvailableError("MCP package is not available. Install with: pip install mcp")
|
|
58
|
+
|
|
59
|
+
# MCP server configuration
|
|
60
|
+
self.server_path = self.config.mcp_server_path
|
|
61
|
+
self.server_args = self.config.mcp_server_args or []
|
|
62
|
+
self.server_env = self.config.mcp_server_env or {}
|
|
63
|
+
|
|
64
|
+
if not self.server_path:
|
|
65
|
+
raise ValueError("mcp_server_path must be specified in config")
|
|
66
|
+
|
|
67
|
+
# MCP client state
|
|
68
|
+
self.session: Optional[mcp_session.ClientSession] = None
|
|
69
|
+
self.stdio_client: Optional[mcp_stdio.StdioServerParameters] = None
|
|
70
|
+
self._available_tools: Dict[str, mcp_types.Tool] = {}
|
|
71
|
+
|
|
72
|
+
async def build(self):
|
|
73
|
+
"""Initialize the MCP client and connect to the server."""
|
|
74
|
+
await super().build()
|
|
75
|
+
|
|
76
|
+
if self.session is None:
|
|
77
|
+
await self._connect_to_server()
|
|
78
|
+
await self._discover_tools()
|
|
79
|
+
|
|
80
|
+
async def cleanup(self):
|
|
81
|
+
"""Cleanup MCP client resources."""
|
|
82
|
+
if self.session:
|
|
83
|
+
try:
|
|
84
|
+
await self.session.close()
|
|
85
|
+
except Exception as e:
|
|
86
|
+
self.logger.warning(f"Error closing MCP session: {e}")
|
|
87
|
+
|
|
88
|
+
self.session = None
|
|
89
|
+
self.stdio_client = None
|
|
90
|
+
self._available_tools = {}
|
|
91
|
+
|
|
92
|
+
await super().cleanup()
|
|
93
|
+
|
|
94
|
+
async def _connect_to_server(self):
|
|
95
|
+
"""Connect to the MCP server."""
|
|
96
|
+
try:
|
|
97
|
+
self.logger.info(f"Connecting to MCP server: {self.server_path}")
|
|
98
|
+
|
|
99
|
+
# Create stdio server parameters
|
|
100
|
+
self.stdio_client = mcp_stdio.StdioServerParameters(
|
|
101
|
+
command=self.server_path, args=self.server_args, env=self.server_env
|
|
102
|
+
)
|
|
103
|
+
|
|
104
|
+
# Create and initialize the session
|
|
105
|
+
self.session = await mcp_stdio.stdio_client(self.stdio_client)
|
|
106
|
+
|
|
107
|
+
# Initialize the session
|
|
108
|
+
await self.session.initialize()
|
|
109
|
+
|
|
110
|
+
self.logger.info("Successfully connected to MCP server")
|
|
111
|
+
|
|
112
|
+
except Exception as e:
|
|
113
|
+
self.logger.error(f"Failed to connect to MCP server: {e}")
|
|
114
|
+
raise
|
|
115
|
+
|
|
116
|
+
async def _discover_tools(self):
|
|
117
|
+
"""Discover available tools from the MCP server."""
|
|
118
|
+
try:
|
|
119
|
+
if not self.session:
|
|
120
|
+
raise RuntimeError("MCP session not initialized")
|
|
121
|
+
|
|
122
|
+
# List available tools
|
|
123
|
+
tools_response = await self.session.list_tools()
|
|
124
|
+
|
|
125
|
+
self._available_tools = {}
|
|
126
|
+
for tool in tools_response.tools:
|
|
127
|
+
# Filter tools if specified in config
|
|
128
|
+
if self.config.activated_tools is None or tool.name in self.config.activated_tools:
|
|
129
|
+
self._available_tools[tool.name] = tool
|
|
130
|
+
|
|
131
|
+
self.logger.info(f"Discovered {len(self._available_tools)} MCP tools: {list(self._available_tools.keys())}")
|
|
132
|
+
|
|
133
|
+
except Exception as e:
|
|
134
|
+
self.logger.error(f"Failed to discover MCP tools: {e}")
|
|
135
|
+
raise
|
|
136
|
+
|
|
137
|
+
async def _call_mcp_tool(self, tool_name: str, arguments: Dict[str, Any]) -> Any:
|
|
138
|
+
"""
|
|
139
|
+
Call an MCP tool with the given arguments.
|
|
140
|
+
|
|
141
|
+
Args:
|
|
142
|
+
tool_name: Name of the tool to call
|
|
143
|
+
arguments: Arguments to pass to the tool
|
|
144
|
+
|
|
145
|
+
Returns:
|
|
146
|
+
Tool execution result
|
|
147
|
+
"""
|
|
148
|
+
if not self.session:
|
|
149
|
+
raise RuntimeError("MCP session not initialized")
|
|
150
|
+
|
|
151
|
+
if tool_name not in self._available_tools:
|
|
152
|
+
raise ValueError(f"Tool '{tool_name}' not available. Available tools: {list(self._available_tools.keys())}")
|
|
153
|
+
|
|
154
|
+
try:
|
|
155
|
+
self.logger.debug(f"Calling MCP tool '{tool_name}' with arguments: {arguments}")
|
|
156
|
+
|
|
157
|
+
# Call the tool
|
|
158
|
+
result = await self.session.call_tool(tool_name, arguments)
|
|
159
|
+
|
|
160
|
+
# Extract content from the result
|
|
161
|
+
if hasattr(result, "content") and result.content:
|
|
162
|
+
# Handle different content types
|
|
163
|
+
content_parts = []
|
|
164
|
+
for content in result.content:
|
|
165
|
+
if hasattr(content, "text"):
|
|
166
|
+
content_parts.append(content.text)
|
|
167
|
+
elif hasattr(content, "data"):
|
|
168
|
+
# Handle binary data
|
|
169
|
+
content_parts.append(f"[Binary data: {len(content.data)} bytes]")
|
|
170
|
+
else:
|
|
171
|
+
content_parts.append(str(content))
|
|
172
|
+
|
|
173
|
+
return "\n".join(content_parts) if content_parts else str(result)
|
|
174
|
+
else:
|
|
175
|
+
return str(result)
|
|
176
|
+
|
|
177
|
+
except Exception as e:
|
|
178
|
+
self.logger.error(f"MCP tool call failed for '{tool_name}': {e}")
|
|
179
|
+
raise
|
|
180
|
+
|
|
181
|
+
async def get_tools_map(self) -> Dict[str, Any]:
|
|
182
|
+
"""
|
|
183
|
+
Get the mapping of tool names to their implementation functions.
|
|
184
|
+
|
|
185
|
+
This creates wrapper functions for each MCP tool that handle the
|
|
186
|
+
argument passing and result processing.
|
|
187
|
+
|
|
188
|
+
Returns:
|
|
189
|
+
Dictionary mapping tool names to callable functions
|
|
190
|
+
"""
|
|
191
|
+
tools_map = {}
|
|
192
|
+
|
|
193
|
+
for tool_name, tool_info in self._available_tools.items():
|
|
194
|
+
# Create a wrapper function for each MCP tool
|
|
195
|
+
async def tool_wrapper(tool_name=tool_name, **kwargs):
|
|
196
|
+
return await self._call_mcp_tool(tool_name, kwargs)
|
|
197
|
+
|
|
198
|
+
# Set function metadata for better introspection
|
|
199
|
+
tool_wrapper.__name__ = tool_name
|
|
200
|
+
tool_wrapper.__doc__ = tool_info.description
|
|
201
|
+
|
|
202
|
+
tools_map[tool_name] = tool_wrapper
|
|
203
|
+
|
|
204
|
+
return tools_map
|
|
205
|
+
|
|
206
|
+
async def list_available_tools(self) -> List[Dict[str, Any]]:
|
|
207
|
+
"""
|
|
208
|
+
Get information about all available MCP tools.
|
|
209
|
+
|
|
210
|
+
Returns:
|
|
211
|
+
List of tool information dictionaries
|
|
212
|
+
"""
|
|
213
|
+
tools_info = []
|
|
214
|
+
for tool_name, tool in self._available_tools.items():
|
|
215
|
+
info = {
|
|
216
|
+
"name": tool.name,
|
|
217
|
+
"description": tool.description,
|
|
218
|
+
"input_schema": tool.inputSchema if hasattr(tool, "inputSchema") else {},
|
|
219
|
+
}
|
|
220
|
+
tools_info.append(info)
|
|
221
|
+
|
|
222
|
+
return tools_info
|
|
223
|
+
|
|
224
|
+
async def get_tool_info(self, tool_name: str) -> Optional[Dict[str, Any]]:
|
|
225
|
+
"""
|
|
226
|
+
Get detailed information about a specific tool.
|
|
227
|
+
|
|
228
|
+
Args:
|
|
229
|
+
tool_name: Name of the tool
|
|
230
|
+
|
|
231
|
+
Returns:
|
|
232
|
+
Tool information dictionary or None if not found
|
|
233
|
+
"""
|
|
234
|
+
if tool_name not in self._available_tools:
|
|
235
|
+
return None
|
|
236
|
+
|
|
237
|
+
tool = self._available_tools[tool_name]
|
|
238
|
+
return {
|
|
239
|
+
"name": tool.name,
|
|
240
|
+
"description": tool.description,
|
|
241
|
+
"input_schema": tool.inputSchema if hasattr(tool, "inputSchema") else {},
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
|
|
245
|
+
def create_mcp_toolkit(
|
|
246
|
+
server_path: str,
|
|
247
|
+
server_args: Optional[List[str]] = None,
|
|
248
|
+
server_env: Optional[Dict[str, str]] = None,
|
|
249
|
+
activated_tools: Optional[List[str]] = None,
|
|
250
|
+
**config_params,
|
|
251
|
+
) -> MCPToolkit:
|
|
252
|
+
"""
|
|
253
|
+
Convenience function to create an MCP toolkit.
|
|
254
|
+
|
|
255
|
+
Args:
|
|
256
|
+
server_path: Path to the MCP server executable
|
|
257
|
+
server_args: Arguments to pass to the server
|
|
258
|
+
server_env: Environment variables for the server
|
|
259
|
+
activated_tools: List of tools to activate (None for all)
|
|
260
|
+
**config_params: Additional configuration parameters
|
|
261
|
+
|
|
262
|
+
Returns:
|
|
263
|
+
Configured MCPToolkit instance
|
|
264
|
+
"""
|
|
265
|
+
config = ToolkitConfig(
|
|
266
|
+
mode="mcp",
|
|
267
|
+
name="mcp",
|
|
268
|
+
activated_tools=activated_tools,
|
|
269
|
+
mcp_server_path=server_path,
|
|
270
|
+
mcp_server_args=server_args or [],
|
|
271
|
+
mcp_server_env=server_env or {},
|
|
272
|
+
config=config_params,
|
|
273
|
+
)
|
|
274
|
+
|
|
275
|
+
return MCPToolkit(config=config)
|
|
@@ -0,0 +1,214 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Toolkit registry for managing and discovering available toolkits.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
from typing import Dict, List, Optional, Type, Union
|
|
6
|
+
|
|
7
|
+
from noesium.core.utils.logging import get_logger
|
|
8
|
+
|
|
9
|
+
from .base import AsyncBaseToolkit, BaseToolkit
|
|
10
|
+
from .config import ToolkitConfig
|
|
11
|
+
|
|
12
|
+
logger = get_logger(__name__)
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class ToolkitRegistry:
|
|
16
|
+
"""
|
|
17
|
+
Registry for managing available toolkits.
|
|
18
|
+
|
|
19
|
+
Provides a centralized way to register, discover, and instantiate toolkits.
|
|
20
|
+
"""
|
|
21
|
+
|
|
22
|
+
_registry: Dict[str, Type[Union[BaseToolkit, AsyncBaseToolkit]]] = {}
|
|
23
|
+
|
|
24
|
+
@classmethod
|
|
25
|
+
def register(cls, name: str, toolkit_class: Type[Union[BaseToolkit, AsyncBaseToolkit]]):
|
|
26
|
+
"""
|
|
27
|
+
Register a toolkit class.
|
|
28
|
+
|
|
29
|
+
Args:
|
|
30
|
+
name: Toolkit name for lookup
|
|
31
|
+
toolkit_class: Toolkit class to register
|
|
32
|
+
"""
|
|
33
|
+
if not issubclass(toolkit_class, (BaseToolkit, AsyncBaseToolkit)):
|
|
34
|
+
raise ValueError(f"Toolkit class must inherit from BaseToolkit or AsyncBaseToolkit")
|
|
35
|
+
|
|
36
|
+
cls._registry[name] = toolkit_class
|
|
37
|
+
logger.debug(f"Registered toolkit: {name} -> {toolkit_class.__name__}")
|
|
38
|
+
|
|
39
|
+
@classmethod
|
|
40
|
+
def unregister(cls, name: str):
|
|
41
|
+
"""
|
|
42
|
+
Unregister a toolkit.
|
|
43
|
+
|
|
44
|
+
Args:
|
|
45
|
+
name: Toolkit name to unregister
|
|
46
|
+
"""
|
|
47
|
+
if name in cls._registry:
|
|
48
|
+
del cls._registry[name]
|
|
49
|
+
logger.debug(f"Unregistered toolkit: {name}")
|
|
50
|
+
|
|
51
|
+
@classmethod
|
|
52
|
+
def get_toolkit_class(cls, name: str) -> Type[Union[BaseToolkit, AsyncBaseToolkit]]:
|
|
53
|
+
"""
|
|
54
|
+
Get a registered toolkit class by name.
|
|
55
|
+
|
|
56
|
+
Args:
|
|
57
|
+
name: Toolkit name
|
|
58
|
+
|
|
59
|
+
Returns:
|
|
60
|
+
Toolkit class
|
|
61
|
+
|
|
62
|
+
Raises:
|
|
63
|
+
KeyError: If toolkit is not registered
|
|
64
|
+
"""
|
|
65
|
+
if name not in cls._registry:
|
|
66
|
+
raise KeyError(f"Toolkit '{name}' not found. Available toolkits: {list(cls._registry.keys())}")
|
|
67
|
+
return cls._registry[name]
|
|
68
|
+
|
|
69
|
+
@classmethod
|
|
70
|
+
def list_toolkits(cls) -> List[str]:
|
|
71
|
+
"""
|
|
72
|
+
Get list of registered toolkit names.
|
|
73
|
+
|
|
74
|
+
Returns:
|
|
75
|
+
List of toolkit names
|
|
76
|
+
"""
|
|
77
|
+
return list(cls._registry.keys())
|
|
78
|
+
|
|
79
|
+
@classmethod
|
|
80
|
+
def create_toolkit(
|
|
81
|
+
cls, name: str, config: Optional[Union[ToolkitConfig, Dict]] = None
|
|
82
|
+
) -> Union[BaseToolkit, AsyncBaseToolkit]:
|
|
83
|
+
"""
|
|
84
|
+
Create a toolkit instance by name.
|
|
85
|
+
|
|
86
|
+
Args:
|
|
87
|
+
name: Toolkit name
|
|
88
|
+
config: Toolkit configuration
|
|
89
|
+
|
|
90
|
+
Returns:
|
|
91
|
+
Toolkit instance
|
|
92
|
+
|
|
93
|
+
Raises:
|
|
94
|
+
KeyError: If toolkit is not registered
|
|
95
|
+
"""
|
|
96
|
+
toolkit_class = cls.get_toolkit_class(name)
|
|
97
|
+
return toolkit_class(config=config)
|
|
98
|
+
|
|
99
|
+
@classmethod
|
|
100
|
+
def is_registered(cls, name: str) -> bool:
|
|
101
|
+
"""
|
|
102
|
+
Check if a toolkit is registered.
|
|
103
|
+
|
|
104
|
+
Args:
|
|
105
|
+
name: Toolkit name
|
|
106
|
+
|
|
107
|
+
Returns:
|
|
108
|
+
True if registered, False otherwise
|
|
109
|
+
"""
|
|
110
|
+
return name in cls._registry
|
|
111
|
+
|
|
112
|
+
@classmethod
|
|
113
|
+
def clear(cls):
|
|
114
|
+
"""Clear all registered toolkits."""
|
|
115
|
+
cls._registry.clear()
|
|
116
|
+
logger.debug("Cleared toolkit registry")
|
|
117
|
+
|
|
118
|
+
|
|
119
|
+
def register_toolkit(name: str):
|
|
120
|
+
"""
|
|
121
|
+
Decorator for registering toolkit classes.
|
|
122
|
+
|
|
123
|
+
Args:
|
|
124
|
+
name: Toolkit name for registration
|
|
125
|
+
|
|
126
|
+
Example:
|
|
127
|
+
@register_toolkit("search")
|
|
128
|
+
class SearchToolkit(AsyncBaseToolkit):
|
|
129
|
+
pass
|
|
130
|
+
"""
|
|
131
|
+
|
|
132
|
+
def decorator(toolkit_class: Type[Union[BaseToolkit, AsyncBaseToolkit]]):
|
|
133
|
+
ToolkitRegistry.register(name, toolkit_class)
|
|
134
|
+
return toolkit_class
|
|
135
|
+
|
|
136
|
+
return decorator
|
|
137
|
+
|
|
138
|
+
|
|
139
|
+
def get_toolkit(name: str, config: Optional[Union[ToolkitConfig, Dict]] = None) -> Union[BaseToolkit, AsyncBaseToolkit]:
|
|
140
|
+
"""
|
|
141
|
+
Convenience function to get a toolkit instance.
|
|
142
|
+
|
|
143
|
+
Args:
|
|
144
|
+
name: Toolkit name
|
|
145
|
+
config: Toolkit configuration
|
|
146
|
+
|
|
147
|
+
Returns:
|
|
148
|
+
Toolkit instance
|
|
149
|
+
"""
|
|
150
|
+
return ToolkitRegistry.create_toolkit(name, config)
|
|
151
|
+
|
|
152
|
+
|
|
153
|
+
def get_toolkits_map(
|
|
154
|
+
names: Optional[List[str]] = None, configs: Optional[Dict[str, Union[ToolkitConfig, Dict]]] = None
|
|
155
|
+
) -> Dict[str, Union[BaseToolkit, AsyncBaseToolkit]]:
|
|
156
|
+
"""
|
|
157
|
+
Get multiple toolkit instances as a mapping.
|
|
158
|
+
|
|
159
|
+
Args:
|
|
160
|
+
names: List of toolkit names (if None, gets all registered toolkits)
|
|
161
|
+
configs: Mapping of toolkit names to their configurations
|
|
162
|
+
|
|
163
|
+
Returns:
|
|
164
|
+
Dict mapping toolkit names to instances
|
|
165
|
+
"""
|
|
166
|
+
if names is None:
|
|
167
|
+
names = ToolkitRegistry.list_toolkits()
|
|
168
|
+
|
|
169
|
+
if configs is None:
|
|
170
|
+
configs = {}
|
|
171
|
+
|
|
172
|
+
toolkits = {}
|
|
173
|
+
for name in names:
|
|
174
|
+
config = configs.get(name)
|
|
175
|
+
toolkits[name] = ToolkitRegistry.create_toolkit(name, config)
|
|
176
|
+
|
|
177
|
+
return toolkits
|
|
178
|
+
|
|
179
|
+
|
|
180
|
+
# Auto-discovery of built-in toolkits
|
|
181
|
+
def _discover_builtin_toolkits():
|
|
182
|
+
"""
|
|
183
|
+
Discover and register built-in toolkits.
|
|
184
|
+
|
|
185
|
+
This function attempts to import and register all built-in toolkit modules.
|
|
186
|
+
"""
|
|
187
|
+
import importlib
|
|
188
|
+
import pkgutil
|
|
189
|
+
from pathlib import Path
|
|
190
|
+
|
|
191
|
+
# Get the toolkits directory
|
|
192
|
+
toolkits_dir = Path(__file__).parent / "toolkits"
|
|
193
|
+
|
|
194
|
+
if not toolkits_dir.exists():
|
|
195
|
+
return
|
|
196
|
+
|
|
197
|
+
# Import all toolkit modules
|
|
198
|
+
for module_info in pkgutil.iter_modules([str(toolkits_dir)]):
|
|
199
|
+
if module_info.name.startswith("_"):
|
|
200
|
+
continue
|
|
201
|
+
|
|
202
|
+
try:
|
|
203
|
+
module_name = f"noesium.core.toolify.toolkits.{module_info.name}"
|
|
204
|
+
importlib.import_module(module_name)
|
|
205
|
+
logger.debug(f"Discovered toolkit module: {module_name}")
|
|
206
|
+
except ImportError as e:
|
|
207
|
+
logger.warning(f"Failed to import toolkit module {module_name}: {e}")
|
|
208
|
+
|
|
209
|
+
|
|
210
|
+
# Initialize built-in toolkits on import
|
|
211
|
+
try:
|
|
212
|
+
_discover_builtin_toolkits()
|
|
213
|
+
except Exception as e:
|
|
214
|
+
logger.warning(f"Failed to discover built-in toolkits: {e}")
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
# Core built-in toolkits
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Tracing and observability modules for the noesium framework.
|
|
3
|
+
|
|
4
|
+
This package provides:
|
|
5
|
+
- Opik tracing configuration for LLM communications
|
|
6
|
+
- Token usage tracking for custom LLM clients
|
|
7
|
+
- LangGraph hooks and callbacks for monitoring
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
from .langgraph_hooks import NodeLoggingCallback, TokenUsageCallback
|
|
11
|
+
from .opik_tracing import configure_opik, create_opik_trace, get_opik_project, is_opik_enabled
|
|
12
|
+
from .token_tracker import (
|
|
13
|
+
TokenUsage,
|
|
14
|
+
TokenUsageTracker,
|
|
15
|
+
estimate_token_usage,
|
|
16
|
+
extract_token_usage_from_openai_response,
|
|
17
|
+
get_token_tracker,
|
|
18
|
+
record_token_usage,
|
|
19
|
+
)
|
|
20
|
+
|
|
21
|
+
__all__ = [
|
|
22
|
+
# LangGraph hooks
|
|
23
|
+
"NodeLoggingCallback",
|
|
24
|
+
"TokenUsageCallback",
|
|
25
|
+
# Opik tracing
|
|
26
|
+
"configure_opik",
|
|
27
|
+
"create_opik_trace",
|
|
28
|
+
"get_opik_project",
|
|
29
|
+
"is_opik_enabled",
|
|
30
|
+
# Token tracking
|
|
31
|
+
"TokenUsage",
|
|
32
|
+
"TokenUsageTracker",
|
|
33
|
+
"estimate_token_usage",
|
|
34
|
+
"extract_token_usage_from_openai_response",
|
|
35
|
+
"get_token_tracker",
|
|
36
|
+
"record_token_usage",
|
|
37
|
+
]
|