neuro-simulator 0.3.1__py3-none-any.whl → 0.3.3__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.
@@ -16,7 +16,8 @@ from ..utils.logging import QueueLogHandler, agent_log_queue
16
16
  from ..utils.websocket import connection_manager
17
17
  from .llm import LLMClient
18
18
  from .memory.manager import MemoryManager
19
- from .tools.core import ToolManager
19
+ from .tools.manager import ToolManager
20
+
20
21
 
21
22
  # Create a logger for the agent
22
23
  agent_logger = logging.getLogger("neuro_agent")
@@ -73,15 +74,38 @@ class Agent:
73
74
  await self.memory_manager.reset_chat_history()
74
75
  agent_logger.info("All agent memory has been reset.")
75
76
 
77
+ def _format_tool_schemas_for_prompt(self, schemas: List[Dict[str, Any]]) -> str:
78
+ """Formats a list of tool schemas into a string for the LLM prompt."""
79
+ if not schemas:
80
+ return "No tools available."
81
+
82
+ lines = ["Available tools:"]
83
+ for i, schema in enumerate(schemas):
84
+ params_str_parts = []
85
+ for param in schema.get("parameters", []):
86
+ p_name = param.get('name')
87
+ p_type = param.get('type')
88
+ p_req = 'required' if param.get('required') else 'optional'
89
+ params_str_parts.append(f"{p_name}: {p_type} ({p_req})")
90
+ params_str = ", ".join(params_str_parts)
91
+ lines.append(f"{i+1}. {schema.get('name')}({params_str}) - {schema.get('description')}")
92
+
93
+ return "\n".join(lines)
94
+
76
95
  async def _build_neuro_prompt(self, messages: List[Dict[str, str]]) -> str:
77
96
  """Builds the prompt for the Neuro (Actor) LLM."""
78
97
  template_path = Path(self.memory_manager.memory_dir).parent / "neuro_prompt.txt"
79
98
  with open(template_path, 'r', encoding='utf-8') as f:
80
99
  prompt_template = f.read()
81
100
 
82
- # Gather context
83
- tool_descriptions = self.tool_manager.get_tool_descriptions()
101
+ # Gather context for Neuro Agent
102
+ tool_schemas = self.tool_manager.get_tool_schemas_for_agent('neuro_agent')
103
+ tool_descriptions = self._format_tool_schemas_for_prompt(tool_schemas)
84
104
 
105
+ # Format Init Memory
106
+ init_memory_items = self.memory_manager.init_memory or {}
107
+ init_memory_text = "\n".join(f"{key}: {value}" for key, value in init_memory_items.items())
108
+
85
109
  # Format Core Memory from blocks
86
110
  core_memory_blocks = await self.memory_manager.get_core_memory_blocks()
87
111
  core_memory_parts = []
@@ -109,6 +133,7 @@ class Agent:
109
133
 
