gllm-core-binary 0.3.23b3__py3-none-any.whl → 0.4.2b1__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 (39) hide show
  1. gllm_core/adapters/__init__.py +5 -0
  2. gllm_core/adapters/__init__.pyi +3 -0
  3. gllm_core/adapters/tool/__init__.py +6 -0
  4. gllm_core/adapters/tool/__init__.pyi +4 -0
  5. gllm_core/adapters/tool/google_adk.py +91 -0
  6. gllm_core/adapters/tool/google_adk.pyi +23 -0
  7. gllm_core/adapters/tool/langchain.py +130 -0
  8. gllm_core/adapters/tool/langchain.pyi +31 -0
  9. gllm_core/constants.py +0 -1
  10. gllm_core/constants.pyi +0 -1
  11. gllm_core/event/event_emitter.py +8 -44
  12. gllm_core/event/event_emitter.pyi +9 -21
  13. gllm_core/event/handler/console_event_handler.py +1 -12
  14. gllm_core/event/handler/console_event_handler.pyi +0 -1
  15. gllm_core/event/handler/print_event_handler.py +15 -59
  16. gllm_core/event/handler/print_event_handler.pyi +1 -2
  17. gllm_core/schema/__init__.py +2 -2
  18. gllm_core/schema/__init__.pyi +2 -2
  19. gllm_core/schema/component.py +236 -27
  20. gllm_core/schema/component.pyi +164 -17
  21. gllm_core/schema/schema_generator.py +150 -0
  22. gllm_core/schema/schema_generator.pyi +35 -0
  23. gllm_core/schema/tool.py +31 -1
  24. gllm_core/schema/tool.pyi +21 -0
  25. gllm_core/utils/__init__.py +2 -0
  26. gllm_core/utils/__init__.pyi +2 -1
  27. gllm_core/utils/analyzer.py +24 -1
  28. gllm_core/utils/analyzer.pyi +15 -1
  29. gllm_core/utils/concurrency.py +2 -2
  30. gllm_core/utils/logger_manager.py +17 -7
  31. gllm_core/utils/logger_manager.pyi +3 -0
  32. gllm_core/utils/main_method_resolver.py +185 -0
  33. gllm_core/utils/main_method_resolver.pyi +54 -0
  34. gllm_core/utils/retry.py +130 -21
  35. gllm_core/utils/retry.pyi +6 -29
  36. {gllm_core_binary-0.3.23b3.dist-info → gllm_core_binary-0.4.2b1.dist-info}/METADATA +6 -1
  37. {gllm_core_binary-0.3.23b3.dist-info → gllm_core_binary-0.4.2b1.dist-info}/RECORD +39 -27
  38. {gllm_core_binary-0.3.23b3.dist-info → gllm_core_binary-0.4.2b1.dist-info}/WHEEL +0 -0
  39. {gllm_core_binary-0.3.23b3.dist-info → gllm_core_binary-0.4.2b1.dist-info}/top_level.txt +0 -0
@@ -10,12 +10,9 @@ References:
10
10
 
11
11
  from __future__ import annotations
12
12
 
13
- import ast
14
13
  import inspect
15
14
  import logging
16
- import textwrap
17
15
  import warnings
18
- from abc import ABC, abstractmethod
19
16
  from functools import lru_cache
20
17
  from traceback import format_exc
21
18
  from typing import TYPE_CHECKING, Any, Callable
@@ -23,25 +20,83 @@ from typing import TYPE_CHECKING, Any, Callable
23
20
  if TYPE_CHECKING:
24
21
  from gllm_core.event.event_emitter import EventEmitter
25
22
 
