neuro-simulator 0.3.2__tar.gz → 0.4.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 (58) hide show
  1. {neuro_simulator-0.3.2 → neuro_simulator-0.4.0}/PKG-INFO +1 -1
  2. neuro_simulator-0.4.0/neuro_simulator/agent/core.py +201 -0
  3. {neuro_simulator-0.3.2 → neuro_simulator-0.4.0}/neuro_simulator/agent/llm.py +1 -1
  4. neuro_simulator-0.4.0/neuro_simulator/agent/memory/manager.py +125 -0
  5. neuro_simulator-0.4.0/neuro_simulator/agent/memory_prompt.txt +14 -0
  6. neuro_simulator-0.4.0/neuro_simulator/agent/neuro_prompt.txt +32 -0
  7. neuro_simulator-0.4.0/neuro_simulator/agent/tools/add_temp_memory.py +61 -0
  8. neuro_simulator-0.4.0/neuro_simulator/agent/tools/add_to_core_memory_block.py +64 -0
  9. neuro_simulator-0.4.0/neuro_simulator/agent/tools/base.py +56 -0
  10. neuro_simulator-0.4.0/neuro_simulator/agent/tools/create_core_memory_block.py +78 -0
  11. neuro_simulator-0.4.0/neuro_simulator/agent/tools/delete_core_memory_block.py +44 -0
  12. neuro_simulator-0.4.0/neuro_simulator/agent/tools/get_core_memory_block.py +44 -0
  13. neuro_simulator-0.4.0/neuro_simulator/agent/tools/get_core_memory_blocks.py +30 -0
  14. neuro_simulator-0.4.0/neuro_simulator/agent/tools/manager.py +143 -0
  15. neuro_simulator-0.4.0/neuro_simulator/agent/tools/remove_from_core_memory_block.py +65 -0
  16. neuro_simulator-0.4.0/neuro_simulator/agent/tools/speak.py +56 -0
  17. neuro_simulator-0.4.0/neuro_simulator/agent/tools/update_core_memory_block.py +65 -0
  18. {neuro_simulator-0.3.2 → neuro_simulator-0.4.0}/neuro_simulator/api/system.py +5 -2
  19. neuro_simulator-0.4.0/neuro_simulator/cli.py +125 -0
  20. {neuro_simulator-0.3.2 → neuro_simulator-0.4.0}/neuro_simulator/core/agent_factory.py +0 -1
  21. {neuro_simulator-0.3.2 → neuro_simulator-0.4.0}/neuro_simulator/core/application.py +72 -43
  22. {neuro_simulator-0.3.2 → neuro_simulator-0.4.0}/neuro_simulator/core/config.py +66 -63
  23. neuro_simulator-0.4.0/neuro_simulator/core/path_manager.py +69 -0
  24. {neuro_simulator-0.3.2 → neuro_simulator-0.4.0}/neuro_simulator/services/audience.py +0 -2
  25. {neuro_simulator-0.3.2 → neuro_simulator-0.4.0}/neuro_simulator/services/audio.py +0 -1
  26. {neuro_simulator-0.3.2 → neuro_simulator-0.4.0}/neuro_simulator/services/builtin.py +10 -25
  27. {neuro_simulator-0.3.2 → neuro_simulator-0.4.0}/neuro_simulator/services/letta.py +19 -1
  28. {neuro_simulator-0.3.2 → neuro_simulator-0.4.0}/neuro_simulator/services/stream.py +24 -21
  29. {neuro_simulator-0.3.2 → neuro_simulator-0.4.0}/neuro_simulator/utils/logging.py +9 -0
  30. {neuro_simulator-0.3.2 → neuro_simulator-0.4.0}/neuro_simulator/utils/queue.py +27 -4
  31. {neuro_simulator-0.3.2 → neuro_simulator-0.4.0}/neuro_simulator/utils/websocket.py +1 -3
  32. {neuro_simulator-0.3.2 → neuro_simulator-0.4.0}/neuro_simulator.egg-info/PKG-INFO +1 -1
  33. {neuro_simulator-0.3.2 → neuro_simulator-0.4.0}/neuro_simulator.egg-info/SOURCES.txt +14 -4
  34. {neuro_simulator-0.3.2 → neuro_simulator-0.4.0}/pyproject.toml +2 -2
  35. neuro_simulator-0.3.2/neuro_simulator/agent/base.py +0 -43
  36. neuro_simulator-0.3.2/neuro_simulator/agent/core.py +0 -234
  37. neuro_simulator-0.3.2/neuro_simulator/agent/factory.py +0 -30
  38. neuro_simulator-0.3.2/neuro_simulator/agent/memory/manager.py +0 -204
  39. neuro_simulator-0.3.2/neuro_simulator/agent/tools/core.py +0 -102
  40. neuro_simulator-0.3.2/neuro_simulator/api/stream.py +0 -1
  41. neuro_simulator-0.3.2/neuro_simulator/cli.py +0 -95
  42. {neuro_simulator-0.3.2 → neuro_simulator-0.4.0}/README.md +0 -0
  43. {neuro_simulator-0.3.2 → neuro_simulator-0.4.0}/neuro_simulator/__init__.py +0 -0
  44. {neuro_simulator-0.3.2 → neuro_simulator-0.4.0}/neuro_simulator/agent/__init__.py +0 -0
  45. {neuro_simulator-0.3.2 → neuro_simulator-0.4.0}/neuro_simulator/agent/memory/__init__.py +0 -0
  46. {neuro_simulator-0.3.2 → neuro_simulator-0.4.0}/neuro_simulator/agent/tools/__init__.py +0 -0
  47. {neuro_simulator-0.3.2 → neuro_simulator-0.4.0}/neuro_simulator/api/__init__.py +0 -0
  48. {neuro_simulator-0.3.2 → neuro_simulator-0.4.0}/neuro_simulator/core/__init__.py +0 -0
  49. {neuro_simulator-0.3.2 → neuro_simulator-0.4.0}/neuro_simulator/core/agent_interface.py +0 -0
  50. {neuro_simulator-0.3.2 → neuro_simulator-0.4.0}/neuro_simulator/services/__init__.py +0 -0
  51. {neuro_simulator-0.3.2 → neuro_simulator-0.4.0}/neuro_simulator/utils/__init__.py +0 -0
  52. {neuro_simulator-0.3.2 → neuro_simulator-0.4.0}/neuro_simulator/utils/process.py +0 -0
  53. {neuro_simulator-0.3.2 → neuro_simulator-0.4.0}/neuro_simulator/utils/state.py +0 -0
  54. {neuro_simulator-0.3.2 → neuro_simulator-0.4.0}/neuro_simulator.egg-info/dependency_links.txt +0 -0
  55. {neuro_simulator-0.3.2 → neuro_simulator-0.4.0}/neuro_simulator.egg-info/entry_points.txt +0 -0
  56. {neuro_simulator-0.3.2 → neuro_simulator-0.4.0}/neuro_simulator.egg-info/requires.txt +0 -0
  57. {neuro_simulator-0.3.2 → neuro_simulator-0.4.0}/neuro_simulator.egg-info/top_level.txt +0 -0
  58. {neuro_simulator-0.3.2 → neuro_simulator-0.4.0}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: neuro_simulator