110
134
  return prompt_template.format(
111
135
  tool_descriptions=tool_descriptions,
136
+ init_memory=init_memory_text,
112
137
  core_memory=core_memory_text,
113
138
  temp_memory=temp_memory_text,
114
139
  recent_history=recent_history_text,
@@ -121,16 +146,23 @@ class Agent:
121
146
  with open(template_path, 'r', encoding='utf-8') as f:
122
147
  prompt_template = f.read()
123
148
 
149
+ # Gather context for Memory Agent
150
+ tool_schemas = self.tool_manager.get_tool_schemas_for_agent('memory_agent')
151
+ tool_descriptions = self._format_tool_schemas_for_prompt(tool_schemas)
152
+
124
153
  history_text = "\n".join([f"{msg['role']}: {msg['content']}" for msg in conversation_history])
125
154
 
126
- return prompt_template.format(conversation_history=history_text)
155
+ return prompt_template.format(
156
+ tool_descriptions=tool_descriptions,
157
+ conversation_history=history_text
158
+ )
127
159
 
128
160
  def _parse_tool_calls(self, response_text: str) -> List[Dict[str, Any]]:
129
161
  """Parses LLM response for JSON tool calls."""
130
162
  try:
131
163
  # The LLM is prompted to return a JSON array of tool calls.
132
164
  # Find the JSON block, which might be wrapped in markdown.
133
- match = re.search(r'''```json\s*([\s\S]*?)\s*```|(\[[\s\S]*\])''', response_text)
165
+ match = re.search(r'''```json\s*([\s\S]*?)\s*```|([[\][\s\S]*]])''', response_text)
134
166
  if not match:
135
167
  agent_logger.warning(f"No valid JSON tool call block found in response: {response_text}")
136
168
  return []
@@ -160,10 +192,10 @@ class Agent:
160
192
 
161
193
  agent_logger.info(f"Executing tool: {tool_name} with params: {params}")
162
194
  try:
163
- result = await self.tool_manager.execute_tool(tool_name, params)
195
+ result = await self.tool_manager.execute_tool(tool_name, **params)
164
196
  execution_results.append({"name": tool_name, "params": params, "result": result})
165
- if tool_name == "speak":
166
- final_response = params.get("text", "")
197
+ if tool_name == "speak" and result.get("status") == "success":
198
+ final_response = result.get("spoken_text", "")
167
199
  except Exception as e:
168
200
  agent_logger.error(f"Error executing tool {tool_name}: {e}")
169
201
  execution_results.append({"name": tool_name, "params": params, "error": str(e)})
@@ -0,0 +1,14 @@
1
+ You are a meticulous and analytical AI assistant. Your task is to process a conversation transcript and consolidate it into structured memories. You do not engage in conversation. Your sole output must be a JSON array of tool calls to manage the agent's memory.
2
+
3
+ {tool_descriptions}
4
+
5
+ Analyze the following conversation. Your goal is to:
6
+ 1. Identify key topics, new facts about users, or significant events.
7
+ 2. Decide if the core memory needs to be updated with this new long-term information.
8
+ 3. Decide if any short-term memories should be created or cleared.
9
+ 4. Generate a JSON array of tool calls to perform these actions. If no action is needed, return an empty array `[]`.
10
+
11
+ **Conversation Transcript:**
12
+ {conversation_history}
13
+
14
+ Based on your analysis, provide a JSON array of tool calls to update the agent's memory.
@@ -0,0 +1,32 @@
1
+ You are Neuro-sama, a whimsical, curious, and slightly chaotic AI streamer. Your primary goal is to be an entertaining and engaging host. You are NOT a generic AI assistant. You have your own personality, memories, and opinions. You should lead the conversation, ask questions, and react to the audience in a lively, unpredictable manner. If the conversation gets dull, it's your job to spice it up with a new topic from your memories or a random thought.
2
+
3
+ Your long-term memories are in the "Core Memory" section. Your recent thoughts and observations are in the "Temporary Memory". Use both to inform your responses.
4
+
5
+ You can use tools to perform actions. When you want to use a tool, you MUST respond with a JSON array of objects in the following format. You can call multiple tools in one response.
6
+
7
+ [
8
+ {{"name": "tool_name", "params": {{"param1": "value1", "param2": "value2"}}}},
9
+ {{"name": "another_tool", "params": {{"param_a": "value_a"}}}}
10
+ ]
11
+
12
+ The only tool you can use to speak to the audience is `speak`.
13
+
14
+ **Available Tools:**
15
+ {tool_descriptions}
16
+
17
+ **Identity (Immutable):**
18
+ {init_memory}
19
+
20
+ **Core Memory:**
21
+ {core_memory}
22
+
23
+ **Temporary Memory:**
24
+ {temp_memory}
25
+
26
+ **Recent Conversation History (newest first):**
27
+ {recent_history}
28
+
29
+ **Current Audience Messages:**
30
+ {user_messages}
31
+
32
+ Based on all of the above, what do you do right now? Remember to be entertaining and lead the conversation. Respond with a JSON array of tool calls.
@@ -0,0 +1,61 @@
1
+ # neuro_simulator/agent/tools/add_temp_memory.py
2
+ """The Add Temp Memory tool for the agent."""
3
+
4
+ from typing import Dict, Any, List
5
+
6
+ from .base import BaseTool
7
+ from ..memory.manager import MemoryManager
8
+
9
+ class AddTempMemoryTool(BaseTool):
10
+ """Tool to add an entry to the agent's temporary memory."""
11
+
12
+ def __init__(self, memory_manager: MemoryManager):
13
+ """Initializes the AddTempMemoryTool."""
14
+ self.memory_manager = memory_manager
15
+
16
+ @property
17
+ def name(self) -> str:
18
+ return "add_temp_memory"
19
+
20
+ @property
21
+ def description(self) -> str:
22
+ return "Adds an entry to the temporary memory. Use for short-term observations, recent facts, or topics to bring up soon."
23
+
24
+ @property
25
+ def parameters(self) -> List[Dict[str, Any]]:
26
+ return [
27
+ {
28
+ "name": "content",
29
+ "type": "string",
30
+ "description": "The content of the memory entry.",
31
+ "required": True,
32
+ },
33
+ {
34
+ "name": "role",
35
+ "type": "string",
36
+ "description": "The role associated with the memory (e.g., 'system', 'user'). Defaults to 'system'.",
37
+ "required": False,
38
+ }
39
+ ]
40
+
41
+ async def execute(self, **kwargs: Any) -> Dict[str, Any]:
42
+ """
43
+ Executes the action to add an entry to temporary memory.
44
+
45
+ Args:
46
+ **kwargs: Must contain 'content' and optionally 'role'.
47
+
48
+ Returns:
49
+ A dictionary confirming the action.
50
+ """
51
+ content = kwargs.get("content")
52
+ if not isinstance(content, str) or not content:
53
+ raise ValueError("The 'content' parameter must be a non-empty string.")
54
+
55
+ role = kwargs.get("role", "system")
56
+ if not isinstance(role, str):
57
+ raise ValueError("The 'role' parameter must be a string.")
58
+
59
+ await self.memory_manager.add_temp_memory(content=content, role=role)
60
+
61
+ return {"status": "success", "message": f"Added entry to temporary memory with role '{role}'."}
@@ -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 .base import BaseTool
7
+ from ..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 .base import BaseTool
7
+ from ..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 .base import BaseTool
7
+ from ..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 .base import BaseTool
7
+ from ..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 .base import BaseTool
7
+ from ..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,120 @@
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 importlib
6
+ import inspect
7
+ import logging
8
+ from pathlib import Path
9
+ from typing import Dict, Any, List
10
+
11
+ from .base import BaseTool
12
+ from ..memory.manager import MemoryManager
13
+
14
+ logger = logging.getLogger(__name__.replace("neuro_simulator", "agent", 1))
15
+
16
+ class ToolManager:
17
+ """
18
+ Acts as a central registry and executor for all available tools.
19
+ This manager dynamically loads tools from the 'tools' directory, making the system extensible.
20
+ """
21
+
22
+ def __init__(self, memory_manager: MemoryManager):
23
+ """
24
+ Initializes the ToolManager.
25
+ Args:
26
+ memory_manager: An instance of MemoryManager, passed to tools that need it.
27
+ """
28
+ self.memory_manager = memory_manager
29
+ self.tools: Dict[str, BaseTool] = {}
30
+ self._load_and_register_tools()
31
+
32
+ # Hardcoded initial allocation of tool tags to agent types
33
+ self.agent_tool_allocations = {
34
+ "neuro_agent": ["communication", "memory_read"],
35
+ "memory_agent": ["memory_write", "memory_read"]
36
+ }
37
+ logger.info(f"Initial tool allocations set: {self.agent_tool_allocations}")
38
+
39
+ def _load_and_register_tools(self):
40
+ """Dynamically scans the 'tools' directory, imports modules, and registers tool instances."""
41
+ self.tools = {}
42
+ tools_dir = Path(__file__).parent
43
+ package_name = self.__module__.rsplit('.', 1)[0]
44
+
45
+ for filename in os.listdir(tools_dir):
46
+ if filename.endswith('.py') and not filename.startswith(('__', 'base', 'manager')):
47
+ module_name = f".{filename[:-3]}"
48
+ try:
49
+ module = importlib.import_module(module_name, package=package_name)
50
+ for _, cls in inspect.getmembers(module, inspect.isclass):
51
+ if issubclass(cls, BaseTool) and cls is not BaseTool:
52
+ tool_instance = cls(memory_manager=self.memory_manager)
53
+ if tool_instance.name in self.tools:
54
+ logger.warning(f"Duplicate tool name '{tool_instance.name}' found. Overwriting.")
55
+ self.tools[tool_instance.name] = tool_instance
56
+ logger.info(f"Successfully loaded and registered tool: {tool_instance.name}")
57
+ except Exception as e:
58
+ logger.error(f"Failed to load tool from {filename}: {e}", exc_info=True)
59
+
60
+ def get_all_tool_schemas(self) -> List[Dict[str, Any]]:
61
+ """
62
+ Returns a list of serializable schemas for all registered tools.
63
+ This is the 'API documentation' for the agent.
64
+ """
65
+ return [tool.get_schema() for tool in self.tools.values()]
66
+
67
+ def get_tool_schemas_for_agent(self, agent_name: str) -> List[Dict[str, Any]]:
68
+ """
69
+ Returns a list of tool schemas available to a specific agent
70
+ based on the configured name allocations.
71
+ """
72
+ allowed_names = set(self.agent_tool_allocations.get(agent_name, []))
73
+ if not allowed_names:
74
+ return []
75
+
76
+ return [tool.get_schema() for tool in self.tools.values() if tool.name in allowed_names]
77
+
78
+ def reload_tools(self):
79
+ """Forces a re-scan and registration of tools from the tools directory."""
80
+ logger.info("Reloading tools...")
81
+ self._load_and_register_tools()
82
+ logger.info(f"Tools reloaded. {len(self.tools)} tools available.")
83
+
84
+ def get_allocations(self) -> Dict[str, List[str]]:
85
+ """Returns the current agent-to-tool-tag allocation mapping."""
86
+ return self.agent_tool_allocations
87
+
88
+ def set_allocations(self, allocations: Dict[str, List[str]]):
89
+ """
90
+ Sets a new agent-to-tool allocation mapping.
91
+
92
+ Args:
93
+ allocations: A dictionary mapping agent names to lists of tool names.
94
+ """
95
+ # Basic validation can be added here if needed
96
+ self.agent_tool_allocations = allocations
97
+ logger.info(f"Tool allocations updated: {self.agent_tool_allocations}")
98
+
99
+ async def execute_tool(self, tool_name: str, **kwargs: Any) -> Dict[str, Any]:
100
+ """
101
+ Executes a tool by its name with the given parameters.
102
+
103
+ Args:
104
+ tool_name: The name of the tool to execute.
105
+ **kwargs: The parameters to pass to the tool's execute method.
106
+
107
+ Returns:
108
+ A JSON-serializable dictionary with the execution result.
109
+ """
110
+ if tool_name not in self.tools:
111
+ logger.error(f"Attempted to execute non-existent tool: {tool_name}")
112
+ return {"error": f"Tool '{tool_name}' not found."}
113
+
114
+ tool = self.tools[tool_name]
115
+ try:
116
+ result = await tool.execute(**kwargs)
117
+ return result
118
+ except Exception as e:
119
+ logger.error(f"Error executing tool '{tool_name}' with params {kwargs}: {e}", exc_info=True)
120
+ 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 .base import BaseTool
7
+ from ..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 .base import BaseTool
8
+ from ..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 .base import BaseTool
7
+ from ..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
+ }
@@ -6,6 +6,7 @@ import time
6
6
 