26
- from gllm_core.utils.analyzer import MethodSignature, ParameterInfo, ParameterKind, RunAnalyzer, RunProfile
23
+
24
+ from pydantic import BaseModel
25
+
26
+ from gllm_core.schema.schema_generator import generate_params_model
27
+ from gllm_core.schema.tool import Tool, tool
28
+ from gllm_core.utils import BinaryHandlingStrategy, binary_handler_factory
29
+ from gllm_core.utils.analyzer import MethodSignature, ParameterInfo, ParameterKind, RunProfile, analyze_method
27
30
  from gllm_core.utils.logger_manager import LoggerManager
31
+ from gllm_core.utils.main_method_resolver import MainMethodResolver
32
+
33
+
34
+ def main(method: Callable) -> Callable:
35
+ """Decorate a Component method as the async main entrypoint.
36
+
37
+ Usage:
38
+ Declare the coroutine that should act as the primary execution path
39
+ for a `Component` subclass. The decorated coroutine will be resolved by
40
+ `Component.run()` unless another subclass overrides the decoration.
41
+
42
+ Args:
43
+ method (Callable): Coroutine to mark as the main entrypoint.
44
+
45
+ Returns:
46
+ Callable: The same coroutine that is passed to the decorator. The decorator only marks the method as the main
47
+ entrypoint. It does not wrap or change its behavior or signature.
48
+
49
+ Raises:
50
+ TypeError: If the decorated callable is not asynchronous.
51
+ """
52
+ actual_method = method.__func__ if isinstance(method, (classmethod, staticmethod)) else method
28
53
 
54
+ if not inspect.iscoroutinefunction(actual_method):
55
+ method_name = getattr(actual_method, "__name__", "unknown")
56
+ raise TypeError(f"Main method {method_name!r} must be asynchronous")
57
+ method.__is_main__ = True
58
+ return method
29
59
 
30
- class Component(ABC):
60
+
61
+ class Component:
31
62
  """An abstract base class for all components used throughout the Gen AI applications.
32
63
 
33
64
  Every instance of Component has access to class-level `_default_log_level` and `_logger`, as detailed below.
34
65
  For components that require high observability, it is recommended to set `_default_log_level` to `logging.INFO`
35
66
  or higher.
36
67
 
37
- Example:
38
- ```python
39
- class MyComponent(Component):
40
- _default_log_level = logging.INFO
68
+ Defining Custom Components:
69
+ There are two ways to define the main execution logic for a component:
70
+
71
+ 1. **Using the @main decorator (Recommended)**:
72
+ Decorate an async method with `@main` to mark it as the primary entrypoint.
73
+ This is the preferred approach as it provides explicit control over the main method.
74
+
75
+ ```python
76
+ class MyComponent(Component):
77
+ _default_log_level = logging.INFO
78
+
79
+ @main
80
+ async def execute(self, **kwargs: Any) -> Any:
81
+ return "Hello from @main!"
82
+ ```
41
83
 
42
- def _run(self, **kwargs: Any) -> Any:
43
- return "Hello, World!"
44
- ```
84
+ 2. **Implementing _run method (Deprecated)**:
85
+ Override the abstract `_run` method. This is the traditional approach and still supported.
86
+
87
+ ```python
88
+ class MyComponent(Component):
89
+ _default_log_level = logging.INFO
90
+
91
+ async def _run(self, **kwargs: Any) -> Any:
92
+ return "Hello, World!"
93
+ ```
94
+
95
+ The `run()` method resolves the main entrypoint using the following precedence:
96
+ 1. Method decorated with @main in the current class.
97
+ 2. Method decorated with @main in the nearest ancestor class.
98
+ 3. Method named in __main_method__ property.
99
+ 4. The _run method (with deprecation warning).
45
100
 
46
101
  Attributes:
47
102
  run_profile (RunProfile): The profile of the `_run` method.
@@ -52,12 +107,100 @@ class Component(ABC):
52
107
  **Do not override this property in your subclass.**
53
108
 
54
109
  You also do not need to write this attribute in your component's docstring.
55
- _default_log_level (int): The default log level for the component. Defaults to DEBUG.
56
- _logger (logging.Logger): The logger instance for the component.
57
110
  """
