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.
Files changed (86) hide show
  1. noesium/core/__init__.py +4 -0
  2. noesium/core/agent/__init__.py +14 -0
  3. noesium/core/agent/base.py +227 -0
  4. noesium/core/consts.py +6 -0
  5. noesium/core/goalith/conflict/conflict.py +104 -0
  6. noesium/core/goalith/conflict/detector.py +53 -0
  7. noesium/core/goalith/decomposer/__init__.py +6 -0
  8. noesium/core/goalith/decomposer/base.py +46 -0
  9. noesium/core/goalith/decomposer/callable_decomposer.py +65 -0
  10. noesium/core/goalith/decomposer/llm_decomposer.py +326 -0
  11. noesium/core/goalith/decomposer/prompts.py +140 -0
  12. noesium/core/goalith/decomposer/simple_decomposer.py +61 -0
  13. noesium/core/goalith/errors.py +22 -0
  14. noesium/core/goalith/goalgraph/graph.py +526 -0
  15. noesium/core/goalith/goalgraph/node.py +179 -0
  16. noesium/core/goalith/replanner/base.py +31 -0
  17. noesium/core/goalith/replanner/replanner.py +36 -0
  18. noesium/core/goalith/service.py +26 -0
  19. noesium/core/llm/__init__.py +154 -0
  20. noesium/core/llm/base.py +152 -0
  21. noesium/core/llm/litellm.py +528 -0
  22. noesium/core/llm/llamacpp.py +487 -0
  23. noesium/core/llm/message.py +184 -0
  24. noesium/core/llm/ollama.py +459 -0
  25. noesium/core/llm/openai.py +520 -0
  26. noesium/core/llm/openrouter.py +89 -0
  27. noesium/core/llm/prompt.py +551 -0
  28. noesium/core/memory/__init__.py +11 -0
  29. noesium/core/memory/base.py +464 -0
  30. noesium/core/memory/memu/__init__.py +24 -0
  31. noesium/core/memory/memu/config/__init__.py +26 -0
  32. noesium/core/memory/memu/config/activity/config.py +46 -0
  33. noesium/core/memory/memu/config/event/config.py +46 -0
  34. noesium/core/memory/memu/config/markdown_config.py +241 -0
  35. noesium/core/memory/memu/config/profile/config.py +48 -0
  36. noesium/core/memory/memu/llm_adapter.py +129 -0
  37. noesium/core/memory/memu/memory/__init__.py +31 -0
  38. noesium/core/memory/memu/memory/actions/__init__.py +40 -0
  39. noesium/core/memory/memu/memory/actions/add_activity_memory.py +299 -0
  40. noesium/core/memory/memu/memory/actions/base_action.py +342 -0
  41. noesium/core/memory/memu/memory/actions/cluster_memories.py +262 -0
  42. noesium/core/memory/memu/memory/actions/generate_suggestions.py +198 -0
  43. noesium/core/memory/memu/memory/actions/get_available_categories.py +66 -0
  44. noesium/core/memory/memu/memory/actions/link_related_memories.py +515 -0
  45. noesium/core/memory/memu/memory/actions/run_theory_of_mind.py +254 -0
  46. noesium/core/memory/memu/memory/actions/update_memory_with_suggestions.py +514 -0
  47. noesium/core/memory/memu/memory/embeddings.py +130 -0
  48. noesium/core/memory/memu/memory/file_manager.py +306 -0
  49. noesium/core/memory/memu/memory/memory_agent.py +578 -0
  50. noesium/core/memory/memu/memory/recall_agent.py +376 -0
  51. noesium/core/memory/memu/memory_store.py +628 -0
  52. noesium/core/memory/models.py +149 -0
  53. noesium/core/msgbus/__init__.py +12 -0
  54. noesium/core/msgbus/base.py +395 -0
  55. noesium/core/orchestrix/__init__.py +0 -0
  56. noesium/core/py.typed +0 -0
  57. noesium/core/routing/__init__.py +20 -0
  58. noesium/core/routing/base.py +66 -0
  59. noesium/core/routing/router.py +241 -0
  60. noesium/core/routing/strategies/__init__.py +9 -0
  61. noesium/core/routing/strategies/dynamic_complexity.py +361 -0
  62. noesium/core/routing/strategies/self_assessment.py +147 -0
  63. noesium/core/routing/types.py +38 -0
  64. noesium/core/toolify/__init__.py +39 -0
  65. noesium/core/toolify/base.py +360 -0
  66. noesium/core/toolify/config.py +138 -0
  67. noesium/core/toolify/mcp_integration.py +275 -0
  68. noesium/core/toolify/registry.py +214 -0
  69. noesium/core/toolify/toolkits/__init__.py +1 -0
  70. noesium/core/tracing/__init__.py +37 -0
  71. noesium/core/tracing/langgraph_hooks.py +308 -0
  72. noesium/core/tracing/opik_tracing.py +144 -0
  73. noesium/core/tracing/token_tracker.py +166 -0
  74. noesium/core/utils/__init__.py +10 -0
  75. noesium/core/utils/logging.py +172 -0
  76. noesium/core/utils/statistics.py +12 -0
  77. noesium/core/utils/typing.py +17 -0
  78. noesium/core/vector_store/__init__.py +79 -0
  79. noesium/core/vector_store/base.py +94 -0
  80. noesium/core/vector_store/pgvector.py +304 -0
  81. noesium/core/vector_store/weaviate.py +383 -0
  82. noesium-0.1.0.dist-info/METADATA +525 -0
  83. noesium-0.1.0.dist-info/RECORD +86 -0
  84. noesium-0.1.0.dist-info/WHEEL +5 -0
  85. noesium-0.1.0.dist-info/licenses/LICENSE +21 -0
  86. 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
+ ]