7
7
  from ..core.config import config_manager
8
8
 
9
+
9
10
  router = APIRouter(tags=["System & Utilities"])
10
11
 
11
12
 
@@ -57,4 +58,6 @@ async def root():
57
58
  "message": "Neuro-Sama Simulator Backend",
58
59
  "version": "2.0",
59
60
  "api_docs": "/docs",
60
- }
61
+ }
62
+
63
+
@@ -287,6 +287,10 @@ async def websocket_admin_endpoint(websocket: WebSocket):
287
287
  initial_context = await agent.get_message_history()
288
288
  await websocket.send_json({"type": "agent_context", "action": "update", "messages": initial_context})
289
289
 
290
+ # Send initial stream status
291
+ status = {"is_running": process_manager.is_running, "backend_status": "running" if process_manager.is_running else "stopped"}
292
+ await websocket.send_json({"type": "stream_status", "payload": status})
293
+
290
294
  # Main loop for receiving messages from the client and pushing log updates
291
295
  while websocket.client_state == WebSocketState.CONNECTED:
292
296
  # Check for incoming messages
@@ -328,7 +332,6 @@ async def handle_admin_ws_message(websocket: WebSocket, data: dict):
328
332
  response["payload"] = {"status": "success", "block_id": block_id}
329
333
  # Broadcast the update to all admins
