neuro-simulator 0.3.2__py3-none-any.whl → 0.4.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.
- neuro_simulator/agent/core.py +103 -136
- neuro_simulator/agent/llm.py +1 -1
- neuro_simulator/agent/memory/manager.py +24 -103
- neuro_simulator/agent/memory_prompt.txt +14 -0
- neuro_simulator/agent/neuro_prompt.txt +32 -0
- neuro_simulator/agent/tools/add_temp_memory.py +61 -0
- neuro_simulator/agent/tools/add_to_core_memory_block.py +64 -0
- neuro_simulator/agent/tools/base.py +56 -0
- neuro_simulator/agent/tools/create_core_memory_block.py +78 -0
- neuro_simulator/agent/tools/delete_core_memory_block.py +44 -0
- neuro_simulator/agent/tools/get_core_memory_block.py +44 -0
- neuro_simulator/agent/tools/get_core_memory_blocks.py +30 -0
- neuro_simulator/agent/tools/manager.py +143 -0
- neuro_simulator/agent/tools/remove_from_core_memory_block.py +65 -0
- neuro_simulator/agent/tools/speak.py +56 -0
- neuro_simulator/agent/tools/update_core_memory_block.py +65 -0
- neuro_simulator/api/system.py +5 -2
- neuro_simulator/cli.py +83 -53
- neuro_simulator/core/agent_factory.py +0 -1
- neuro_simulator/core/application.py +72 -43
- neuro_simulator/core/config.py +66 -63
- neuro_simulator/core/path_manager.py +69 -0
- neuro_simulator/services/audience.py +0 -2
- neuro_simulator/services/audio.py +0 -1
- neuro_simulator/services/builtin.py +10 -25
- neuro_simulator/services/letta.py +19 -1
- neuro_simulator/services/stream.py +24 -21
- neuro_simulator/utils/logging.py +9 -0
- neuro_simulator/utils/queue.py +27 -4
- neuro_simulator/utils/websocket.py +1 -3
- {neuro_simulator-0.3.2.dist-info → neuro_simulator-0.4.0.dist-info}/METADATA +1 -1
- neuro_simulator-0.4.0.dist-info/RECORD +46 -0
- neuro_simulator/agent/base.py +0 -43
- neuro_simulator/agent/factory.py +0 -30
- neuro_simulator/agent/tools/core.py +0 -102
- neuro_simulator/api/stream.py +0 -1
- neuro_simulator-0.3.2.dist-info/RECORD +0 -36
- {neuro_simulator-0.3.2.dist-info → neuro_simulator-0.4.0.dist-info}/WHEEL +0 -0
- {neuro_simulator-0.3.2.dist-info → neuro_simulator-0.4.0.dist-info}/entry_points.txt +0 -0
- {neuro_simulator-0.3.2.dist-info → neuro_simulator-0.4.0.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,64 @@
|
|
1
|
+
# neuro_simulator/agent/tools/add_to_core_memory_block.py
|
2
|
+
"""The Add to Core Memory Block tool for the agent."""
|
3
|
+
|
4
|
+
from typing import Dict, Any, List
|
5
|
+
|
6
|
+
from neuro_simulator.agent.tools.base import BaseTool
|
7
|
+
from neuro_simulator.agent.memory.manager import MemoryManager
|
8
|
+
|
9
|
+
class AddToCoreMemoryBlockTool(BaseTool):
|
10
|
+
"""Tool to add an item to an existing core memory block's content list."""
|
11
|
+
|
12
|
+
def __init__(self, memory_manager: MemoryManager):
|
13
|
+
self.memory_manager = memory_manager
|
14
|
+
|
15
|
+
@property
|
16
|
+
def name(self) -> str:
|
17
|
+
return "add_to_core_memory_block"
|
18
|
+
|
19
|
+
@property
|
20
|
+
def description(self) -> str:
|
21
|
+
return "Adds a new string item to the content list of a specific core memory block."
|
22
|
+
|
23
|
+
@property
|
24
|
+
def parameters(self) -> List[Dict[str, Any]]:
|
25
|
+
return [
|
26
|
+
{
|
27
|
+
"name": "block_id",
|
28
|
+
"type": "string",
|
29
|
+
"description": "The ID of the memory block to add to.",
|
30
|
+
"required": True,
|
31
|
+
},
|
32
|
+
{
|
33
|
+
"name": "item",
|
34
|
+
"type": "string",
|
35
|
+
"description": "The new string item to add to the block's content list.",
|
36
|
+
"required": True,
|
37
|
+
}
|
38
|
+
]
|
39
|
+
|
40
|
+
async def execute(self, **kwargs: Any) -> Dict[str, Any]:
|
41
|
+
block_id = kwargs.get("block_id")
|
42
|
+
item = kwargs.get("item")
|
43
|
+
if not block_id or not item:
|
44
|
+
raise ValueError("The 'block_id' and 'item' parameters are required.")
|
45
|
+
|
46
|
+
# This functionality doesn't exist in MemoryManager, so we need to implement it here.
|
47
|
+
# It's a common pattern: get, modify, save.
|
48
|
+
block = await self.memory_manager.get_core_memory_block(block_id)
|
49
|
+
if block is None:
|
50
|
+
raise ValueError(f"Block '{block_id}' not found.")
|
51
|
+
|
52
|
+
content = block.get("content", [])
|
53
|
+
if not isinstance(content, list):
|
54
|
+
# Handle case where content might not be a list
|
55
|
+
raise TypeError(f"Content of block '{block_id}' is not a list.")
|
56
|
+
|
57
|
+
content.append(item)
|
58
|
+
|
59
|
+
await self.memory_manager.update_core_memory_block(block_id=block_id, content=content)
|
60
|
+
|
61
|
+
return {
|
62
|
+
"status": "success",
|
63
|
+
"message": f"Added item to core memory block '{block_id}'."
|
64
|
+
}
|
@@ -0,0 +1,56 @@
|
|
1
|
+
# neuro_simulator/agent/tools/base.py
|
2
|
+
"""Base classes and definitions for the tool system."""
|
3
|
+
|
4
|
+
from abc import ABC, abstractmethod
|
5
|
+
from typing import Dict, Any, List, Coroutine
|
6
|
+
|
7
|
+
class BaseTool(ABC):
|
8
|
+
"""
|
9
|
+
Abstract base class for all tools.
|
10
|
+
It defines the standard interface that all tools must implement to be discoverable and executable.
|
11
|
+
"""
|
12
|
+
|
13
|
+
@property
|
14
|
+
@abstractmethod
|
15
|
+
def name(self) -> str:
|
16
|
+
"""The unique name of the tool (e.g., 'speak', 'create_core_memory_block')."""
|
17
|
+
pass
|
18
|
+
|
19
|
+
@property
|
20
|
+
@abstractmethod
|
21
|
+
def description(self) -> str:
|
22
|
+
"""A concise description of what the tool does, intended for use by an LLM."""
|
23
|
+
pass
|
24
|
+
|
25
|
+
@property
|
26
|
+
@abstractmethod
|
27
|
+
def parameters(self) -> List[Dict[str, Any]]:
|
28
|
+
"""
|
29
|
+
A list of dictionaries describing the tool's parameters.
|
30
|
+
This follows a JSON Schema-like format.
|
31
|
+
Example:
|
32
|
+
return [
|
33
|
+
{"name": "param1", "type": "string", "description": "The first parameter.", "required": True},
|
34
|
+
{"name": "param2", "type": "integer", "description": "The second parameter.", "required": False}
|
35
|
+
]
|
36
|
+
"""
|
37
|
+
pass
|
38
|
+
|
39
|
+
@abstractmethod
|
40
|
+
async def execute(self, **kwargs: Any) -> Dict[str, Any]:
|
41
|
+
"""
|
42
|
+
The method that executes the tool's logic.
|
43
|
+
It must return a JSON-serializable dictionary.
|
44
|
+
"""
|
45
|
+
pass
|
46
|
+
|
47
|
+
def get_schema(self) -> Dict[str, Any]:
|
48
|
+
"""
|
49
|
+
Returns a serializable dictionary representing the tool's public schema.
|
50
|
+
This is used by the ToolManager to expose tools to agents or future external services.
|
51
|
+
"""
|
52
|
+
return {
|
53
|
+
"name": self.name,
|
54
|
+
"description": self.description,
|
55
|
+
"parameters": self.parameters
|
56
|
+
}
|
@@ -0,0 +1,78 @@
|
|
1
|
+
# neuro_simulator/agent/tools/create_core_memory_block.py
|
2
|
+
"""The Create Core Memory Block tool for the agent."""
|
3
|
+
|
4
|
+
from typing import Dict, Any, List
|
5
|
+
|
6
|
+
from neuro_simulator.agent.tools.base import BaseTool
|
7
|
+
from neuro_simulator.agent.memory.manager import MemoryManager
|
8
|
+
|
9
|
+
class CreateCoreMemoryBlockTool(BaseTool):
|
10
|
+
"""Tool to create a new core memory block."""
|
11
|
+
|
12
|
+
def __init__(self, memory_manager: MemoryManager):
|
13
|
+
"""Initializes the CreateCoreMemoryBlockTool."""
|
14
|
+
self.memory_manager = memory_manager
|
15
|
+
|
16
|
+
@property
|
17
|
+
def name(self) -> str:
|
18
|
+
return "create_core_memory_block"
|
19
|
+
|
20
|
+
@property
|
21
|
+
def description(self) -> str:
|
22
|
+
return "Creates a new core memory block with a specified title and description. Returns the new block's ID."
|
23
|
+
|
24
|
+
@property
|
25
|
+
def parameters(self) -> List[Dict[str, Any]]:
|
26
|
+
return [
|
27
|
+
{
|
28
|
+
"name": "title",
|
29
|
+
"type": "string",
|
30
|
+
"description": "The title for the new memory block.",
|
31
|
+
"required": True,
|
32
|
+
},
|
33
|
+
{
|
34
|
+
"name": "description",
|
35
|
+
"type": "string",
|
36
|
+
"description": "A short description of the purpose of this memory block.",
|
37
|
+
"required": True,
|
38
|
+
},
|
39
|
+
{
|
40
|
+
"name": "content",
|
41
|
+
"type": "array",
|
42
|
+
"description": "An optional list of initial string entries for the block's content.",
|
43
|
+
"required": False,
|
44
|
+
}
|
45
|
+
]
|
46
|
+
|
47
|
+
async def execute(self, **kwargs: Any) -> Dict[str, Any]:
|
48
|
+
"""
|
49
|
+
Executes the action to create a new core memory block.
|
50
|
+
|
51
|
+
Args:
|
52
|
+
**kwargs: Must contain 'title' and 'description', and optionally 'content'.
|
53
|
+
|
54
|
+
Returns:
|
55
|
+
A dictionary with the result, including the new block's ID.
|
56
|
+
"""
|
57
|
+
title = kwargs.get("title")
|
58
|
+
description = kwargs.get("description")
|
59
|
+
content = kwargs.get("content", [])
|
60
|
+
|
61
|
+
if not isinstance(title, str) or not title:
|
62
|
+
raise ValueError("The 'title' parameter must be a non-empty string.")
|
63
|
+
if not isinstance(description, str) or not description:
|
64
|
+
raise ValueError("The 'description' parameter must be a non-empty string.")
|
65
|
+
if not isinstance(content, list):
|
66
|
+
raise ValueError("The 'content' parameter must be a list of strings.")
|
67
|
+
|
68
|
+
block_id = await self.memory_manager.create_core_memory_block(
|
69
|
+
title=title,
|
70
|
+
description=description,
|
71
|
+
content=content
|
72
|
+
)
|
73
|
+
|
74
|
+
return {
|
75
|
+
"status": "success",
|
76
|
+
"message": f"Created core memory block '{block_id}' with title '{title}'.",
|
77
|
+
"block_id": block_id
|
78
|
+
}
|
@@ -0,0 +1,44 @@
|
|
1
|
+
# neuro_simulator/agent/tools/delete_core_memory_block.py
|
2
|
+
"""The Delete Core Memory Block tool for the agent."""
|
3
|
+
|
4
|
+
from typing import Dict, Any, List
|
5
|
+
|
6
|
+
from neuro_simulator.agent.tools.base import BaseTool
|
7
|
+
from neuro_simulator.agent.memory.manager import MemoryManager
|
8
|
+
|
9
|
+
class DeleteCoreMemoryBlockTool(BaseTool):
|
10
|
+
"""Tool to delete an existing core memory block."""
|
11
|
+
|
12
|
+
def __init__(self, memory_manager: MemoryManager):
|
13
|
+
self.memory_manager = memory_manager
|
14
|
+
|
15
|
+
@property
|
16
|
+
def name(self) -> str:
|
17
|
+
return "delete_core_memory_block"
|
18
|
+
|
19
|
+
@property
|
20
|
+
def description(self) -> str:
|
21
|
+
return "Deletes a core memory block using its ID."
|
22
|
+
|
23
|
+
@property
|
24
|
+
def parameters(self) -> List[Dict[str, Any]]:
|
25
|
+
return [
|
26
|
+
{
|
27
|
+
"name": "block_id",
|
28
|
+
"type": "string",
|
29
|
+
"description": "The ID of the memory block to delete.",
|
30
|
+
"required": True,
|
31
|
+
}
|
32
|
+
]
|
33
|
+
|
34
|
+
async def execute(self, **kwargs: Any) -> Dict[str, Any]:
|
35
|
+
block_id = kwargs.get("block_id")
|
36
|
+
if not block_id:
|
37
|
+
raise ValueError("The 'block_id' parameter is required.")
|
38
|
+
|
39
|
+
await self.memory_manager.delete_core_memory_block(block_id=block_id)
|
40
|
+
|
41
|
+
return {
|
42
|
+
"status": "success",
|
43
|
+
"message": f"Core memory block '{block_id}' has been deleted."
|
44
|
+
}
|
@@ -0,0 +1,44 @@
|
|
1
|
+
# neuro_simulator/agent/tools/get_core_memory_block.py
|
2
|
+
"""The Get Core Memory Block tool for the agent."""
|
3
|
+
|
4
|
+
from typing import Dict, Any, List
|
5
|
+
|
6
|
+
from neuro_simulator.agent.tools.base import BaseTool
|
7
|
+
from neuro_simulator.agent.memory.manager import MemoryManager
|
8
|
+
|
9
|
+
class GetCoreMemoryBlockTool(BaseTool):
|
10
|
+
"""Tool to retrieve a single core memory block by its ID."""
|
11
|
+
|
12
|
+
def __init__(self, memory_manager: MemoryManager):
|
13
|
+
self.memory_manager = memory_manager
|
14
|
+
|
15
|
+
@property
|
16
|
+
def name(self) -> str:
|
17
|
+
return "get_core_memory_block"
|
18
|
+
|
19
|
+
@property
|
20
|
+
def description(self) -> str:
|
21
|
+
return "Retrieves the full details of a single core memory block using its ID."
|
22
|
+
|
23
|
+
@property
|
24
|
+
def parameters(self) -> List[Dict[str, Any]]:
|
25
|
+
return [
|
26
|
+
{
|
27
|
+
"name": "block_id",
|
28
|
+
"type": "string",
|
29
|
+
"description": "The ID of the memory block to retrieve.",
|
30
|
+
"required": True,
|
31
|
+
}
|
32
|
+
]
|
33
|
+
|
34
|
+
async def execute(self, **kwargs: Any) -> Dict[str, Any]:
|
35
|
+
block_id = kwargs.get("block_id")
|
36
|
+
if not block_id:
|
37
|
+
raise ValueError("The 'block_id' parameter is required.")
|
38
|
+
|
39
|
+
block = await self.memory_manager.get_core_memory_block(block_id)
|
40
|
+
|
41
|
+
if block is None:
|
42
|
+
return {"status": "error", "message": f"Block '{block_id}' not found."}
|
43
|
+
|
44
|
+
return {"status": "success", "block": block}
|
@@ -0,0 +1,30 @@
|
|
1
|
+
# neuro_simulator/agent/tools/get_core_memory_blocks.py
|
2
|
+
"""The Get Core Memory Blocks tool for the agent."""
|
3
|
+
|
4
|
+
from typing import Dict, Any, List
|
5
|
+
|
6
|
+
from neuro_simulator.agent.tools.base import BaseTool
|
7
|
+
from neuro_simulator.agent.memory.manager import MemoryManager
|
8
|
+
|
9
|
+
class GetCoreMemoryBlocksTool(BaseTool):
|
10
|
+
"""Tool to retrieve all core memory blocks."""
|
11
|
+
|
12
|
+
def __init__(self, memory_manager: MemoryManager):
|
13
|
+
self.memory_manager = memory_manager
|
14
|
+
|
15
|
+
@property
|
16
|
+
def name(self) -> str:
|
17
|
+
return "get_core_memory_blocks"
|
18
|
+
|
19
|
+
@property
|
20
|
+
def description(self) -> str:
|
21
|
+
return "Retrieves a list of all available core memory blocks, including their IDs, titles, and descriptions."
|
22
|
+
|
23
|
+
@property
|
24
|
+
def parameters(self) -> List[Dict[str, Any]]:
|
25
|
+
return []
|
26
|
+
|
27
|
+
async def execute(self, **kwargs: Any) -> Dict[str, Any]:
|
28
|
+
blocks = await self.memory_manager.get_core_memory_blocks()
|
29
|
+
# The result should be JSON serializable, which a Dict of Dicts already is.
|
30
|
+
return {"status": "success", "blocks": blocks}
|
@@ -0,0 +1,143 @@
|
|
1
|
+
# neuro_simulator/agent/tools/manager.py
|
2
|
+
"""The central tool manager for the agent, responsible for loading, managing, and executing tools."""
|
3
|
+
|
4
|
+
import os
|
5
|
+
import json
|
6
|
+
import importlib
|
7
|
+
import inspect
|
8
|
+
import logging
|
9
|
+
import shutil
|
10
|
+
from pathlib import Path
|
11
|
+
from typing import Any, Dict, List
|
12
|
+
|
13
|
+
from .base import BaseTool
|
14
|
+
from ..memory.manager import MemoryManager
|
15
|
+
from ...core.path_manager import path_manager
|
16
|
+
|
17
|
+
logger = logging.getLogger(__name__.replace("neuro_simulator", "agent", 1))
|
18
|
+
|
19
|
+
class ToolManager:
|
20
|
+
"""
|
21
|
+
Acts as a central registry and executor for all available tools.
|
22
|
+
This manager dynamically loads tools from the user's working directory.
|
23
|
+
"""
|
24
|
+
|
25
|
+
def __init__(self, memory_manager: MemoryManager):
|
26
|
+
if not path_manager:
|
27
|
+
raise RuntimeError("PathManager not initialized before ToolManager.")
|
28
|
+
self.memory_manager = memory_manager
|
29
|
+
self.tools: Dict[str, BaseTool] = {}
|
30
|
+
self.agent_tool_allocations: Dict[str, List[str]] = {}
|
31
|
+
|
32
|
+
self._copy_builtin_tools()
|
33
|
+
self.reload_tools() # Initial load
|
34
|
+
self._load_allocations()
|
35
|
+
|
36
|
+
def _copy_builtin_tools(self):
|
37
|
+
"""Copies the packaged built-in tools to the working directory, overwriting existing ones."""
|
38
|
+
try:
|
39
|
+
import pkg_resources
|
40
|
+
source_dir_str = pkg_resources.resource_filename('neuro_simulator', 'agent/tools')
|
41
|
+
source_dir = Path(source_dir_str)
|
42
|
+
except (ModuleNotFoundError, KeyError):
|
43
|
+
source_dir = Path(__file__).parent
|
44
|
+
|
45
|
+
dest_dir = path_manager.builtin_tools_dir
|
46
|
+
if not dest_dir.exists():
|
47
|
+
os.makedirs(dest_dir)
|
48
|
+
|
49
|
+
for item in os.listdir(source_dir):
|
50
|
+
source_item = source_dir / item
|
51
|
+
if source_item.is_file() and source_item.name.endswith('.py') and not source_item.name.startswith(('__', 'manager')):
|
52
|
+
shutil.copy(source_item, dest_dir / item)
|
53
|
+
logger.info(f"Built-in tools copied to {dest_dir}")
|
54
|
+
|
55
|
+
def _load_and_register_tools(self):
|
56
|
+
"""Dynamically scans the user tools directory, imports modules, and registers tool instances."""
|
57
|
+
self.tools = {}
|
58
|
+
tools_dir = path_manager.user_tools_dir
|
59
|
+
|
60
|
+
for root, _, files in os.walk(tools_dir):
|
61
|
+
for filename in files:
|
62
|
+
if filename.endswith('.py') and not filename.startswith('__'):
|
63
|
+
module_path = Path(root) / filename
|
64
|
+
# Create a module spec from the file path
|
65
|
+
spec = importlib.util.spec_from_file_location(module_path.stem, module_path)
|
66
|
+
if spec:
|
67
|
+
try:
|
68
|
+
module = importlib.util.module_from_spec(spec)
|
69
|
+
spec.loader.exec_module(module)
|
70
|
+
for _, cls in inspect.getmembers(module, inspect.isclass):
|
71
|
+
if issubclass(cls, BaseTool) and cls is not BaseTool:
|
72
|
+
tool_instance = cls(memory_manager=self.memory_manager)
|
73
|
+
if tool_instance.name in self.tools:
|
74
|
+
logger.warning(f"Duplicate tool name '{tool_instance.name}' found. Overwriting.")
|
75
|
+
self.tools[tool_instance.name] = tool_instance
|
76
|
+
logger.info(f"Successfully loaded and registered tool: {tool_instance.name}")
|
77
|
+
except Exception as e:
|
78
|
+
logger.error(f"Failed to load tool from {module_path}: {e}", exc_info=True)
|
79
|
+
|
80
|
+
def _load_allocations(self):
|
81
|
+
"""Loads tool allocations from JSON files, creating defaults if they don't exist."""
|
82
|
+
default_allocations = {
|
83
|
+
"neuro_agent": ["speak", "get_core_memory_blocks", "get_core_memory_block"],
|
84
|
+
"memory_agent": ["add_temp_memory", "create_core_memory_block", "update_core_memory_block", "delete_core_memory_block", "add_to_core_memory_block", "remove_from_core_memory_block", "get_core_memory_blocks", "get_core_memory_block"]
|
85
|
+
}
|
86
|
+
|
87
|
+
# Load neuro agent allocations
|
88
|
+
if path_manager.neuro_tools_path.exists():
|
89
|
+
with open(path_manager.neuro_tools_path, 'r', encoding='utf-8') as f:
|
90
|
+
self.agent_tool_allocations['neuro_agent'] = json.load(f)
|
91
|
+
else:
|
92
|
+
self.agent_tool_allocations['neuro_agent'] = default_allocations['neuro_agent']
|
93
|
+
with open(path_manager.neuro_tools_path, 'w', encoding='utf-8') as f:
|
94
|
+
json.dump(default_allocations['neuro_agent'], f, indent=2)
|
95
|
+
|
96
|
+
# Load memory agent allocations
|
97
|
+
if path_manager.memory_agent_tools_path.exists():
|
98
|
+
with open(path_manager.memory_agent_tools_path, 'r', encoding='utf-8') as f:
|
99
|
+
self.agent_tool_allocations['memory_agent'] = json.load(f)
|
100
|
+
else:
|
101
|
+
self.agent_tool_allocations['memory_agent'] = default_allocations['memory_agent']
|
102
|
+
with open(path_manager.memory_agent_tools_path, 'w', encoding='utf-8') as f:
|
103
|
+
json.dump(default_allocations['memory_agent'], f, indent=2)
|
104
|
+
|
105
|
+
logger.info(f"Tool allocations loaded: {self.agent_tool_allocations}")
|
106
|
+
|
107
|
+
def get_all_tool_schemas(self) -> List[Dict[str, Any]]:
|
108
|
+
return [tool.get_schema() for tool in self.tools.values()]
|
109
|
+
|
110
|
+
def get_tool_schemas_for_agent(self, agent_name: str) -> List[Dict[str, Any]]:
|
111
|
+
allowed_names = set(self.agent_tool_allocations.get(agent_name, []))
|
112
|
+
if not allowed_names:
|
113
|
+
return []
|
114
|
+
return [tool.get_schema() for tool in self.tools.values() if tool.name in allowed_names]
|
115
|
+
|
116
|
+
def reload_tools(self):
|
117
|
+
logger.info("Reloading tools...")
|
118
|
+
self._load_and_register_tools()
|
119
|
+
logger.info(f"Tools reloaded. {len(self.tools)} tools available.")
|
120
|
+
|
121
|
+
def get_allocations(self) -> Dict[str, List[str]]:
|
122
|
+
return self.agent_tool_allocations
|
123
|
+
|
124
|
+
def set_allocations(self, allocations: Dict[str, List[str]]):
|
125
|
+
self.agent_tool_allocations = allocations
|
126
|
+
# Persist the changes to the JSON files
|
127
|
+
with open(path_manager.neuro_tools_path, 'w', encoding='utf-8') as f:
|
128
|
+
json.dump(allocations.get('neuro_agent', []), f, indent=2)
|
129
|
+
with open(path_manager.memory_agent_tools_path, 'w', encoding='utf-8') as f:
|
130
|
+
json.dump(allocations.get('memory_agent', []), f, indent=2)
|
131
|
+
logger.info(f"Tool allocations updated and saved: {self.agent_tool_allocations}")
|
132
|
+
|
133
|
+
async def execute_tool(self, tool_name: str, **kwargs: Any) -> Dict[str, Any]:
|
134
|
+
if tool_name not in self.tools:
|
135
|
+
logger.error(f"Attempted to execute non-existent tool: {tool_name}")
|
136
|
+
return {"error": f"Tool '{tool_name}' not found."}
|
137
|
+
tool = self.tools[tool_name]
|
138
|
+
try:
|
139
|
+
result = await tool.execute(**kwargs)
|
140
|
+
return result
|
141
|
+
except Exception as e:
|
142
|
+
logger.error(f"Error executing tool '{tool_name}' with params {kwargs}: {e}", exc_info=True)
|
143
|
+
return {"error": f"An unexpected error occurred while executing the tool: {str(e)}"}
|
@@ -0,0 +1,65 @@
|
|
1
|
+
# neuro_simulator/agent/tools/remove_from_core_memory_block.py
|
2
|
+
"""The Remove from Core Memory Block tool for the agent."""
|
3
|
+
|
4
|
+
from typing import Dict, Any, List
|
5
|
+
|
6
|
+
from neuro_simulator.agent.tools.base import BaseTool
|
7
|
+
from neuro_simulator.agent.memory.manager import MemoryManager
|
8
|
+
|
9
|
+
class RemoveFromCoreMemoryBlockTool(BaseTool):
|
10
|
+
"""Tool to remove an item from an existing core memory block's content list by its index."""
|
11
|
+
|
12
|
+
def __init__(self, memory_manager: MemoryManager):
|
13
|
+
self.memory_manager = memory_manager
|
14
|
+
|
15
|
+
@property
|
16
|
+
def name(self) -> str:
|
17
|
+
return "remove_from_core_memory_block"
|
18
|
+
|
19
|
+
@property
|
20
|
+
def description(self) -> str:
|
21
|
+
return "Removes an item from the content list of a specific core memory block by its zero-based index."
|
22
|
+
|
23
|
+
@property
|
24
|
+
def parameters(self) -> List[Dict[str, Any]]:
|
25
|
+
return [
|
26
|
+
{
|
27
|
+
"name": "block_id",
|
28
|
+
"type": "string",
|
29
|
+
"description": "The ID of the memory block to modify.",
|
30
|
+
"required": True,
|
31
|
+
},
|
32
|
+
{
|
33
|
+
"name": "index",
|
34
|
+
"type": "integer",
|
35
|
+
"description": "The zero-based index of the item to remove from the content list.",
|
36
|
+
"required": True,
|
37
|
+
}
|
38
|
+
]
|
39
|
+
|
40
|
+
async def execute(self, **kwargs: Any) -> Dict[str, Any]:
|
41
|
+
block_id = kwargs.get("block_id")
|
42
|
+
index = kwargs.get("index")
|
43
|
+
|
44
|
+
if not block_id or index is None:
|
45
|
+
raise ValueError("The 'block_id' and 'index' parameters are required.")
|
46
|
+
|
47
|
+
block = await self.memory_manager.get_core_memory_block(block_id)
|
48
|
+
if block is None:
|
49
|
+
raise ValueError(f"Block '{block_id}' not found.")
|
50
|
+
|
51
|
+
content = block.get("content", [])
|
52
|
+
if not isinstance(content, list):
|
53
|
+
raise TypeError(f"Content of block '{block_id}' is not a list.")
|
54
|
+
|
55
|
+
try:
|
56
|
+
removed_item = content.pop(index)
|
57
|
+
except IndexError:
|
58
|
+
raise IndexError(f"Index {index} is out of bounds for content in block '{block_id}'.")
|
59
|
+
|
60
|
+
await self.memory_manager.update_core_memory_block(block_id=block_id, content=content)
|
61
|
+
|
62
|
+
return {
|
63
|
+
"status": "success",
|
64
|
+
"message": f"Removed item '{removed_item}' from core memory block '{block_id}' at index {index}."
|
65
|
+
}
|
@@ -0,0 +1,56 @@
|
|
1
|
+
# neuro_simulator/agent/tools/speak.py
|
2
|
+
"""The Speak tool for the agent."""
|
3
|
+
|
4
|
+
import logging
|
5
|
+
from typing import Dict, Any, List
|
6
|
+
|
7
|
+
from neuro_simulator.agent.tools.base import BaseTool
|
8
|
+
from neuro_simulator.agent.memory.manager import MemoryManager
|
9
|
+
|
10
|
+
logger = logging.getLogger(__name__.replace("neuro_simulator", "agent", 1))
|
11
|
+
|
12
|
+
class SpeakTool(BaseTool):
|
13
|
+
"""Tool for the agent to speak to the audience."""
|
14
|
+
|
15
|
+
def __init__(self, memory_manager: MemoryManager):
|
16
|
+
"""Initializes the SpeakTool."""
|
17
|
+
# This tool doesn't directly use the memory manager, but accepts it for consistency.
|
18
|
+
self.memory_manager = memory_manager
|
19
|
+
|
20
|
+
@property
|
21
|
+
def name(self) -> str:
|
22
|
+
return "speak"
|
23
|
+
|
24
|
+
@property
|
25
|
+
def description(self) -> str:
|
26
|
+
return "Outputs text to the user. This is the primary way to communicate with the audience."
|
27
|
+
|
28
|
+
@property
|
29
|
+
def parameters(self) -> List[Dict[str, Any]]:
|
30
|
+
return [
|
31
|
+
{
|
32
|
+
"name": "text",
|
33
|
+
"type": "string",
|
34
|
+
"description": "The text to be spoken to the audience.",
|
35
|
+
"required": True,
|
36
|
+
}
|
37
|
+
]
|
38
|
+
|
39
|
+
async def execute(self, **kwargs: Any) -> Dict[str, Any]:
|
40
|
+
"""
|
41
|
+
Executes the speak action.
|
42
|
+
|
43
|
+
Args:
|
44
|
+
**kwargs: Must contain a 'text' key with the string to be spoken.
|
45
|
+
|
46
|
+
Returns:
|
47
|
+
A dictionary confirming the action and the text that was spoken.
|
48
|
+
"""
|
49
|
+
text = kwargs.get("text")
|
50
|
+
if not isinstance(text, str) or not text:
|
51
|
+
raise ValueError("The 'text' parameter must be a non-empty string.")
|
52
|
+
|
53
|
+
logger.info(f"Agent says: {text}")
|
54
|
+
# The result of the speak tool is the text that was spoken.
|
55
|
+
# This can be used for logging or further processing.
|
56
|
+
return {"status": "success", "spoken_text": text}
|
@@ -0,0 +1,65 @@
|
|
1
|
+
# neuro_simulator/agent/tools/update_core_memory_block.py
|
2
|
+
"""The Update Core Memory Block tool for the agent."""
|
3
|
+
|
4
|
+
from typing import Dict, Any, List, Optional
|
5
|
+
|
6
|
+
from neuro_simulator.agent.tools.base import BaseTool
|
7
|
+
from neuro_simulator.agent.memory.manager import MemoryManager
|
8
|
+
|
9
|
+
class UpdateCoreMemoryBlockTool(BaseTool):
|
10
|
+
"""Tool to update an existing core memory block."""
|
11
|
+
|
12
|
+
def __init__(self, memory_manager: MemoryManager):
|
13
|
+
self.memory_manager = memory_manager
|
14
|
+
|
15
|
+
@property
|
16
|
+
def name(self) -> str:
|
17
|
+
return "update_core_memory_block"
|
18
|
+
|
19
|
+
@property
|
20
|
+
def description(self) -> str:
|
21
|
+
return "Updates an existing core memory block. All parameters are optional, only provided fields will be updated."
|
22
|
+
|
23
|
+
@property
|
24
|
+
def parameters(self) -> List[Dict[str, Any]]:
|
25
|
+
return [
|
26
|
+
{
|
27
|
+
"name": "block_id",
|
28
|
+
"type": "string",
|
29
|
+
"description": "The ID of the memory block to update.",
|
30
|
+
"required": True,
|
31
|
+
},
|
32
|
+
{
|
33
|
+
"name": "title",
|
34
|
+
"type": "string",
|
35
|
+
"description": "The new title for the memory block.",
|
36
|
+
"required": False,
|
37
|
+
},
|
38
|
+
{
|
39
|
+
"name": "description",
|
40
|
+
"type": "string",
|
41
|
+
"description": "The new description for the memory block.",
|
42
|
+
"required": False,
|
43
|
+
},
|
44
|
+
{
|
45
|
+
"name": "content",
|
46
|
+
"type": "array",
|
47
|
+
"description": "The new list of string entries, which will overwrite the existing content.",
|
48
|
+
"required": False,
|
49
|
+
}
|
50
|
+
]
|
51
|
+
|
52
|
+
async def execute(self, **kwargs: Any) -> Dict[str, Any]:
|
53
|
+
block_id = kwargs.get("block_id")
|
54
|
+
if not block_id:
|
55
|
+
raise ValueError("The 'block_id' parameter is required.")
|
56
|
+
|
57
|
+
# Only include parameters that are not None to avoid overwriting with nulls
|
58
|
+
update_payload = {k: v for k, v in kwargs.items() if v is not None}
|
59
|
+
|
60
|
+
await self.memory_manager.update_core_memory_block(**update_payload)
|
61
|
+
|
62
|
+
return {
|
63
|
+
"status": "success",
|
64
|
+
"message": f"Successfully updated core memory block '{block_id}'."
|
65
|
+
}
|
neuro_simulator/api/system.py
CHANGED
@@ -1,10 +1,13 @@
|
|
1
1
|
# neuro_simulator/api/system.py
|
2
2
|
"""API endpoints for system, config, and utility functions."""
|
3
3
|
|
4
|
-
from fastapi import APIRouter, Depends, HTTPException, status, Request
|
5
4
|
import time
|
5
|
+
from typing import Any, Dict
|
6
|
+
|
7
|
+
from fastapi import APIRouter, Depends, HTTPException, status, Request
|
6
8
|
|
7
|
-
from ..core.config import config_manager
|
9
|
+
from ..core.config import AppSettings, config_manager
|
10
|
+
from ..utils.process import process_manager
|
8
11
|
|
9
12
|
|
10
13
|
router = APIRouter(tags=["System & Utilities"])
|