58
111
 
59
112
  _default_log_level = logging.DEBUG
60
113
 
114
+ def __init_subclass__(cls, **kwargs):
115
+ """Hook called when a subclass is created.
116
+
117
+ This validates the __main_method__ property and checks for multiple @main decorators
118
+ within the current class definition. Uses MainMethodResolver for consistent validation logic.
119
+
120
+ Note: Multiple inheritance conflicts are intentionally deferred to runtime (get_main())
121
+ to allow class definition to succeed.
122
+
123
+ Raises:
124
+ AttributeError: If __main_method__ refers to a non-existent method.
125
+ TypeError: If multiple methods are decorated with @main in the same class.
126
+ """
127
+ super().__init_subclass__(**kwargs)
128
+
129
+ MainMethodResolver.validate_class(cls)
130
+
131
+ @classmethod
132
+ @lru_cache(maxsize=None)
133
+ def get_main(cls) -> Callable | None:
134
+ """Return the resolved main coroutine for this Component class.
135
+
136
+ This method resolves the main method for the Component class following
137
+ the precedence rules:
138
+ 1. Most derived coroutine decorated with `@main`.
139
+ 2. Method named by `__main_method__`.
140
+ 3. `_run` coroutine as a deprecated fallback.
141
+
142
+ Results are cached for performance.
143
+
144
+ Returns:
145
+ Callable | None: The coroutine that will be executed by `run()` or
146
+ `None` when no entrypoint can be determined.
147
+
148
+ Raises:
149
+ TypeError: If conflicting main methods are inherited from multiple ancestors.
150
+ """
151
+ return MainMethodResolver(cls).resolve()
152
+
153
+ @classmethod
154
+ @lru_cache(maxsize=None)
155
+ def _get_input_params_model(cls) -> type[BaseModel] | None:
156
+ """Generate and cache the input-parameter model for this component class.
157
+
158
+ Returns:
159
+ type[BaseModel] | None: A Pydantic model describing the component's
160
+ input parameters, or `None` when no main method can be resolved or the
161
+ model generation fails.
162
+ """
163
+ main_method = cls.get_main() or cls._run
164
+
165
+ try:
166
+ return generate_params_model(main_method, cls.__name__)
167
+ except Exception: # pragma: no cover - defensive fallback
168
+ logging.getLogger(__name__).exception("Failed to generate input params model for %s", cls.__name__)
169
+
170
+ @property
171
+ def input_params(self) -> type[BaseModel] | None:
172
+ """Return the Pydantic model describing this component's main method input parameters.
173
+
174
+ Returns:
175
+ type[BaseModel] | None: The cached model that mirrors the signature of
176
+ the resolved main method, or `None` if no main method can be
177
+ determined.
178
+
179
+ Examples:
180
+ ```python
181
+ from pydantic import ValidationError
182
+
183
+ component = SomeComponent()
184
+ ParamsModel = component.input_params
185
+ assert ParamsModel.__name__ == "SomeComponentParams"
186
+ fields = list(ParamsModel.model_fields)
187
+
188
+ # Validation with valid params
189
+ params = ParamsModel(text="hello")
190
+
191
+ # Validation catches missing required fields
192
+ try:
193
+ invalid_params = ParamsModel() # Missing required 'text' field
194
+ except ValidationError as e:
195
+ print(f"Validation failed: {e.error_count()} errors")
196
+
197
+ # Argument construction
198
+ payload = params.model_dump()
199
+ result = await component.run(**payload)
200
+ ```
201
+ """
202
+ return self.__class__._get_input_params_model()
203
+
61
204
  @property
62
205
  def _logger(self) -> logging.Logger:
63
206
  """Get a logger instance configured for this component class.
@@ -94,28 +237,92 @@ class Component(ABC):
94
237
  async def run(self, **kwargs: Any) -> Any:
95
238
  """Runs the operations defined for the component.