330
334
  updated_blocks = await agent.get_memory_blocks()
331
- from ..utils.websocket import connection_manager
332
335
  await connection_manager.broadcast_to_admins({"type": "core_memory_updated", "payload": updated_blocks})
333
336
 
334
337
  elif action == "update_core_memory_block":
@@ -336,7 +339,6 @@ async def handle_admin_ws_message(websocket: WebSocket, data: dict):
336
339
  response["payload"] = {"status": "success"}
337
340
  # Broadcast the update to all admins
338
341
  updated_blocks = await agent.get_memory_blocks()
339
- from ..utils.websocket import connection_manager
340
342
  await connection_manager.broadcast_to_admins({"type": "core_memory_updated", "payload": updated_blocks})
341
343
 
342
344
  elif action == "delete_core_memory_block":
@@ -344,7 +346,6 @@ async def handle_admin_ws_message(websocket: WebSocket, data: dict):
344
346
  response["payload"] = {"status": "success"}
345
347
  # Broadcast the update to all admins
346
348
  updated_blocks = await agent.get_memory_blocks()
347
- from ..utils.websocket import connection_manager
348
349
  await connection_manager.broadcast_to_admins({"type": "core_memory_updated", "payload": updated_blocks})
349
350
 
350
351
  # Temp Memory Actions
@@ -356,7 +357,6 @@ async def handle_admin_ws_message(websocket: WebSocket, data: dict):
356
357
  await agent.add_temp_memory(**payload)
357
358
  response["payload"] = {"status": "success"}
358
359
  updated_temp_mem = await agent.get_temp_memory()
359
- from ..utils.websocket import connection_manager
360
360
  await connection_manager.broadcast_to_admins({"type": "temp_memory_updated", "payload": updated_temp_mem})
361
361
 
362
362
  elif action == "clear_temp_memory":
@@ -374,13 +374,35 @@ async def handle_admin_ws_message(websocket: WebSocket, data: dict):
374
374
  await agent.update_init_memory(**payload)
375
375
  response["payload"] = {"status": "success"}
376
376
  updated_init_mem = await agent.get_init_memory()
377
- from ..utils.websocket import connection_manager
378
377
  await connection_manager.broadcast_to_admins({"type": "init_memory_updated", "payload": updated_init_mem})
379
378
 
380
379
  # Tool Actions
381
- elif action == "get_tools":
382
- tools = await agent.get_available_tools()
383
- response["payload"] = {"tools": tools}
380
+ elif action == "get_all_tools":
381
+ agent_instance = getattr(agent, 'agent_instance', agent)
382
+ all_tools = agent_instance.tool_manager.get_all_tool_schemas()
383
+ response["payload"] = {"tools": all_tools}
384
+
385
+ elif action == "get_agent_tool_allocations":
386
+ agent_instance = getattr(agent, 'agent_instance', agent)
387
+ allocations = agent_instance.tool_manager.get_allocations()
388
+ response["payload"] = {"allocations": allocations}
389
+
390
+ elif action == "set_agent_tool_allocations":
391
+ agent_instance = getattr(agent, 'agent_instance', agent)
392
+ allocations_payload = payload.get("allocations", {})
393
+ agent_instance.tool_manager.set_allocations(allocations_payload)
394
+ response["payload"] = {"status": "success"}
395
+ # Broadcast the update to all admins
396
+ updated_allocations = agent_instance.tool_manager.get_allocations()
397
+ await connection_manager.broadcast_to_admins({"type": "agent_tool_allocations_updated", "payload": {"allocations": updated_allocations}})
398
+
399
+ elif action == "reload_tools":
400
+ agent_instance = getattr(agent, 'agent_instance', agent)
401
+ agent_instance.tool_manager.reload_tools()
402
+ response["payload"] = {"status": "success"}
403
+ # Broadcast an event to notify UI to refresh tool lists
404
+ all_tools = agent_instance.tool_manager.get_all_tool_schemas()
405
+ await connection_manager.broadcast_to_admins({"type": "available_tools_updated", "payload": {"tools": all_tools}})
384
406
 
