amrita_core 0.1.0__tar.gz → 0.2.0__tar.gz

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 (32) hide show
  1. {amrita_core-0.1.0/src/amrita_core.egg-info → amrita_core-0.2.0}/PKG-INFO +3 -1
  2. {amrita_core-0.1.0 → amrita_core-0.2.0}/README.md +3 -1
  3. {amrita_core-0.1.0 → amrita_core-0.2.0}/pyproject.toml +1 -1
  4. {amrita_core-0.1.0 → amrita_core-0.2.0}/src/amrita_core/__init__.py +2 -1
  5. {amrita_core-0.1.0 → amrita_core-0.2.0}/src/amrita_core/chatmanager.py +26 -20
  6. amrita_core-0.2.0/src/amrita_core/tools/manager.py +368 -0
  7. {amrita_core-0.1.0 → amrita_core-0.2.0/src/amrita_core.egg-info}/PKG-INFO +3 -1
  8. amrita_core-0.1.0/src/amrita_core/tools/manager.py +0 -163
  9. {amrita_core-0.1.0 → amrita_core-0.2.0}/LICENSE +0 -0
  10. {amrita_core-0.1.0 → amrita_core-0.2.0}/setup.cfg +0 -0
  11. {amrita_core-0.1.0 → amrita_core-0.2.0}/src/amrita_core/builtins/__init__.py +0 -0
  12. {amrita_core-0.1.0 → amrita_core-0.2.0}/src/amrita_core/builtins/adapter.py +0 -0
  13. {amrita_core-0.1.0 → amrita_core-0.2.0}/src/amrita_core/builtins/agent.py +0 -0
  14. {amrita_core-0.1.0 → amrita_core-0.2.0}/src/amrita_core/builtins/tools.py +0 -0
  15. {amrita_core-0.1.0 → amrita_core-0.2.0}/src/amrita_core/config.py +0 -0
  16. {amrita_core-0.1.0 → amrita_core-0.2.0}/src/amrita_core/hook/event.py +0 -0
  17. {amrita_core-0.1.0 → amrita_core-0.2.0}/src/amrita_core/hook/exception.py +0 -0
  18. {amrita_core-0.1.0 → amrita_core-0.2.0}/src/amrita_core/hook/matcher.py +0 -0
  19. {amrita_core-0.1.0 → amrita_core-0.2.0}/src/amrita_core/hook/on.py +0 -0
  20. {amrita_core-0.1.0 → amrita_core-0.2.0}/src/amrita_core/libchat.py +0 -0
  21. {amrita_core-0.1.0 → amrita_core-0.2.0}/src/amrita_core/logging.py +0 -0
  22. {amrita_core-0.1.0 → amrita_core-0.2.0}/src/amrita_core/preset.py +0 -0
  23. {amrita_core-0.1.0 → amrita_core-0.2.0}/src/amrita_core/protocol.py +0 -0
  24. {amrita_core-0.1.0 → amrita_core-0.2.0}/src/amrita_core/tokenizer.py +0 -0
  25. {amrita_core-0.1.0 → amrita_core-0.2.0}/src/amrita_core/tools/mcp.py +0 -0
  26. {amrita_core-0.1.0 → amrita_core-0.2.0}/src/amrita_core/tools/models.py +0 -0
  27. {amrita_core-0.1.0 → amrita_core-0.2.0}/src/amrita_core/types.py +0 -0
  28. {amrita_core-0.1.0 → amrita_core-0.2.0}/src/amrita_core/utils.py +0 -0
  29. {amrita_core-0.1.0 → amrita_core-0.2.0}/src/amrita_core.egg-info/SOURCES.txt +0 -0
  30. {amrita_core-0.1.0 → amrita_core-0.2.0}/src/amrita_core.egg-info/dependency_links.txt +0 -0
  31. {amrita_core-0.1.0 → amrita_core-0.2.0}/src/amrita_core.egg-info/requires.txt +0 -0
  32. {amrita_core-0.1.0 → amrita_core-0.2.0}/src/amrita_core.egg-info/top_level.txt +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: amrita_core