96
239
 
97
- This method emits the provided input arguments using an EventEmitter instance if available, executes a process
98
- defined in the `_run` method, and emits the resulting output if the EventEmitter is provided.
240
+ This method emits the provided input arguments using an EventEmitter instance if available, executes the
241
+ resolved main method, and emits the resulting output if the EventEmitter is provided.
242
+
243
+ The main method is resolved using the following precedence:
244
+ 1. Method decorated with @main in the current class.
245
+ 2. Method decorated with @main in the nearest ancestor class.
246
+ 3. Method named in __main_method__ property.
247
+ 4. The _run method (with deprecation warning).
99
248
 
100
249
  Args:
101
250
  **kwargs (Any): A dictionary of arguments to be processed. May include an `event_emitter`
102
251
  key with an EventEmitter instance.
103
252
 
104
253
  Returns:
105
- Any: The result of the `_run` method.
254
+ Any: The result of the resolved main method.
255
+
256
+ Raises:
257
+ TypeError: If conflicting main methods are inherited from multiple ancestors.
258
+ AttributeError: If __main_method__ refers to a non-existent method.
106
259
  """
107
260
  event_emitter = kwargs.get("event_emitter")
108
261
 
109
262
  input_event = self._format_input_event(kwargs)
110
263
  await self._log_io_event(input_event, event_emitter)
111
264
 
112
- output = await self._run(**kwargs)
265
+ main_method = self.get_main()
266
+ if main_method:
267
+ output = await main_method(self, **kwargs)
268
+ else:
269
+ self._logger.warning(
270
+ "_run method is deprecated and will be removed in a future release.\n"
271
+ "Use the `@main` decorator instead."
272
+ )
273
+ output = await self._run(**kwargs)
113
274
 
114
275
  output_event = self._format_output_event(output)
115
276
  await self._log_io_event(output_event, event_emitter)
116
277
 
117
278
  return output
118
279
 
280
+ def as_tool(self, name: str | None = None, description: str | None = None, title: str | None = None) -> Tool:
281
+ """Convert the component's main method into a `Tool` instance.
282
+
283
+ Example:
284
+ ```python
285
+ from gllm_core.schema import Component, main
286
+
287
+ class MyComponent(Component):
288
+ @main
289
+ async def my_method(self, param: str) -> str:
290
+ return param
291
+
292
+ component = MyComponent()
293
+ tool = component.as_tool()
294
+ ```
295
+
296
+ Args:
297
+ name (str | None, optional): Identifier for the resulting tool. Defaults to the component class name.
298
+ description (str | None, optional): Summary of the tool's behavior. Defaults to None, in which case the
299
+ main method's docstring is used.
300
+ title (str | None, optional): Optional display title for the tool. Defaults to None, in which case the
301
+ component's class name is used.
302
+
303
+ Returns:
304
+ Tool: The tool wrapping the component's main method.
305
+
306
+ Raises:
307
+ RuntimeError: If the component does not declare a main method using @main or __main_method__.
308
+ """
309
+ main_method = self.get_main()
310
+ if main_method is None or main_method is self.__class__._run:
311
+ raise RuntimeError(
312
+ "Component main method is not declared. Use @main or __main_method__ before converting to a tool."
313
+ )
314
+
315
+ bound_main = main_method.__get__(self, self.__class__)
316
+
317
+ tool_name = name or self.__class__.__name__
318
+ tool_decorator = tool(name=tool_name, description=description, title=title)
319
+ component_tool = tool_decorator(bound_main)
320
+
321
+ if not isinstance(component_tool, Tool): # pragma: no cover - Defensive: decorator should always return Tool
322
+ raise TypeError("Failed to convert component main method into a Tool instance.")
323
+
324
+ return component_tool
325
+
119
326
  async def _log_io_event(self, event_message: str, event_emitter: EventEmitter | None = None) -> None:
120
327
  """Logs an event message and emits it via the event emitter if available.