3
- Version: 0.3.2
3
+ Version: 0.4.0
4
4
  Summary: Neuro Simulator Server
5
5
  Author-email: Moha-Master <hongkongreporter@outlook.com>
6
6
  License-Expression: MIT
@@ -0,0 +1,201 @@
1
+ # neuro_simulator/agent/core.py
2
+ """
3
+ Core module for the Neuro Simulator's built-in agent.
4
+ Implements a dual-LLM "Actor/Thinker" architecture for responsive interaction
5
+ and asynchronous memory consolidation.
6
+ """
7
+
8
+ import asyncio
9
+ import json
10
+ import logging
11
+ import re
12
+ from datetime import datetime
13
+ from pathlib import Path
14
+ from typing import Any, Dict, List
15
+
16
+ from ..core.path_manager import path_manager
17
+ from .llm import LLMClient
18
+ from .memory.manager import MemoryManager
19
+ from .tools.manager import ToolManager
20
+
21
+ logger = logging.getLogger("neuro_agent")
22
+
23
+ class Agent:
24
+ """
25
+ Main Agent class implementing the Actor/Thinker model.
26
+ - The "Neuro" part (Actor) handles real-time interaction.
27
+ - The "Memory" part (Thinker) handles background memory consolidation.
28
+ """
29
+
30
+ def __init__(self):
31
+ if not path_manager:
32
+ raise RuntimeError("PathManager must be initialized before the Agent.")
33
+
34
+ self.memory_manager = MemoryManager()
35
+ self.tool_manager = ToolManager(self.memory_manager)
36
+
37
+ self.neuro_llm = LLMClient()
38
+ self.memory_llm = LLMClient()
39
+
40
+ self._initialized = False
41
+ self.turn_counter = 0
42
+ self.reflection_threshold = 3
43
+
44
+ logger.info("Agent instance created with dual-LLM architecture.")
45
+
46
+ async def initialize(self):
47
+ """Initialize the agent, loading any persistent memory."""
48
+ if not self._initialized:
49
+ logger.info("Initializing agent memory manager...")
50
+ await self.memory_manager.initialize()
51
+ self._initialized = True
52
+ logger.info("Agent initialized successfully.")
53
+
54
+ async def reset_all_memory(self):
55
+ """Reset all agent memory types and clear history logs."""
56
+ await self.memory_manager.reset_temp_memory()
57
+ # Clear history files by overwriting them
58
+ open(path_manager.neuro_history_path, 'w').close()
59
+ open(path_manager.memory_agent_history_path, 'w').close()
60
+ logger.info("All agent memory and history logs have been reset.")
61
+
62
+ async def get_neuro_history(self, limit: int = 20) -> List[Dict[str, Any]]:
63
+ """Reads the last N lines from the Neuro agent's history log."""
64
+ return await self._read_history_log(path_manager.neuro_history_path, limit)
65
+
66
+ async def _append_to_history_log(self, file_path: Path, data: Dict[str, Any]):
67
+ """Appends a new entry to a JSON Lines history file."""
68
+ data['timestamp'] = datetime.now().isoformat()
69
+ with open(file_path, 'a', encoding='utf-8') as f:
70
+ f.write(json.dumps(data, ensure_ascii=False) + '\n')
71
+
72
+ async def _read_history_log(self, file_path: Path, limit: int) -> List[Dict[str, Any]]:
73
+ """Reads the last N lines from a JSON Lines history file."""
74
+ if not file_path.exists():
75
+ return []
76
+ try:
77
+ with open(file_path, 'r', encoding='utf-8') as f:
78
+ lines = f.readlines()
79
+ # Get the last N lines and parse them
80
+ return [json.loads(line) for line in lines[-limit:]]
81
+ except (json.JSONDecodeError, IndexError) as e:
82
+ logger.error(f"Could not read or parse history from {file_path}: {e}")
83
+ return []
84
+
85
+ def _format_tool_schemas_for_prompt(self, schemas: List[Dict[str, Any]]) -> str:
86
+ """Formats a list of tool schemas into a string for the LLM prompt."""
87
+ if not schemas:
88
+ return "No tools available."
89
+ lines = ["Available tools:"]
90
+ for i, schema in enumerate(schemas):
91
+ params_str_parts = []
92
+ for param in schema.get("parameters", []):
93
+ p_name = param.get('name')
94
+ p_type = param.get('type')
95
+ p_req = 'required' if param.get('required') else 'optional'
96
+ params_str_parts.append(f"{p_name}: {p_type} ({p_req})")
97
+ params_str = ", ".join(params_str_parts)
98
+ lines.append(f"{i+1}. {schema.get('name')}({params_str}) - {schema.get('description')}")
99
+ return "\n".join(lines)
100
+
101
+ async def _build_neuro_prompt(self, messages: List[Dict[str, str]]) -> str:
102
+ """Builds the prompt for the Neuro (Actor) LLM."""
103
+ prompt_template = "" # Define a default empty prompt
104
+ if path_manager.neuro_prompt_path.exists():
105
+ with open(path_manager.neuro_prompt_path, 'r', encoding='utf-8') as f:
106
+ prompt_template = f.read()
107
+ else:
108
+ logger.warning(f"Neuro prompt template not found at {path_manager.neuro_prompt_path}")
109
+
110
+ tool_schemas = self.tool_manager.get_tool_schemas_for_agent('neuro_agent')
111
+ tool_descriptions = self._format_tool_schemas_for_prompt(tool_schemas)
112
+
113
+ init_memory_text = "\n".join(f"{key}: {value}" for key, value in self.memory_manager.init_memory.items())
114
+
115
+ core_memory_blocks = await self.memory_manager.get_core_memory_blocks()
116
+ core_memory_parts = [f"\nBlock: {b.get('title', '')} ({b_id})\nDescription: {b.get('description', '')}\nContent:\n" + "\n".join([f" - {item}" for item in b.get("content", [])]) for b_id, b in core_memory_blocks.items()]
117
+ core_memory_text = "\n".join(core_memory_parts) if core_memory_parts else "Not set."
118
+
119
+ temp_memory_text = "\n".join([f"[{item.get('role', 'system')}] {item.get('content', '')}" for item in self.memory_manager.temp_memory]) if self.memory_manager.temp_memory else "Empty."
120
+
121
+ recent_history = await self._read_history_log(path_manager.neuro_history_path, limit=10)
122
+ recent_history_text = "\n".join([f"{msg.get('role', 'unknown')}: {msg.get('content', '')}" for msg in recent_history])
123
+ user_messages_text = "\n".join([f"{msg['username']}: {msg['text']}" for msg in messages])
124
+
125
+ return prompt_template.format(
126
+ tool_descriptions=tool_descriptions,
127
+ init_memory=init_memory_text,
128
+ core_memory=core_memory_text,
129
+ temp_memory=temp_memory_text,
130
+ recent_history=recent_history_text,
131
+ user_messages=user_messages_text
132
+ )
133
+
134
+ async def _build_memory_prompt(self, conversation_history: List[Dict[str, str]]) -> str:
135
+ """Builds the prompt for the Memory (Thinker) LLM."""
136
+ prompt_template = "" # Define a default empty prompt
137
+ if path_manager.memory_agent_prompt_path.exists():
138
+ with open(path_manager.memory_agent_prompt_path, 'r', encoding='utf-8') as f:
139
+ prompt_template = f.read()
140
+ else:
141
+ logger.warning(f"Memory prompt template not found at {path_manager.memory_agent_prompt_path}")
142
+
143
+ tool_schemas = self.tool_manager.get_tool_schemas_for_agent('memory_agent')
144
+ tool_descriptions = self._format_tool_schemas_for_prompt(tool_schemas)
145
+ history_text = "\n".join([f"{msg.get('role', 'unknown')}: {msg.get('content', '')}" for msg in conversation_history])
146
+
147
+ return prompt_template.format(
148
+ tool_descriptions=tool_descriptions,
149
+ conversation_history=history_text
150
+ )
151
+
152
+ def _parse_tool_calls(self, response_text: str) -> List[Dict[str, Any]]:
153
+ try:
154
+ match = re.search(r'''```json\s*([\s\S]*?)\s*```|(\[[\s\S]*\])''', response_text)
155
+ if not match:
156
+ logger.warning(f"No valid JSON tool call block found in response: {response_text}")
157
+ return []
158
+ json_str = match.group(1) or match.group(2)
159
+ return json.loads(json_str)
160
+ except Exception as e:
161
+ logger.error(f"Failed to parse tool calls from LLM response: {e}")
162
+ return []
163
+
164
+ async def _execute_tool_calls(self, tool_calls: List[Dict[str, Any]]) -> Dict[str, Any]:
165
+ execution_results = []
166
+ final_response = ""
167
+ for tool_call in tool_calls:
168
+ tool_name = tool_call.get("name")
169
+ params = tool_call.get("params", {})
170
+ if not tool_name:
171
+ continue
172
+ logger.info(f"Executing tool: {tool_name} with params: {params}")
173
+ try:
174
+ result = await self.tool_manager.execute_tool(tool_name, **params)
175
+ execution_results.append({"name": tool_name, "params": params, "result": result})
176
+ if tool_name == "speak" and result.get("status") == "success":
177
+ final_response = result.get("spoken_text", "")
178
+ except Exception as e:
179
+ logger.error(f"Error executing tool {tool_name}: {e}")
180
+ execution_results.append({"name": tool_name, "params": params, "error": str(e)})
181
+ return {"tool_executions": execution_results, "final_response": final_response}
182
+
183
+ async def process_and_respond(self, messages: List[Dict[str, str]]) -> Dict[str, Any]:
184
+ await self.initialize()
185
+ logger.info(f"Processing {len(messages)} messages in Actor flow.")
186
+
187
+ for msg in messages:
188
+ await self._append_to_history_log(path_manager.neuro_history_path, {'role': 'user', 'content': f"{msg['username']}: {msg['text']}"})
189
+
190
+ prompt = await self._build_neuro_prompt(messages)
191
+ response_text = await self.neuro_llm.generate(prompt)
192
+
193
+ tool_calls = self._parse_tool_calls(response_text)
194
+ processing_result = await self._execute_tool_calls(tool_calls)
195
+
196
+ if final_response := processing_result.get("final_response", ""):
197
+ await self._append_to_history_log(path_manager.neuro_history_path, {'role': 'assistant', 'content': final_response})
198
+
199
+ return processing_result
200
+
201
+
@@ -6,7 +6,7 @@ LLM client for the Neuro Simulator's built-in agent.
6
6
  import asyncio