3
- Version: 0.1.0
3
+ Version: 0.2.0
4
4
  Summary: Agent core of Project Amrita
5
5
  Project-URL: Homepage, https://github.com/AmritaBot/AmritaCore
6
6
  Project-URL: Source, https://github.com/AmritaBot/AmritaCore
@@ -60,6 +60,8 @@ This repository contains documentation organized as follows:
60
60
 
61
61
  Documentation is currently under construction. For quick start, please refer to the examples in the `demo/` folder.
62
62
 
63
+ Please view [Docs](https://amrita-core.suggar.top) for more information.
64
+
63
65
  ## 🛠️ Quick Start
64
66
 
65
67
  To quickly start using AmritaCore, check out the examples in the [demo](./demo/) directory. The basic example demonstrates how to initialize the core, configure settings, and run a simple chat session with the AI assistant.
@@ -42,6 +42,8 @@ This repository contains documentation organized as follows:
42
42
 
43
43
  Documentation is currently under construction. For quick start, please refer to the examples in the `demo/` folder.
44
44
 
45
+ Please view [Docs](https://amrita-core.suggar.top) for more information.
46
+
45
47
  ## 🛠️ Quick Start
46
48
 
47
49
  To quickly start using AmritaCore, check out the examples in the [demo](./demo/) directory. The basic example demonstrates how to initialize the core, configure settings, and run a simple chat session with the AI assistant.
@@ -52,4 +54,4 @@ We welcome contributions! Please see our contribution guidelines for more inform
52
54
 
53
55
  ## 📄 License
54
56
 
55
- This project is licensed under the AGPL-3.0 License - see the [LICENSE](./LICENSE) file for details.
57
+ This project is licensed under the AGPL-3.0 License - see the [LICENSE](./LICENSE) file for details.
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "amrita_core"
3
- version = "0.1.0"
3
+ version = "0.2.0"
4
4
  description = "Agent core of Project Amrita"
5
5
  readme = "README.md"
6
6
  requires-python = ">=3.10,<3.14"
@@ -13,7 +13,7 @@ from .libchat import (
13
13
  from .logging import debug_log, logger
14
14
  from .preset import PresetManager, PresetReport
15
15
  from .tools import mcp
16
- from .tools.manager import ToolsManager, on_tools
16
+ from .tools.manager import ToolsManager, on_tools, simple_tool
17
17
  from .tools.models import (
18
18
  FunctionDefinitionSchema,
19
19
  FunctionParametersSchema,
@@ -73,6 +73,7 @@ __all__ = [
73
73
  "on_precompletion",
74
74
  "on_tools",
75
75
  "set_config",
76
+ "simple_tool",
76
77
  "text_generator",
77
78
  "tools_caller",
78
79
  ]
@@ -1,3 +1,5 @@
1
+ from __future__ import annotations
2
+
1
3
  import asyncio
2
4
  import copy
3
5
  from abc import ABC, abstractmethod
@@ -434,10 +436,9 @@ class ChatObject:
434
436
  _is_running: bool = False # Whether it is running
435
437
  _is_done: bool = False # Whether it has completed
436
438
  _task: Task[None]
437
- _has_task: bool = False
438
439
  _err: BaseException | None = None
439
- _wait: bool = True
440
440
  _queue_done: bool = False
441
+ _has_consumer: bool = False
441
442
  __done_marker = object()
442
443
 
443
444
  def __init__(
@@ -446,7 +447,6 @@ class ChatObject:
446
447
  user_input: USER_INPUT,
447
448
  context: Memory,
448
449
  session_id: str,
449
- run_blocking: bool = True,
450
450
  queue_size: int = 25,
451
451
  overflow_queue_size: int = 45,
452
452
  ) -> None:
@@ -468,7 +468,6 @@ class ChatObject:
468
468
  self.time = datetime.now(utc)
469
469
  self.config: AmritaConfig = get_config()
470
470
  self.last_call = datetime.now(utc)
471
- self._wait = run_blocking
472
471
 
473
472
  # Initialize async queue for streaming responses
474
473
  self.response_queue = asyncio.Queue(queue_size)
@@ -484,15 +483,6 @@ class ChatObject:
484
483
  """
485
484
  return self._err
486
485
 
487
- def call(self):
488
- """
489
- Get callable object
490
-
491
- Returns:
492
- Callable object (usually the class's __call__ method)
493
- """
494
- return self.__call__()
495
-
496
486
  def is_running(self) -> bool:
497
487
  """
498
488
  Check if the task is running
@@ -518,14 +508,31 @@ class ChatObject:
518
508
  """
519
509
  self._is_done = True
520
510
  self._is_running = False
521
- self._task.cancel()
511
+ if hasattr(self, "_task") and not self._task.done():
512
+ self._task.cancel()
522
513
 
523
514
  def __await__(self):
524
515
  if not hasattr(self, "_task"):
525
516
  raise RuntimeError("ChatObject not running")
526
517
  return self._task.__await__()
527
518
 
528
- async def __call__(self) -> None:
519
+ async def __aenter__(self) -> Self:
520
+ if not hasattr(self, "_task"):
521
+ raise RuntimeError("ChatObject not running")
522
+ if self._has_consumer:
523
+ raise RuntimeError("ChatObject already has a consumer")
524
+ return self
525
+
526
+ async def __aexit__(self, exc_type, exc_val, exc_tb):
527
+ self.terminate()
528
+
529
+ def begin(self) -> Self:
530
+ if not hasattr(self, "_task"):
531
+ logger.debug("Starting chat object task...")
532
+ self._task = asyncio.create_task(self._entry())
533
+ return self
534
+
535
+ async def _entry(self) -> None:
529
536
  """Call chat object to process messages
530
537
 
531
538
  Args:
@@ -533,11 +540,6 @@ class ChatObject:
533
540
  matcher: Matcher
534
541
  bot: Bot instance
535
542
  """
536
- if not self._has_task:
537
- logger.debug("Starting chat object task...")
538
- self._has_task = True
539
- self._task = asyncio.create_task(self.__call__())
540
- return await self._task if self._wait else None
541
543
  if not self._is_running and not self._is_done:
542
544
  self.stream_id = uuid4().hex
543
545
  logger.debug(f"Starting chat processing, stream ID:{self.stream_id}")
@@ -553,6 +555,7 @@ class ChatObject:
553
555
  self._is_done = True
554
556
  self.end_at = datetime.now(utc)
555
557
  chat_manager.running_chat_object_id2map.pop(self.stream_id, None)
558
+ chat_manager.clean_obj(self.session_id, 1000) # To avoid memory leaks
556
559
  logger.debug("Chat event processing completed")
557
560
 
558
561
  else:
@@ -686,6 +689,9 @@ class ChatObject:
686
689
  Yields:
687
690
  Either a string or MessageContent object from the response queue
688
691
  """
692
+ if self._has_consumer:
693
+ raise RuntimeError("Queue is already being consumed.")
694
+ self._has_consumer = True
689
695
  return self._response_generator()
690
696
 
691
697
  async def full_response(self) -> str:
@@ -0,0 +1,368 @@
1
+ import inspect
2
+ import re
3
+ import typing
4
+ from asyncio import iscoroutinefunction
5
+ from collections.abc import Awaitable, Callable
6
+ from functools import wraps
7
+ from typing import Any, ClassVar, get_args, get_origin, get_type_hints, overload
8
+
9
+ from typing_extensions import Self
10
+
11
+ from .models import (
12
+ JSON_OBJECT_TYPE,
13
+ FunctionDefinitionSchema,
14
+ FunctionParametersSchema,
15
+ FunctionPropertySchema,
16
+ ToolContext,
17
+ ToolData,
18
+ ToolFunctionSchema,
19
+ )
20
+
21
+ T = typing.TypeVar("T")
22
+
23
+
24
+ class ToolsManager:
25
+ _instance = None
26
+ _models: ClassVar[dict[str, ToolData]] = {}
27
+ _disabled_tools: ClassVar[set[str]] = (
28
+ set()
29
+ ) # Disabled tools, has_tool and get_tool will not return disabled tools
30
+
31
+ def __new__(cls) -> Self:
32
+ if cls._instance is None:
33
+ cls._instance = super().__new__(cls)
34
+ return cls._instance
35
+
36
+ def has_tool(self, name: str) -> bool:
37
+ return False if name in self._disabled_tools else name in self._models
38
+
39
+ @overload
40
+ def get_tool(self, name: str) -> ToolData | None: ...
41
+ @overload
42
+ def get_tool(self, name: str, default: T) -> ToolData | T: ...
43
+ def get_tool(self, name: str, default: T = None) -> ToolData | T | None:
44
+ if not self.has_tool(name):
45
+ return default
46
+ tool: ToolData = self._models[name]
47
+ return tool if tool.enable_if() else default
48
+
49
+ @overload
50
+ def get_tool_meta(self, name: str) -> ToolFunctionSchema | None: ...
51
+ @overload
52
+ def get_tool_meta(self, name: str, default: T) -> ToolFunctionSchema | T: ...
53
+ def get_tool_meta(
54
+ self, name: str, default: T | None = None
55
+ ) -> ToolFunctionSchema | None | T:
56
+ func_data = self.get_tool(name)
57
+ if func_data is None:
58
+ return default
59
+ if isinstance(func_data, ToolData):
60
+ return func_data.data
61
+ return default
62
+
63
+ @overload
64
+ def get_tool_func(
65
+ self, name: str, default: T
66
+ ) -> (
67
+ Callable[[dict[str, Any]], Awaitable[str]]
68
+ | Callable[[ToolContext], Awaitable[str | None]]
69
+ | T
70
+ ): ...
71
+ @overload
72
+ def get_tool_func(
73
+ self,
74
+ name: str,
75
+ ) -> (
76
+ Callable[[dict[str, Any]], Awaitable[str]]
77
+ | Callable[[ToolContext], Awaitable[str | None]]
78
+ | None
79
+ ): ...
80
+ def get_tool_func(
81
+ self, name: str, default: T | None = None
82
+ ) -> (
83
+ Callable[[dict[str, Any]], Awaitable[str]]
84
+ | Callable[[ToolContext], Awaitable[str | None]]
85
+ | None
86
+ | T
87
+ ):
88
+ func_data = self.get_tool(name)
89
+ if func_data is None:
90
+ return default
91
+ if isinstance(func_data, ToolData):
92
+ return func_data.func
93
+ return default
94
+
95
+ def get_tools(self) -> dict[str, ToolData]:
96
+ return {
97
+ name: data
98
+ for name, data in self._models.items()
99
+ if (name not in self._disabled_tools and data.enable_if())
100
+ }
101
+
102
+ def tools_meta(self) -> dict[str, ToolFunctionSchema]:
103
+ return {
104
+ k: v.data
105
+ for k, v in self._models.items()
106
+ if (k not in self._disabled_tools and v.enable_if())
107
+ }
108
+
109
+ def tools_meta_dict(self, **kwargs) -> dict[str, dict[str, Any]]:
110
+ return {
111
+ k: v.data.model_dump(**kwargs)
112
+ for k, v in self._models.items()
113
+ if (k not in self._disabled_tools and v.enable_if())
114
+ }
115
+
116
+ def register_tool(self, tool: ToolData) -> None:
117
+ if tool.data.function.name not in self._models:
118
+ self._models[tool.data.function.name] = tool
119
+ else:
120
+ raise ValueError(f"Tool {tool.data.function.name} already exists")
121
+
122
+ def remove_tool(self, name: str) -> None:
123
+ self._models.pop(name, None)
124
+ if name in self._disabled_tools:
125
+ self._disabled_tools.remove(name)
126
+
127
+ def enable_tool(self, name: str) -> None:
128
+ if name in self._disabled_tools:
129
+ self._disabled_tools.remove(name)
130
+ else:
131
+ raise ValueError(f"Tool {name} is not disabled")
132
+
133
+ def disable_tool(self, name: str) -> None:
134
+ if self.has_tool(name):
135
+ self._disabled_tools.add(name)
136
+ else:
137
+ raise ValueError(f"Tool {name} does not exist or has been disabled")
138
+
139
+ def get_disabled_tools(self) -> list[str]:
140
+ return list(self._disabled_tools)
141
+
142
+
143
+ def _parse_google_docstring(docstring: str | None) -> tuple[str, dict[str, str]]:
144
+ """
145
+ Parse Google-style docstring to extract function description and parameter descriptions.
146
+
147
+ Args:
148
+ docstring: The docstring to parse
149
+
150
+ Returns:
151
+ A tuple containing (function_description, parameter_descriptions_dict)
152
+ """
153
+ if not docstring:
154
+ return "(no description provided for this tool)", {}
155
+
156
+ # Clean up the docstring
157
+ lines = [line.strip() for line in docstring.split("\n") if line.strip()]
158
+
159
+ # Find the index where Args section starts
160
+ args_start_idx = -1
161
+ for i, line in enumerate(lines):
162
+ if line.lower().startswith("args:"):
163
+ args_start_idx = i
164
+ break
165
+
166
+ # Extract function description (everything before Args section)
167
+ if args_start_idx != -1:
168
+ func_desc_lines = lines[:args_start_idx]
169
+ func_desc = " ".join(func_desc_lines).strip()
170
+
171
+ # Extract Args section
172
+ args_lines = lines[args_start_idx + 1 :]
173
+ else:
174
+ # No Args section found
175
+ func_desc = " ".join(lines).strip()
176
+ args_lines = []
177
+
178
+ # Process Args section
179
+ param_descriptions = {}
180
+
181
+ # Pattern to match parameter descriptions in the format:
182
+ # param_name (type): description
183
+ # or
184
+ # param_name: description
185
+ param_pattern = r"^([a-zA-Z_][a-zA-Z0-9_]*)\s*(?:\(([^)]+)\))?\s*:\s*(.*)"
186
+
187
+ for line in args_lines:
188
+ # Look for parameter pattern at the beginning of the line or after whitespace
189
+ match = re.match(param_pattern, line)
190
+ if match:
191
+ param_name = match.group(1)
192
+ # param_type = match.group(2) # We don't need the type since it's in the annotation
193
+ param_desc = match.group(3).strip()
194
+
195
+ if param_desc:
196
+ param_descriptions[param_name] = param_desc
197
+ else:
198
+ param_descriptions[param_name] = f"Parameter {param_name}"
199
+
200
+ if not func_desc:
201
+ func_desc = "(no description provided for this tool)"
202
+
203
+ return func_desc, param_descriptions
204
+
205
+
206
+ def simple_tool(func: Callable[..., Any | Awaitable[Any]]):
207
+ """
208
+ A decorator that creates a ToolData object based on the function signature and annotations.
209
+ It automatically generates parameter descriptions and metadata.
210
+
211
+ Here is an example of how to use this decorator:
212
+
213
+ ```python
214
+ @simple_tool
215
+ def add(a: int, b: int) -> int:
216
+ \"""Add two numbers together.
217
+
218
+ Args:
219
+ a (int): The first number.
220
+ b (int): The second number.
221
+
222
+ Returns:
223
+ int: The sum of the two numbers.
224
+ \"""
225
+ return a + b
226
+ ```
227
+ """
228
+ signature: inspect.Signature = inspect.signature(func)
229
+
230
+ # Parse Google-style docstring to get function and parameter descriptions
231
+ func_desc, param_descriptions = _parse_google_docstring(func.__doc__)
232
+
233
+ # Get the type hints for the function
234
+ type_hints: dict[str, Any] = get_type_hints(
235
+ func, globalns=globals(), localns=locals()
236
+ )
237
+
238
+ # Prepare parameters schema
239
+ properties = {}
240
+ required = []
241
+
242
+ for param_name, param in signature.parameters.items():
243
+ # Skip 'self' parameter for methods
244
+ if param_name == "self":
245
+ continue
246
+
247
+ # Determine parameter type from type hints
248
+ param_type = type_hints.get(param_name)
249
+
250
+ # If parameter has no type hint, default to string
251
+ json_type: JSON_OBJECT_TYPE = "string"
252
+ if param_type:
253
+ # Map Python types to JSON schema types
254
+ if hasattr(param_type, "__origin__"):
255
+ origin = get_origin(param_type)
256
+ if origin is not None:
257
+ if issubclass(origin, list):
258
+ json_type = "array"
259
+ elif issubclass(origin, dict):
260
+ json_type = "object"
261
+ elif origin is typing.Union:
262
+ args = get_args(param_type)
263
+ if type(None) in args:
264
+ # Handle Optional types - don't add to required
265
+ pass
266
+ else:
267
+ # For Union types, use the first non-None type if available
268
+ non_none_types = [
269
+ arg for arg in args if arg is not type(None)
270
+ ]
271
+ if non_none_types:
272
+ param_type = non_none_types[0]
273
+ json_type = _python_type_to_json_type(param_type)
274
+ else:
275
+ json_type = _python_type_to_json_type(param_type)
276
+ else:
277
+ json_type = _python_type_to_json_type(param_type)
278
+
279
+ # Get parameter description from parsed docstring if available
280
+ param_desc = param_descriptions.get(param_name, f"Parameter {param_name}")
281
+
282
+ # Check if parameter is required (no default value)
283
+ is_required = param.default == inspect.Parameter.empty
284
+ if is_required:
285
+ required.append(param_name)
286
+ property_schema = FunctionPropertySchema(type=json_type, description=param_desc)
287
+
288
+ properties[param_name] = property_schema
289
+
290
+ parameters_schema = FunctionParametersSchema(
291
+ type="object", properties=properties if properties else None, required=required
292
+ )
293
+
294
+ function_def = FunctionDefinitionSchema(
295
+ name=func.__name__, description=func_desc, parameters=parameters_schema
296
+ )
297
+
298
+ # Create a wrapper function that accepts a dictionary of parameters
299
+ @on_tools(function_def, strict=True)
300
+ @wraps(func)
301
+ async def tool_wrapper(params: dict[str, Any]) -> str:
302
+ bound_args: inspect.BoundArguments = signature.bind(**params)
303
+ bound_args.apply_defaults()
304
+
305
+ result = (
306
+ await func(**bound_args.arguments)
307
+ if iscoroutinefunction(func)
308
+ else func(**bound_args.arguments)
309
+ )
310
+
311
+ # Convert result to string as expected by the schema
312
+ return str(result)
313
+
314
+ return tool_wrapper
315
+
316
+
317
+ def _python_type_to_json_type(python_type: type[Any]) -> JSON_OBJECT_TYPE:
318
+ """Convert Python type to JSON schema type."""
319
+ if python_type is str:
320
+ return "string"
321
+ elif python_type is int:
322
+ return "integer"
323
+ elif python_type is float:
324
+ return "number"
325
+ elif python_type is bool:
326
+ return "boolean"
327
+ elif python_type is list:
328
+ return "array"
329
+ elif python_type is dict:
330
+ return "object"
331
+ else:
332
+ # Default to string for unrecognized types
333
+ return "string"
334
+
335
+
336
+ def on_tools(
337
+ data: FunctionDefinitionSchema,
338
+ custom_run: bool = False,
339
+ strict: bool = False,
340
+ enable_if: Callable[[], bool] = lambda: True,
341
+ ) -> Callable[
342
+ ...,
343
+ Callable[[dict[str, Any]], Awaitable[str]]
344
+ | Callable[[ToolContext], Awaitable[str | None]],
345
+ ]:
346
+ """Tool registration decorator
347
+
348
+ Args:
349
+ data (FunctionDefinitionSchema): Function metadata
350
+ custom_run (bool, optional): Whether to enable custom run mode. Defaults to False.
351
+ strict (bool, optional): Whether to enable strict mode. Defaults to False.
352
+ show_call (bool, optional): Whether to show tool call. Defaults to True.
353
+ """
354
+
355
+ def decorator(
356
+ func: Callable[[dict[str, Any]], Awaitable[str]]
357
+ | Callable[[ToolContext], Awaitable[str | None]],
358
+ ):
359
+ tool_data = ToolData(
360
+ func=func,
361
+ data=ToolFunctionSchema(function=data, type="function", strict=strict),
362
+ custom_run=custom_run,
363
+ enable_if=enable_if,
364
+ )
365
+ ToolsManager().register_tool(tool_data)
366
+ return func
367
+
368
+ return decorator
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: amrita_core
3
- Version: 0.1.0
3
+ Version: 0.2.0
4
4
  Summary: Agent core of Project Amrita
5
5
  Project-URL: Homepage, https://github.com/AmritaBot/AmritaCore
6
6
  Project-URL: Source, https://github.com/AmritaBot/AmritaCore
@@ -60,6 +60,8 @@ This repository contains documentation organized as follows:
60
60
 
61
61
  Documentation is currently under construction. For quick start, please refer to the examples in the `demo/` folder.
62
62
 
63
+ Please view [Docs](https://amrita-core.suggar.top) for more information.
64
+
63
65
  ## 🛠️ Quick Start
64
66
 
65
67
  To quickly start using AmritaCore, check out the examples in the [demo](./demo/) directory. The basic example demonstrates how to initialize the core, configure settings, and run a simple chat session with the AI assistant.
@@ -1,163 +0,0 @@
1
- import typing
2
- from collections.abc import Awaitable, Callable
3
- from typing import Any, ClassVar, overload
4
-
5
- from typing_extensions import Self
6
-
7
- from .models import FunctionDefinitionSchema, ToolContext, ToolData, ToolFunctionSchema
8
-
9
- T = typing.TypeVar("T")
10
-
11
-
12
- class ToolsManager:
13
- _instance = None
14
- _models: ClassVar[dict[str, ToolData]] = {}
15
- _disabled_tools: ClassVar[set[str]] = (
16
- set()
17
- ) # Disabled tools, has_tool and get_tool will not return disabled tools
18
-
19
- def __new__(cls) -> Self:
20
- if cls._instance is None:
21
- cls._instance = super().__new__(cls)
22
- return cls._instance
23
-
24
- def has_tool(self, name: str) -> bool:
25
- return False if name in self._disabled_tools else name in self._models
26
-
27
- @overload
28
- def get_tool(self, name: str) -> ToolData | None: ...
29
- @overload
30
- def get_tool(self, name: str, default: T) -> ToolData | T: ...
31
- def get_tool(self, name: str, default: T = None) -> ToolData | T | None:
32
- if not self.has_tool(name):
33
- return default
34
- tool: ToolData = self._models[name]
35
- return tool if tool.enable_if() else default
36
-
37
- @overload
38
- def get_tool_meta(self, name: str) -> ToolFunctionSchema | None: ...
39
- @overload
40
- def get_tool_meta(self, name: str, default: T) -> ToolFunctionSchema | T: ...
41
- def get_tool_meta(
42
- self, name: str, default: T | None = None
43
- ) -> ToolFunctionSchema | None | T:
44
- func_data = self.get_tool(name)
45
- if func_data is None:
46
- return default
47
- if isinstance(func_data, ToolData):
48
- return func_data.data
49
- return default
50
-
51
- @overload
52
- def get_tool_func(
53
- self, name: str, default: T
54
- ) -> (
55
- Callable[[dict[str, Any]], Awaitable[str]]
56
- | Callable[[ToolContext], Awaitable[str | None]]
57
- | T
58
- ): ...
59
- @overload
60
- def get_tool_func(
61
- self,
62
- name: str,
63
- ) -> (
64
- Callable[[dict[str, Any]], Awaitable[str]]
65
- | Callable[[ToolContext], Awaitable[str | None]]
66
- | None
67
- ): ...
68
- def get_tool_func(
69
- self, name: str, default: T | None = None
70
- ) -> (
71
- Callable[[dict[str, Any]], Awaitable[str]]
72
- | Callable[[ToolContext], Awaitable[str | None]]
73
- | None
74
- | T
75
- ):
76
- func_data = self.get_tool(name)
77
- if func_data is None:
78
- return default
79
- if isinstance(func_data, ToolData):
80
- return func_data.func
81
- return default
82
-
83
- def get_tools(self) -> dict[str, ToolData]:
84
- return {
85
- name: data
86
- for name, data in self._models.items()
87
- if (name not in self._disabled_tools and data.enable_if())
88
- }
89
-
90
- def tools_meta(self) -> dict[str, ToolFunctionSchema]:
91
- return {
92
- k: v.data
93
- for k, v in self._models.items()
94
- if (k not in self._disabled_tools and v.enable_if())
95
- }
96
-
97
- def tools_meta_dict(self, **kwargs) -> dict[str, dict[str, Any]]:
98
- return {
99
- k: v.data.model_dump(**kwargs)
100
- for k, v in self._models.items()
101
- if (k not in self._disabled_tools and v.enable_if())
102
- }
103
-
104
- def register_tool(self, tool: ToolData) -> None:
105
- if tool.data.function.name not in self._models:
106
- self._models[tool.data.function.name] = tool
107
- else:
108
- raise ValueError(f"Tool {tool.data.function.name} already exists")
109
-
110
- def remove_tool(self, name: str) -> None:
111
- self._models.pop(name, None)
112
- if name in self._disabled_tools:
113
- self._disabled_tools.remove(name)
114
-
115
- def enable_tool(self, name: str) -> None:
116
- if name in self._disabled_tools:
117
- self._disabled_tools.remove(name)
118
- else:
119
- raise ValueError(f"Tool {name} is not disabled")
120
-
121
- def disable_tool(self, name: str) -> None:
122
- if self.has_tool(name):
123
- self._disabled_tools.add(name)
124
- else:
125
- raise ValueError(f"Tool {name} does not exist or has been disabled")
126
-
127
- def get_disabled_tools(self) -> list[str]:
128
- return list(self._disabled_tools)
129
-
130
-
131
- def on_tools(
132
- data: FunctionDefinitionSchema,
133
- custom_run: bool = False,
134
- strict: bool = False,
135
- enable_if: Callable[[], bool] = lambda: True,
136
- ) -> Callable[
137
- ...,
138
- Callable[[dict[str, Any]], Awaitable[str]]
139
- | Callable[[ToolContext], Awaitable[str | None]],
140
- ]:
141
- """Tool registration decorator
142
-
143
- Args:
144
- data (FunctionDefinitionSchema): Function metadata
145
- custom_run (bool, optional): Whether to enable custom run mode. Defaults to False.
146
- strict (bool, optional): Whether to enable strict mode. Defaults to False.
147
- show_call (bool, optional): Whether to show tool call. Defaults to True.
148
- """
149
-
150
- def decorator(
151
- func: Callable[[dict[str, Any]], Awaitable[str]]
152
- | Callable[[ToolContext], Awaitable[str | None]],
153
- ):
154
- tool_data = ToolData(
155
- func=func,
156
- data=ToolFunctionSchema(function=data, type="function", strict=strict),
157
- custom_run=custom_run,
158
- enable_if=enable_if,
159
- )
160
- ToolsManager().register_tool(tool_data)
161
- return func
162
-
163
- return decorator
File without changes
File without changes