121
328
 
@@ -147,8 +354,10 @@ class Component(ABC):
147
354
 
148
355
  if input_dict:
149
356
  message += " Processing input: "
357
+ handler = binary_handler_factory(BinaryHandlingStrategy.SHOW_SIZE)
150
358
  for key, value in input_dict.items():
151
- message += f"\n - {key}: {value!r}"
359
+ formatted_value = handler(value) if isinstance(value, bytes) else value
360
+ message += f"\n - {key}: {formatted_value!r}"
152
361
 
153
362
  return message
154
363
 
@@ -164,17 +373,21 @@ class Component(ABC):
164
373
  Returns:
165
374
  str: A formatted event message string representing the output data.
166
375
  """
167
- message = f"[Finished {self.__class__.__name__!r}] Successfully produced output:\n{output!r}"
376
+ handler = binary_handler_factory(BinaryHandlingStrategy.SHOW_SIZE)
377
+ formatted_output = handler(output) if isinstance(output, bytes) else output
378
+ message = f"[Finished {self.__class__.__name__!r}] Successfully produced output:\n{formatted_output!r}"
168
379
  return message
169
380
 
170
- @abstractmethod
171
381
  async def _run(self, **kwargs: Any) -> Any:
172
382
  """Defines the core process logic to be implemented by subclasses.
173
383
 
174
- This abstract method must be implemented in a subclass to define the specific process that will be executed.
384
+ This method can be implemented in a subclass to define the specific process that will be executed.
175
385
  It is called by the `run` method and is responsible for handling the provided input arguments and producing
176
386
  an output.
177
387
 
388
+ Deprecation:
389
+ This method is deprecated and will be removed in a future release. Use the `@main` decorator instead.
390
+
178
391
  Args:
179
392
  **kwargs (Any): A dictionary of arguments required for the process.
180
393
 