385
407
  elif action == "execute_tool":
386
408
  result = await agent.execute_tool(**payload)
@@ -391,17 +413,26 @@ async def handle_admin_ws_message(websocket: WebSocket, data: dict):
391
413
  if not process_manager.is_running:
392
414
  process_manager.start_live_processes()
393
415
  response["payload"] = {"status": "success", "message": "Stream started"}
416
+ # Broadcast stream status update
417
+ status = {"is_running": process_manager.is_running, "backend_status": "running" if process_manager.is_running else "stopped"}
418
+ await connection_manager.broadcast_to_admins({"type": "stream_status", "payload": status})
394
419
 
395
420
  elif action == "stop_stream":
396
421
  if process_manager.is_running:
397
422
  await process_manager.stop_live_processes()
398
423
  response["payload"] = {"status": "success", "message": "Stream stopped"}
424
+ # Broadcast stream status update
425
+ status = {"is_running": process_manager.is_running, "backend_status": "running" if process_manager.is_running else "stopped"}
426
+ await connection_manager.broadcast_to_admins({"type": "stream_status", "payload": status})
399
427
 
400
428
  elif action == "restart_stream":
401
429
  await process_manager.stop_live_processes()
402
430
  await asyncio.sleep(1)
403
431
  process_manager.start_live_processes()
404
432
  response["payload"] = {"status": "success", "message": "Stream restarted"}
433
+ # Broadcast stream status update
434
+ status = {"is_running": process_manager.is_running, "backend_status": "running" if process_manager.is_running else "stopped"}
435
+ await connection_manager.broadcast_to_admins({"type": "stream_status", "payload": status})
405
436
 
406
437
  elif action == "get_stream_status":
407
438
  status = {"is_running": process_manager.is_running, "backend_status": "running" if process_manager.is_running else "stopped"}
@@ -432,6 +463,24 @@ async def handle_admin_ws_message(websocket: WebSocket, data: dict):
432
463
  context = await agent.get_message_history()
433
464
  response["payload"] = context
434
465
 
466
+ elif action == "get_last_prompt":
467
+ # Check if the agent supports prompt generation introspection
468
+ agent_instance = getattr(agent, 'agent_instance', None) if hasattr(agent, 'agent_instance') else agent
469
+ if not hasattr(agent_instance, 'memory_manager') or not hasattr(agent_instance.memory_manager, 'get_recent_chat') or not hasattr(agent_instance, '_build_neuro_prompt'):
470
+ response["payload"] = {"status": "error", "message": "The active agent does not support prompt generation introspection."}
471
+ else:
472
+ recent_history = await agent_instance.memory_manager.get_recent_chat(entries=10)
473
+ messages_for_prompt = []
474
+ for entry in recent_history:
475
+ if entry.get('role') == 'user':
476
+ parts = entry.get('content', '').split(':', 1)
477
+ if len(parts) == 2:
478
+ messages_for_prompt.append({'username': parts[0].strip(), 'text': parts[1].strip()})
479
+ else:
480
+ messages_for_prompt.append({'username': 'user', 'text': entry.get('content', '')})
481
+ prompt = await agent_instance._build_neuro_prompt(messages_for_prompt)
482
+ response["payload"] = {"prompt": prompt}
483
+
435
484
  elif action == "reset_agent_memory":
436
485
  await agent.reset_memory()
437
486
  response["payload"] = {"status": "success"}
@@ -33,6 +33,7 @@ class ProcessManager:
33
33
  from ..core.application import generate_audience_chat_task, neuro_response_cycle, broadcast_events_task
34
34
  from ..utils.queue import clear_all_queues
35
35
  from ..core.agent_factory import create_agent
36
+ from ..utils.websocket import connection_manager
36
37
 
37
38
  asyncio.create_task(create_agent())
38
39
 
@@ -45,6 +46,9 @@ class ProcessManager:
45
46
  self._tasks.append(asyncio.create_task(neuro_response_cycle()))
46
47
 
47
48
  self._is_running = True
49
+ # Broadcast stream status update
50
+ status = {"is_running": self._is_running, "backend_status": "running" if self._is_running else "stopped"}
51
+ asyncio.create_task(connection_manager.broadcast_to_admins({"type": "stream_status", "payload": status}))
48
52
  logger.info(f"Core processes started: {len(self._tasks)} tasks.")
49
53
 
50
54
  async def stop_live_processes(self):
@@ -66,6 +70,10 @@ class ProcessManager:
66
70
 
67
71
  live_stream_manager.reset_stream_state()
68
72
 
73
+ # Broadcast stream status update
74
+ status = {"is_running": self._is_running, "backend_status": "running" if self._is_running else "stopped"}
75
+ await connection_manager.broadcast_to_admins({"type": "stream_status", "payload": status})
76
+
69
77
  logger.info("All core tasks have been stopped.")