7
7
  import logging
8
8
  from pathlib import Path
9
- from typing import Optional
9
+ from typing import Any, Dict
10
10
 
11
11
  from google import genai
12
12
  from google.genai import types
@@ -0,0 +1,125 @@
1
+ # neuro_simulator/agent/memory/manager.py
2
+ """
3
+ Manages the agent's shared memory state (init, core, temp).
4
+ """
5
+
6
+ import json
7
+ import logging
8
+ import random
9
+ import string
10
+ from typing import Any, Dict, List, Optional
11
+ from datetime import datetime
12
+
13
+ from ...core.path_manager import path_manager
14
+
15
+ logger = logging.getLogger(__name__.replace("neuro_simulator", "agent", 1))
16
+
17
+ def generate_id(length=6) -> str:
18
+ """Generate a random ID string."""
19
+ return ''.join(random.choices(string.ascii_letters + string.digits, k=length))
20
+
21
+ class MemoryManager:
22
+ """Manages the three types of shared memory for the agent."""
23
+
24
+ def __init__(self):
25
+ """Initializes the MemoryManager using paths from the global path_manager."""
26
+ if not path_manager:
27
+ raise RuntimeError("PathManager not initialized before MemoryManager.")
28
+
29
+ self.init_memory_file = path_manager.init_memory_path
30
+ self.core_memory_file = path_manager.core_memory_path
31
+ self.temp_memory_file = path_manager.temp_memory_path
32
+
33
+ self.init_memory: Dict[str, Any] = {}
34
+ self.core_memory: Dict[str, Any] = {}
35
+ self.temp_memory: List[Dict[str, Any]] = []
36
+
37
+ async def initialize(self):
38
+ """Load all memory types from files, creating defaults if they don't exist."""
39
+ # Load or create init memory
40
+ if self.init_memory_file.exists():
41
+ with open(self.init_memory_file, 'r', encoding='utf-8') as f:
42
+ self.init_memory = json.load(f)
43
+ else:
44
+ self.init_memory = {
45
+ "name": "Neuro-Sama", "role": "AI VTuber",
46
+ "personality": "Friendly, curious, and entertaining",
47
+ "capabilities": ["Chat with viewers", "Answer questions"]
48
+ }
49
+ await self._save_init_memory()
50
+
51
+ # Load or create core memory
52
+ if self.core_memory_file.exists():
53
+ with open(self.core_memory_file, 'r', encoding='utf-8') as f:
54
+ self.core_memory = json.load(f)
55
+ else:
56
+ self.core_memory = {"blocks": {}}
57
+ await self._save_core_memory()
58
+
59
+ # Load or create temp memory
60
+ if self.temp_memory_file.exists():
61
+ with open(self.temp_memory_file, 'r', encoding='utf-8') as f:
62
+ self.temp_memory = json.load(f)
63
+ else:
64
+ self.temp_memory = []
65
+ await self._save_temp_memory()
66
+
67
+ logger.info("MemoryManager initialized and memory files loaded/created.")
68
+
69
+ async def _save_init_memory(self):
70
+ with open(self.init_memory_file, 'w', encoding='utf-8') as f:
71
+ json.dump(self.init_memory, f, ensure_ascii=False, indent=2)
72
+
73
+ async def update_init_memory(self, new_memory: Dict[str, Any]):
74
+ self.init_memory.update(new_memory)
75
+ await self._save_init_memory()
76
+
77
+ async def _save_core_memory(self):
78
+ with open(self.core_memory_file, 'w', encoding='utf-8') as f:
79
+ json.dump(self.core_memory, f, ensure_ascii=False, indent=2)
80
+
81
+ async def _save_temp_memory(self):
82
+ with open(self.temp_memory_file, 'w', encoding='utf-8') as f:
83
+ json.dump(self.temp_memory, f, ensure_ascii=False, indent=2)
84
+
85
+ async def reset_temp_memory(self):
86
+ """Reset temp memory to a default empty state."""
87
+ self.temp_memory = []
88
+ await self._save_temp_memory()
89
+ logger.info("Agent temp memory has been reset.")
90
+
91
+ async def add_temp_memory(self, content: str, role: str = "system"):
92
+ self.temp_memory.append({"id": generate_id(), "content": content, "role": role, "timestamp": datetime.now().isoformat()})
93
+ if len(self.temp_memory) > 20:
94
+ self.temp_memory = self.temp_memory[-20:]
95
+ await self._save_temp_memory()
96
+
97
+ async def get_core_memory_blocks(self) -> Dict[str, Any]:
98
+ return self.core_memory.get("blocks", {})
99
+
100
+ async def get_core_memory_block(self, block_id: str) -> Optional[Dict[str, Any]]:
101
+ return self.core_memory.get("blocks", {}).get(block_id)
102
+
103
+ async def create_core_memory_block(self, title: str, description: str, content: List[str]) -> str:
104
+ block_id = generate_id()
105
+ if "blocks" not in self.core_memory:
106
+ self.core_memory["blocks"] = {}
107
+ self.core_memory["blocks"][block_id] = {
108
+ "id": block_id, "title": title, "description": description, "content": content or []
109
+ }
110
+ await self._save_core_memory()
111
+ return block_id
112
+
113
+ async def update_core_memory_block(self, block_id: str, title: Optional[str] = None, description: Optional[str] = None, content: Optional[List[str]] = None):
114
+ block = self.core_memory.get("blocks", {}).get(block_id)
115
+ if not block:
116
+ raise ValueError(f"Block '{block_id}' not found")
117
+ if title is not None: block["title"] = title
118
+ if description is not None: block["description"] = description
119
+ if content is not None: block["content"] = content
120
+ await self._save_core_memory()
121
+
122
+ async def delete_core_memory_block(self, block_id: str):
123
+ if "blocks" in self.core_memory and block_id in self.core_memory["blocks"]:
124
+ del self.core_memory["blocks"][block_id]
125
+ await self._save_core_memory()
@@ -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 Any, Dict, List
5
+
6
+ from neuro_simulator.agent.tools.base import BaseTool
7
+ from neuro_simulator.agent.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 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
+ }