@@ -227,11 +440,7 @@ class Component(ABC):
227
440
  """
228
441
  try:
229
442
  method = cls._run
230
- source = inspect.getsource(method)
231
- tree = ast.parse(textwrap.dedent(source))
232
- analyzer = RunAnalyzer(cls)
233
- analyzer.visit(tree)
234
- return analyzer.profile
443
+ return analyze_method(cls, method)
235
444
  except Exception as e:
236
445
  warnings.warn(
237
446
  f"Failed to analyze the _run method: {e}.\n{format_exc()}",
@@ -1,25 +1,71 @@
1
- import abc
2
- from abc import ABC
3
1
  from gllm_core.event.event_emitter import EventEmitter as EventEmitter
4
- from gllm_core.utils.analyzer import MethodSignature as MethodSignature, ParameterInfo as ParameterInfo, ParameterKind as ParameterKind, RunAnalyzer as RunAnalyzer, RunProfile as RunProfile
2
+ from gllm_core.schema.schema_generator import generate_params_model as generate_params_model
3
+ from gllm_core.schema.tool import Tool as Tool, tool as tool
4
+ from gllm_core.utils import BinaryHandlingStrategy as BinaryHandlingStrategy, binary_handler_factory as binary_handler_factory
5
+ from gllm_core.utils.analyzer import MethodSignature as MethodSignature, ParameterInfo as ParameterInfo, ParameterKind as ParameterKind, RunProfile as RunProfile, analyze_method as analyze_method
5
6
  from gllm_core.utils.logger_manager import LoggerManager as LoggerManager
6
- from typing import Any
7
+ from gllm_core.utils.main_method_resolver import MainMethodResolver as MainMethodResolver
8
+ from pydantic import BaseModel as BaseModel
9
+ from typing import Any, Callable
7
10
 
8
- class Component(ABC, metaclass=abc.ABCMeta):
11
+ def main(method: Callable) -> Callable:
12
+ """Decorate a Component method as the async main entrypoint.
13
+
14
+ Usage:
15
+ Declare the coroutine that should act as the primary execution path
16
+ for a `Component` subclass. The decorated coroutine will be resolved by
17
+ `Component.run()` unless another subclass overrides the decoration.
18
+
19
+ Args:
20
+ method (Callable): Coroutine to mark as the main entrypoint.
21
+
22
+ Returns:
23
+ Callable: The same coroutine that is passed to the decorator. The decorator only marks the method as the main
24
+ entrypoint. It does not wrap or change its behavior or signature.
25
+
26
+ Raises:
27
+ TypeError: If the decorated callable is not asynchronous.
28
+ """
29
+
30
+ class Component:
9
31
  '''An abstract base class for all components used throughout the Gen AI applications.
10
32
 
11
33
  Every instance of Component has access to class-level `_default_log_level` and `_logger`, as detailed below.
12
34
  For components that require high observability, it is recommended to set `_default_log_level` to `logging.INFO`
13
35
  or higher.
14
36
 
15
- Example:
16
- ```python
17
- class MyComponent(Component):
18
- _default_log_level = logging.INFO
37
+ Defining Custom Components:
38
+ There are two ways to define the main execution logic for a component:
39
+
40
+ 1. **Using the @main decorator (Recommended)**:
41
+ Decorate an async method with `@main` to mark it as the primary entrypoint.
42
+ This is the preferred approach as it provides explicit control over the main method.
43
+
44
+ ```python
45
+ class MyComponent(Component):
46
+ _default_log_level = logging.INFO
47
+
48
+ @main
49
+ async def execute(self, **kwargs: Any) -> Any:
50
+ return "Hello from @main!"
51
+ ```
52
+
53
+ 2. **Implementing _run method (Deprecated)**:
54
+ Override the abstract `_run` method. This is the traditional approach and still supported.
55
+
56
+ ```python
57
+ class MyComponent(Component):
58
+ _default_log_level = logging.INFO
19
59
 
20
- def _run(self, **kwargs: Any) -> Any:
21
- return "Hello, World!"
22
- ```
60
+ async def _run(self, **kwargs: Any) -> Any:
61
+ return "Hello, World!"
62
+ ```
63
+
64
+ The `run()` method resolves the main entrypoint using the following precedence:
65
+ 1. Method decorated with @main in the current class.
66
+ 2. Method decorated with @main in the nearest ancestor class.
67
+ 3. Method named in __main_method__ property.
68
+ 4. The _run method (with deprecation warning).
23
69
 
24
70
  Attributes:
25
71
  run_profile (RunProfile): The profile of the `_run` method.
@@ -30,21 +76,122 @@ class Component(ABC, metaclass=abc.ABCMeta):
30
76
  **Do not override this property in your subclass.**
31
77
 
32
78
  You also do not need to write this attribute in your component\'s docstring.
33
- _default_log_level (int): The default log level for the component. Defaults to DEBUG.
34
- _logger (logging.Logger): The logger instance for the component.
35
79
  '''
80
+ def __init_subclass__(cls, **kwargs) -> None:
81
+ """Hook called when a subclass is created.
82
+
83
+ This validates the __main_method__ property and checks for multiple @main decorators
84
+ within the current class definition. Uses MainMethodResolver for consistent validation logic.
85
+
86
+ Note: Multiple inheritance conflicts are intentionally deferred to runtime (get_main())
87
+ to allow class definition to succeed.
88
+
89
+ Raises:
90
+ AttributeError: If __main_method__ refers to a non-existent method.
91
+ TypeError: If multiple methods are decorated with @main in the same class.
92
+ """
93
+ @classmethod
94
+ def get_main(cls) -> Callable | None:
95
+ """Return the resolved main coroutine for this Component class.
96
+
97
+ This method resolves the main method for the Component class following
98
+ the precedence rules:
99
+ 1. Most derived coroutine decorated with `@main`.
100
+ 2. Method named by `__main_method__`.
101
+ 3. `_run` coroutine as a deprecated fallback.
102
+
103
+ Results are cached for performance.
104
+
105
+ Returns:
106
+ Callable | None: The coroutine that will be executed by `run()` or
107
+ `None` when no entrypoint can be determined.
108
+
109
+ Raises:
110
+ TypeError: If conflicting main methods are inherited from multiple ancestors.
111
+ """
112
+ @property
113
+ def input_params(self) -> type[BaseModel] | None:
114
+ '''Return the Pydantic model describing this component\'s main method input parameters.
115
+
116
+ Returns:
117
+ type[BaseModel] | None: The cached model that mirrors the signature of
118
+ the resolved main method, or `None` if no main method can be
119
+ determined.
120
+
121
+ Examples:
122
+ ```python
123
+ from pydantic import ValidationError
124
+
125
+ component = SomeComponent()
126
+ ParamsModel = component.input_params
127
+ assert ParamsModel.__name__ == "SomeComponentParams"
128
+ fields = list(ParamsModel.model_fields)
129
+
130
+ # Validation with valid params
131
+ params = ParamsModel(text="hello")
132
+
133
+ # Validation catches missing required fields
134
+ try:
135
+ invalid_params = ParamsModel() # Missing required \'text\' field
136
+ except ValidationError as e:
137
+ print(f"Validation failed: {e.error_count()} errors")
138
+
139
+ # Argument construction
140
+ payload = params.model_dump()
141
+ result = await component.run(**payload)
142
+ ```
143
+ '''
36
144
  async def run(self, **kwargs: Any) -> Any:
37
145
  """Runs the operations defined for the component.