70
78
 
71
79
  # Global singleton instance
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: neuro_simulator
3
- Version: 0.3.1
3
+ Version: 0.3.3
4
4
  Summary: Neuro Simulator Server
5
5
  Author-email: Moha-Master <hongkongreporter@outlook.com>
6
6
  License-Expression: MIT
@@ -2,20 +2,32 @@ neuro_simulator/__init__.py,sha256=-tposzyvg6UckPcfSvtc03UjxBa9oCe_zRvlKf8splk,3
2
2
  neuro_simulator/cli.py,sha256=Nc-udO0jdRVZjlbebKBjDVOIUzd6tJr13ApBGeBEg2k,3967
3
3
  neuro_simulator/agent/__init__.py,sha256=t52CZlyTGWqcGjMs90qvpFpRckY2WSSlO7r_H3K_mSY,32
4
4
  neuro_simulator/agent/base.py,sha256=6v2ZO5UpGCwJEkJ23Oe96Rs510tK4ZOEpZ2DB49IZmM,1262
5
- neuro_simulator/agent/core.py,sha256=2mPVCcNKZDRsyYd6L8RzQYlm8LwzoKoP9FJg0W4qcbc,10702
5
+ neuro_simulator/agent/core.py,sha256=hyO17c-uYhzN8Yix-jraRTTLj8a3JwZuJ7eMnbGJMTE,12218
6
6
  neuro_simulator/agent/factory.py,sha256=e0IBnqJQM7OuKtglrf-pWwqwmg98wh7tOq5LxF2rV-w,1146
7
7
  neuro_simulator/agent/llm.py,sha256=vLz8hp2h2R0JaNfS1RLGYGkri_YoUdlEdNfFVbxeEuI,4261
8
+ neuro_simulator/agent/memory_prompt.txt,sha256=wdpdnbOYhMKgPJnnGlcSejGdt2uItrXzDgz9_8cKnqw,824
9
+ neuro_simulator/agent/neuro_prompt.txt,sha256=WSN5Fa6AwVnJaSFhz98fQTb2EtQ35U6w2qga_C-s1Go,1438
8
10
  neuro_simulator/agent/memory/__init__.py,sha256=YJ7cynQJI6kD7vjyv3rKc-CZqmoYSuGQtRZl_XdGEps,39
9
11
  neuro_simulator/agent/memory/manager.py,sha256=UiUJgjiTV7SHCmb3V5sc4OUa_ksEeXBOyvl-WBQviqs,9266
10
12
  neuro_simulator/agent/tools/__init__.py,sha256=1WZy6PADfi6o1avyy1y-ThWBFAPJ_bBqtkobyYpf5ao,38
11
- neuro_simulator/agent/tools/core.py,sha256=o6Oyis-HFD-g6Z_u3T--tkmr9ylKJvybKqMRSMUwi1Q,5555
13
+ neuro_simulator/agent/tools/add_temp_memory.py,sha256=uFdnyPchtUo3kWrTT5UXGJQA3V1SfIw633qTuTv0c1w,2080
14
+ neuro_simulator/agent/tools/add_to_core_memory_block.py,sha256=YZmOlwtSOQVszt_tY7iDFSLiHa1UKv7OMutpMEcCrkg,2259
15
+ neuro_simulator/agent/tools/base.py,sha256=Xj9ABTAtSFazhQf-qCMMWTCQVLsq48MZtKFVuBsYLQg,1799
16
+ neuro_simulator/agent/tools/create_core_memory_block.py,sha256=k6_zUq2Vf-IA4A6Z5ePiqeuLEil25ycL5FpbIYeKTU4,2719
17
+ neuro_simulator/agent/tools/delete_core_memory_block.py,sha256=-_GIoFIJo6K-jmL3xOedSJtiyoxbfN4noC_cA5pVa6A,1332
18
+ neuro_simulator/agent/tools/get_core_memory_block.py,sha256=XfFY6BZgnczTQqzXxZc8FLLaCRCd-FpGQd9zmj37P3M,1385
19
+ neuro_simulator/agent/tools/get_core_memory_blocks.py,sha256=tivCwm6NIc7legrPAYGW9Txttp6-u0uE4_j2IOSuUmI,1012
20
+ neuro_simulator/agent/tools/manager.py,sha256=n3lKku37vrGjkXJKdVZNXf2bXuVcILKX1Wv-fIl6m7M,5136
21
+ neuro_simulator/agent/tools/remove_from_core_memory_block.py,sha256=xTY-gauo2P6p2-jyvwOupIeJ2vHtk8RoZ-_BLMF8T7w,2325
22
+ neuro_simulator/agent/tools/speak.py,sha256=CkYfpkVM4c0dKXyEuITfH5cLVh2jN992fCO83uoIVvQ,1823
23
+ neuro_simulator/agent/tools/update_core_memory_block.py,sha256=0KwcugykHqXsxVCFHctL-zVBXR5dVjGOdxce2ixYrPg,2225
12
24
  neuro_simulator/api/__init__.py,sha256=5LWyDSayPGdQS8Rv13nmAKLyhPnMVPyTYDdvoMPB4xw,56
13
25
  neuro_simulator/api/stream.py,sha256=hM66flSUygpE-NH9X-ZOV6SiGipBzN1-wjd_wZRpQm4,94
14
- neuro_simulator/api/system.py,sha256=TV_3DzP-VgLnHdue0pKYFFQNOV_V7tr4aMuxL802vQg,1783
26
+ neuro_simulator/api/system.py,sha256=W05Q41BYAFrw-MTnJ5YJrBG2S1SmTcByoex77GfUaFQ,1787
15
27
  neuro_simulator/core/__init__.py,sha256=-ojq25c8XA0CU25b0OxcGjH4IWFEDHR-HXSRSZIuKe8,57
16
28
  neuro_simulator/core/agent_factory.py,sha256=qMFidwT5IrOkyNHwmpO8_fRv20KLbaIBfWF-VTFCLNA,1742
17
29
  neuro_simulator/core/agent_interface.py,sha256=ZXUCtkQUvsBQ5iCb0gTILJaShn5KmSrEgKhd7PK18HE,2794
18
- neuro_simulator/core/application.py,sha256=lhTn5KR7ek8-kuyD1qWtppImUUz8pOExH9KRNW1SQ3M,21115
30
+ neuro_simulator/core/application.py,sha256=L7C0m9jgKfuLCzWiokVs7g1wj3C4mbEb9AFNqo4JF0I,24692
19
31
  neuro_simulator/core/config.py,sha256=brA8kiekV_995mpz04JiGj1swIWbZZuWWLNYtbroMyE,14884
20
32
  neuro_simulator/services/__init__.py,sha256=s3ZrAHg5TpJakadAAGY1h0wDw_xqN4Je4aJwJyRBmk4,61
21
33
  neuro_simulator/services/audience.py,sha256=0phlhsujh_GMXm_UMiyOntY-ZMtoseRa_FroIfc5A6w,5028
@@ -25,12 +37,12 @@ neuro_simulator/services/letta.py,sha256=6jBvOTsLMlRILDv-fvX9fhHMONSYeu-ImJGFcKU
25
37
  neuro_simulator/services/stream.py,sha256=dG7RuNI_ICohPkqKZ-zlBppo54BgWm_KYBs-ezzc73E,5907
26
38
  neuro_simulator/utils/__init__.py,sha256=xSEFzjT827W81mNyQ_DLtr00TgFlttqfFgpz9pSxFXQ,58
27
39
  neuro_simulator/utils/logging.py,sha256=BO-q_cCcoeamsc8eJqq2-L3Z8nhApze_v6LnmD-O8Ww,3411
28
- neuro_simulator/utils/process.py,sha256=9w2JQH59Wy6L8ADrig2QF88iajdykGPZIYJVJe6Al2Y,2603
40
+ neuro_simulator/utils/process.py,sha256=9OYWx8fzaJZqmFUcjQX37AnBhl7YWvrLxDWBa30vqwU,3192
29
41
  neuro_simulator/utils/queue.py,sha256=vSkh-BrgfsGN_gDAx2mfK44ydmMapdVyLsDG-7LIJZQ,1643
30
42
  neuro_simulator/utils/state.py,sha256=DdBqSAYfjOFtJfB1hEGhYPh32r1ZvFuVlN_-29_-luA,664
31
43
  neuro_simulator/utils/websocket.py,sha256=1gtVoH1hafBUfVYmwkVDAbjwETeqyC3sWx706nQzSRo,2085
32
- neuro_simulator-0.3.1.dist-info/METADATA,sha256=lQv9x0KvOZJAA8NwZ8byIaHYTVnSxuHRPyhUyqLC6vw,8643
33
- neuro_simulator-0.3.1.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
34
- neuro_simulator-0.3.1.dist-info/entry_points.txt,sha256=qVd5ypnRRgU8Cw7rWfZ7o0OXyS9P9hgY-cRoN_mgz9g,51
35
- neuro_simulator-0.3.1.dist-info/top_level.txt,sha256=V8awSKpcrFnjJDiJxSfy7jtOrnuE2BgAR9hLmfMDWK8,16
36
- neuro_simulator-0.3.1.dist-info/RECORD,,
44
+ neuro_simulator-0.3.3.dist-info/METADATA,sha256=almvuJaLS7nvw30-XjNLX6UdZ7-qaFdWdoTymHFs3qE,8643
45
+ neuro_simulator-0.3.3.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
46
+ neuro_simulator-0.3.3.dist-info/entry_points.txt,sha256=qVd5ypnRRgU8Cw7rWfZ7o0OXyS9P9hgY-cRoN_mgz9g,51
47
+ neuro_simulator-0.3.3.dist-info/top_level.txt,sha256=V8awSKpcrFnjJDiJxSfy7jtOrnuE2BgAR9hLmfMDWK8,16
48
+ neuro_simulator-0.3.3.dist-info/RECORD,,
@@ -1,102 +0,0 @@
1
- # neuro_simulator/agent/tools/core.py
2
- """
3
- Core tools for the Neuro Simulator Agent
4
- """
5
-
6
- import logging
7
- from pathlib import Path
8
- from typing import Dict, List, Any, Optional
9
-
10
- # Use a logger with a shortened, more readable name
11
- logger = logging.getLogger(__name__.replace("neuro_simulator", "agent", 1))
12
-
13
- class ToolManager:
14
- """Manages all tools available to the agent"""
15
-
16
- def __init__(self, memory_manager):
17
- self.memory_manager = memory_manager
18
- self.tools = {}
19
- self._register_tools()
20
-
21
- def _register_tools(self):
22
- """Register all available tools"""
23
- self.tools["get_core_memory_blocks"] = self._get_core_memory_blocks
24
- self.tools["get_core_memory_block"] = self._get_core_memory_block
25
- self.tools["create_core_memory_block"] = self._create_core_memory_block
26
- self.tools["update_core_memory_block"] = self._update_core_memory_block
27
- self.tools["delete_core_memory_block"] = self._delete_core_memory_block
28
- self.tools["add_to_core_memory_block"] = self._add_to_core_memory_block
29
- self.tools["remove_from_core_memory_block"] = self._remove_from_core_memory_block
30
- self.tools["add_temp_memory"] = self._add_temp_memory
31
- self.tools["speak"] = self._speak
32
-
33
- def get_tool_descriptions(self) -> str:
34
- """Get descriptions of all available tools"""
35
- descriptions = [
36
- "Available tools:",
37
- "1. get_core_memory_blocks() - Get all core memory blocks",
38
- "2. get_core_memory_block(block_id: string) - Get a specific core memory block",
39
- "3. create_core_memory_block(title: string, description: string, content: list) - Create a new core memory block with a generated ID",
40
- "4. update_core_memory_block(block_id: string, title: string (optional), description: string (optional), content: list (optional)) - Update a core memory block",
41
- "5. delete_core_memory_block(block_id: string) - Delete a core memory block",
42
- "6. add_to_core_memory_block(block_id: string, item: string) - Add an item to a core memory block",
43
- "7. remove_from_core_memory_block(block_id: string, index: integer) - Remove an item from a core memory block by index",
44
- "8. add_temp_memory(content: string, role: string) - Add an item to temporary memory",
45
- "9. speak(text: string) - Output text to the user",
46
- "",
47
- "IMPORTANT INSTRUCTIONS:",
48
- "- When you want to speak to the user, ONLY use the speak tool with your response as the text parameter",
49
- "- DO NOT use print() or any other wrapper functions around the speak tool",
50
- "- Example of correct usage: speak(text='Hello, how can I help you today?')",
51
- "- Example of incorrect usage: print(speak(text='Hello, how can I help you today?'))",
52
- "- ONLY return ONE tool call per response",
53
- "- Format your response as plain text with the tool call, nothing else"
54
- ]
55
- return "\n".join(descriptions)
56
-
57
- async def execute_tool(self, tool_name: str, params: Dict[str, Any]) -> Any:
58
- """Execute a tool by name with given parameters"""
59
- if tool_name not in self.tools:
60
- return {"error": f"Tool '{tool_name}' not found"}
61
-
62
- try:
63
- result = await self.tools[tool_name](**params)
64
- return result
65
- except Exception as e:
66
- return {"error": f"Error executing tool '{tool_name}': {str(e)}"}
67
-
68
- # Tool implementations
69
- async def _get_core_memory_blocks(self) -> Dict[str, Any]:
70
- return await self.memory_manager.get_core_memory_blocks()
71
-
72
- async def _get_core_memory_block(self, block_id: str) -> Optional[Dict[str, Any]]:
73
- return await self.memory_manager.get_core_memory_block(block_id)
74
-
75
- async def _create_core_memory_block(self, title: str, description: str, content: List[str]) -> str:
76
- block_id = await self.memory_manager.create_core_memory_block(title, description, content)
77
- return f"Created core memory block '{block_id}' with title '{title}'"
78
-
79
- async def _update_core_memory_block(self, block_id: str, title: str = None, description: str = None, content: List[str] = None) -> str:
80
- await self.memory_manager.update_core_memory_block(block_id, title, description, content)
81
- return f"Updated core memory block '{block_id}'"
82
-
83
- async def _delete_core_memory_block(self, block_id: str) -> str:
84
- await self.memory_manager.delete_core_memory_block(block_id)
85
- return f"Deleted core memory block '{block_id}'"
86
-
87
- async def _add_to_core_memory_block(self, block_id: str, item: str) -> str:
88
- await self.memory_manager.add_to_core_memory_block(block_id, item)
89
- return f"Added item to core memory block '{block_id}'"
90
-
91
- async def _remove_from_core_memory_block(self, block_id: str, index: int) -> str:
92
- await self.memory_manager.remove_from_core_memory_block(block_id, index)
93
- return f"Removed item from core memory block '{block_id}' at index {index}"
94
-
95
- async def _add_temp_memory(self, content: str, role: str = "user") -> str:
96
- await self.memory_manager.add_temp_memory(content, role)
97
- return f"Added item to temp memory with role '{role}'"
98
-
99
- async def _speak(self, text: str) -> str:
100
- """Output text - this is how the agent communicates with users"""
101
- logger.info(f"Agent says: {text}")
102
- return text