38
146
 
39
- This method emits the provided input arguments using an EventEmitter instance if available, executes a process
40
- defined in the `_run` method, and emits the resulting output if the EventEmitter is provided.
147
+ This method emits the provided input arguments using an EventEmitter instance if available, executes the
148
+ resolved main method, and emits the resulting output if the EventEmitter is provided.
149
+
150
+ The main method is resolved using the following precedence:
151
+ 1. Method decorated with @main in the current class.
152
+ 2. Method decorated with @main in the nearest ancestor class.
153
+ 3. Method named in __main_method__ property.
154
+ 4. The _run method (with deprecation warning).
41
155
 
42
156
  Args:
43
157
  **kwargs (Any): A dictionary of arguments to be processed. May include an `event_emitter`
44
158
  key with an EventEmitter instance.
45
159
 
46
160
  Returns:
47
- Any: The result of the `_run` method.
161
+ Any: The result of the resolved main method.
162
+
163
+ Raises:
164
+ TypeError: If conflicting main methods are inherited from multiple ancestors.
165
+ AttributeError: If __main_method__ refers to a non-existent method.
166
+ """
167
+ def as_tool(self, name: str | None = None, description: str | None = None, title: str | None = None) -> Tool:
168
+ """Convert the component's main method into a `Tool` instance.
169
+
170
+ Example:
171
+ ```python
172
+ from gllm_core.schema import Component, main
173
+
174
+ class MyComponent(Component):
175
+ @main
176
+ async def my_method(self, param: str) -> str:
177
+ return param
178
+
179
+ component = MyComponent()
180
+ tool = component.as_tool()
181
+ ```
182
+
183
+ Args:
184
+ name (str | None, optional): Identifier for the resulting tool. Defaults to the component class name.
185
+ description (str | None, optional): Summary of the tool's behavior. Defaults to None, in which case the
186
+ main method's docstring is used.
187
+ title (str | None, optional): Optional display title for the tool. Defaults to None, in which case the
188
+ component's class name is used.
189
+
190
+ Returns:
191
+ Tool: The tool wrapping the component's main method.
192
+
193
+ Raises:
194
+ RuntimeError: If the component does not declare a main method using @main or __main_method__.
48
195
  """
49
196
  @property
50
197
  def run_profile(self) -> RunProfile: