neuro-simulator 0.2.2__py3-none-any.whl → 0.3.1__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 +163 -130
- neuro_simulator/agent/memory/manager.py +26 -26
- neuro_simulator/api/stream.py +1 -55
- neuro_simulator/api/system.py +30 -60
- neuro_simulator/cli.py +3 -2
- neuro_simulator/core/agent_interface.py +1 -1
- neuro_simulator/core/application.py +210 -17
- neuro_simulator/services/builtin.py +34 -4
- neuro_simulator/services/letta.py +32 -2
- neuro_simulator/utils/queue.py +3 -1
- neuro_simulator/utils/state.py +5 -1
- neuro_simulator/utils/websocket.py +11 -14
- {neuro_simulator-0.2.2.dist-info → neuro_simulator-0.3.1.dist-info}/METADATA +3 -4
- {neuro_simulator-0.2.2.dist-info → neuro_simulator-0.3.1.dist-info}/RECORD +17 -18
- neuro_simulator/api/agent.py +0 -163
- {neuro_simulator-0.2.2.dist-info → neuro_simulator-0.3.1.dist-info}/WHEEL +0 -0
- {neuro_simulator-0.2.2.dist-info → neuro_simulator-0.3.1.dist-info}/entry_points.txt +0 -0
- {neuro_simulator-0.2.2.dist-info → neuro_simulator-0.3.1.dist-info}/top_level.txt +0 -0
@@ -17,10 +17,10 @@ from starlette.websockets import WebSocketState
|
|
17
17
|
# --- Core Imports ---
|
18
18
|
from .config import config_manager, AppSettings
|
19
19
|
from ..core.agent_factory import create_agent
|
20
|
+
from ..services.letta import LettaAgent
|
21
|
+
from ..services.builtin import BuiltinAgentWrapper
|
20
22
|
|
21
23
|
# --- API Routers ---
|
22
|
-
from ..api.agent import router as agent_router
|
23
|
-
from ..api.stream import router as stream_router
|
24
24
|
from ..api.system import router as system_router
|
25
25
|
|
26
26
|
# --- Services and Utilities ---
|
@@ -58,8 +58,6 @@ app.add_middleware(
|
|
58
58
|
expose_headers=["X-API-Token"],
|
59
59
|
)
|
60
60
|
|
61
|
-
app.include_router(agent_router)
|
62
|
-
app.include_router(stream_router)
|
63
61
|
app.include_router(system_router)
|
64
62
|
|
65
63
|
# --- Background Task Definitions ---
|
@@ -130,18 +128,42 @@ async def neuro_response_cycle():
|
|
130
128
|
|
131
129
|
while True:
|
132
130
|
try:
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
131
|
+
selected_chats = []
|
132
|
+
# Superchat logic
|
133
|
+
if app_state.superchat_queue and (time.time() - app_state.last_superchat_time > 10):
|
134
|
+
sc = app_state.superchat_queue.popleft()
|
135
|
+
app_state.last_superchat_time = time.time()
|
136
|
+
await connection_manager.broadcast({"type": "processing_superchat", "data": sc})
|
137
|
+
|
138
|
+
# Agent-specific payload generation for superchats
|
139
|
+
if isinstance(agent, LettaAgent):
|
140
|
+
selected_chats = [
|
141
|
+
{"role": "system", "content": "=== RANDOM 10 MSG IN CHATROOM ===\nNO MSG FETCH DUE TO UNPROCESSED HIGHLIGHTED MESSAGE"},
|
142
|
+
{"role": "system", "content": f"=== HIGHLIGHTED MESSAGE ===\n{sc['username']}: {sc['text']}"}
|
143
|
+
]
|
144
|
+
else: # For BuiltinAgent and any other future agents
|
145
|
+
selected_chats = [{'username': sc['username'], 'text': sc['text']}]
|
146
|
+
|
147
|
+
# Clear the regular input queue to prevent immediate follow-up with normal chats
|
148
|
+
get_all_neuro_input_chats()
|
149
|
+
else:
|
150
|
+
if is_first_response:
|
151
|
+
add_to_neuro_input_queue({"username": "System", "text": config_manager.settings.neuro_behavior.initial_greeting})
|
152
|
+
is_first_response = False
|
153
|
+
elif is_neuro_input_queue_empty():
|
154
|
+
await asyncio.sleep(1)
|
155
|
+
continue
|
156
|
+
|
157
|
+
current_queue_snapshot = get_all_neuro_input_chats()
|
158
|
+
if not current_queue_snapshot:
|
159
|
+
continue
|
160
|
+
sample_size = min(config_manager.settings.neuro_behavior.input_chat_sample_size, len(current_queue_snapshot))
|
161
|
+
selected_chats = random.sample(current_queue_snapshot, sample_size)
|
139
162
|
|
140
|
-
|
141
|
-
|
142
|
-
selected_chats = random.sample(current_queue_snapshot, sample_size)
|
163
|
+
if not selected_chats:
|
164
|
+
continue
|
143
165
|
|
144
|
-
response_result = await asyncio.wait_for(agent.
|
166
|
+
response_result = await asyncio.wait_for(agent.process_and_respond(selected_chats), timeout=20.0)
|
145
167
|
|
146
168
|
response_text = response_result.get("final_response", "").strip()
|
147
169
|
if not response_text:
|
@@ -208,7 +230,7 @@ async def startup_event():
|
|
208
230
|
logger.info("FastAPI application has started.")
|
209
231
|
|
210
232
|
@app.on_event("shutdown")
|
211
|
-
|
233
|
+
def shutdown_event():
|
212
234
|
"""Actions to perform on application shutdown."""
|
213
235
|
if process_manager.is_running:
|
214
236
|
process_manager.stop_live_processes()
|
@@ -232,11 +254,20 @@ async def websocket_stream_endpoint(websocket: WebSocket):
|
|
232
254
|
raw_data = await websocket.receive_text()
|
233
255
|
data = json.loads(raw_data)
|
234
256
|
if data.get("type") == "user_message":
|
235
|
-
user_message = {"username": data.get("username", "User"), "text": data.get("
|
257
|
+
user_message = {"username": data.get("username", "User"), "text": data.get("text", "").strip()}
|
236
258
|
if user_message["text"]:
|
237
259
|
add_to_audience_buffer(user_message)
|
238
260
|
add_to_neuro_input_queue(user_message)
|
239
261
|
await connection_manager.broadcast({"type": "chat_message", **user_message, "is_user_message": True})
|
262
|
+
elif data.get("type") == "superchat":
|
263
|
+
sc_message = {
|
264
|
+
"username": data.get("username", "User"),
|
265
|
+
"text": data.get("text", "").strip(),
|
266
|
+
"sc_type": data.get("sc_type", "bits")
|
267
|
+
}
|
268
|
+
if sc_message["text"]:
|
269
|
+
app_state.superchat_queue.append(sc_message)
|
270
|
+
|
240
271
|
except WebSocketDisconnect:
|
241
272
|
pass
|
242
273
|
finally:
|
@@ -245,7 +276,10 @@ async def websocket_stream_endpoint(websocket: WebSocket):
|
|
245
276
|
@app.websocket("/ws/admin")
|
246
277
|
async def websocket_admin_endpoint(websocket: WebSocket):
|
247
278
|
await websocket.accept()
|
279
|
+
# Add the new admin client to a dedicated list
|
280
|
+
connection_manager.admin_connections.append(websocket)
|
248
281
|
try:
|
282
|
+
# Send initial state
|
249
283
|
for log_entry in list(server_log_queue): await websocket.send_json({"type": "server_log", "data": log_entry})
|
250
284
|
for log_entry in list(agent_log_queue): await websocket.send_json({"type": "agent_log", "data": log_entry})
|
251
285
|
|
@@ -253,15 +287,174 @@ async def websocket_admin_endpoint(websocket: WebSocket):
|
|
253
287
|
initial_context = await agent.get_message_history()
|
254
288
|
await websocket.send_json({"type": "agent_context", "action": "update", "messages": initial_context})
|
255
289
|
|
290
|
+
# Main loop for receiving messages from the client and pushing log updates
|
256
291
|
while websocket.client_state == WebSocketState.CONNECTED:
|
292
|
+
# Check for incoming messages
|
293
|
+
try:
|
294
|
+
raw_data = await asyncio.wait_for(websocket.receive_text(), timeout=0.01)
|
295
|
+
data = json.loads(raw_data)
|
296
|
+
await handle_admin_ws_message(websocket, data)
|
297
|
+
except asyncio.TimeoutError:
|
298
|
+
pass # No message received, continue to push logs
|
299
|
+
|
300
|
+
# Push log updates
|
257
301
|
if server_log_queue: await websocket.send_json({"type": "server_log", "data": server_log_queue.popleft()})
|
258
302
|
if agent_log_queue: await websocket.send_json({"type": "agent_log", "data": agent_log_queue.popleft()})
|
259
303
|
await asyncio.sleep(0.1)
|
304
|
+
|
260
305
|
except WebSocketDisconnect:
|
261
306
|
pass
|
262
307
|
finally:
|
308
|
+
connection_manager.admin_connections.remove(websocket)
|
263
309
|
logger.info("Admin WebSocket client disconnected.")
|
264
310
|
|
311
|
+
async def handle_admin_ws_message(websocket: WebSocket, data: dict):
|
312
|
+
"""Handles incoming messages from the admin WebSocket."""
|
313
|
+
action = data.get("action")
|
314
|
+
payload = data.get("payload", {})
|
315
|
+
request_id = data.get("request_id")
|
316
|
+
|
317
|
+
agent = await create_agent()
|
318
|
+
response = {"type": "response", "request_id": request_id, "payload": {}}
|
319
|
+
|
320
|
+
try:
|
321
|
+
# Core Memory Actions
|
322
|
+
if action == "get_core_memory_blocks":
|
323
|
+
blocks = await agent.get_memory_blocks()
|
324
|
+
response["payload"] = blocks
|
325
|
+
|
326
|
+
elif action == "create_core_memory_block":
|
327
|
+
block_id = await agent.create_memory_block(**payload)
|
328
|
+
response["payload"] = {"status": "success", "block_id": block_id}
|
329
|
+
# Broadcast the update to all admins
|
330
|
+
updated_blocks = await agent.get_memory_blocks()
|
331
|
+
from ..utils.websocket import connection_manager
|
332
|
+
await connection_manager.broadcast_to_admins({"type": "core_memory_updated", "payload": updated_blocks})
|
333
|
+
|
334
|
+
elif action == "update_core_memory_block":
|
335
|
+
await agent.update_memory_block(**payload)
|
336
|
+
response["payload"] = {"status": "success"}
|
337
|
+
# Broadcast the update to all admins
|
338
|
+
updated_blocks = await agent.get_memory_blocks()
|
339
|
+
from ..utils.websocket import connection_manager
|
340
|
+
await connection_manager.broadcast_to_admins({"type": "core_memory_updated", "payload": updated_blocks})
|
341
|
+
|
342
|
+
elif action == "delete_core_memory_block":
|
343
|
+
await agent.delete_memory_block(**payload)
|
344
|
+
response["payload"] = {"status": "success"}
|
345
|
+
# Broadcast the update to all admins
|
346
|
+
updated_blocks = await agent.get_memory_blocks()
|
347
|
+
from ..utils.websocket import connection_manager
|
348
|
+
await connection_manager.broadcast_to_admins({"type": "core_memory_updated", "payload": updated_blocks})
|
349
|
+
|
350
|
+
# Temp Memory Actions
|
351
|
+
elif action == "get_temp_memory":
|
352
|
+
temp_mem = await agent.get_temp_memory()
|
353
|
+
response["payload"] = temp_mem
|
354
|
+
|
355
|
+
elif action == "add_temp_memory":
|
356
|
+
await agent.add_temp_memory(**payload)
|
357
|
+
response["payload"] = {"status": "success"}
|
358
|
+
updated_temp_mem = await agent.get_temp_memory()
|
359
|
+
from ..utils.websocket import connection_manager
|
360
|
+
await connection_manager.broadcast_to_admins({"type": "temp_memory_updated", "payload": updated_temp_mem})
|
361
|
+
|
362
|
+
elif action == "clear_temp_memory":
|
363
|
+
await agent.clear_temp_memory()
|
364
|
+
response["payload"] = {"status": "success"}
|
365
|
+
updated_temp_mem = await agent.get_temp_memory()
|
366
|
+
await connection_manager.broadcast_to_admins({"type": "temp_memory_updated", "payload": updated_temp_mem})
|
367
|
+
|
368
|
+
# Init Memory Actions
|
369
|
+
elif action == "get_init_memory":
|
370
|
+
init_mem = await agent.get_init_memory()
|
371
|
+
response["payload"] = init_mem
|
372
|
+
|
373
|
+
elif action == "update_init_memory":
|
374
|
+
await agent.update_init_memory(**payload)
|
375
|
+
response["payload"] = {"status": "success"}
|
376
|
+
updated_init_mem = await agent.get_init_memory()
|
377
|
+
from ..utils.websocket import connection_manager
|
378
|
+
await connection_manager.broadcast_to_admins({"type": "init_memory_updated", "payload": updated_init_mem})
|
379
|
+
|
380
|
+
# Tool Actions
|
381
|
+
elif action == "get_tools":
|
382
|
+
tools = await agent.get_available_tools()
|
383
|
+
response["payload"] = {"tools": tools}
|
384
|
+
|
385
|
+
elif action == "execute_tool":
|
386
|
+
result = await agent.execute_tool(**payload)
|
387
|
+
response["payload"] = {"result": result}
|
388
|
+
|
389
|
+
# Stream Control Actions
|
390
|
+
elif action == "start_stream":
|
391
|
+
if not process_manager.is_running:
|
392
|
+
process_manager.start_live_processes()
|
393
|
+
response["payload"] = {"status": "success", "message": "Stream started"}
|
394
|
+
|
395
|
+
elif action == "stop_stream":
|
396
|
+
if process_manager.is_running:
|
397
|
+
await process_manager.stop_live_processes()
|
398
|
+
response["payload"] = {"status": "success", "message": "Stream stopped"}
|
399
|
+
|
400
|
+
elif action == "restart_stream":
|
401
|
+
await process_manager.stop_live_processes()
|
402
|
+
await asyncio.sleep(1)
|
403
|
+
process_manager.start_live_processes()
|
404
|
+
response["payload"] = {"status": "success", "message": "Stream restarted"}
|
405
|
+
|
406
|
+
elif action == "get_stream_status":
|
407
|
+
status = {"is_running": process_manager.is_running, "backend_status": "running" if process_manager.is_running else "stopped"}
|
408
|
+
response["payload"] = status
|
409
|
+
|
410
|
+
# Config Management Actions
|
411
|
+
elif action == "get_configs":
|
412
|
+
from ..api.system import filter_config_for_frontend
|
413
|
+
configs = filter_config_for_frontend(config_manager.settings)
|
414
|
+
response["payload"] = configs
|
415
|
+
|
416
|
+
elif action == "update_configs":
|
417
|
+
from ..api.system import filter_config_for_frontend
|
418
|
+
await config_manager.update_settings(payload)
|
419
|
+
updated_configs = filter_config_for_frontend(config_manager.settings)
|
420
|
+
response["payload"] = updated_configs
|
421
|
+
await connection_manager.broadcast_to_admins({"type": "config_updated", "payload": updated_configs})
|
422
|
+
|
423
|
+
elif action == "reload_configs":
|
424
|
+
await config_manager.update_settings({})
|
425
|
+
response["payload"] = {"status": "success", "message": "Configuration reloaded"}
|
426
|
+
from ..api.system import filter_config_for_frontend
|
427
|
+
updated_configs = filter_config_for_frontend(config_manager.settings)
|
428
|
+
await connection_manager.broadcast_to_admins({"type": "config_updated", "payload": updated_configs})
|
429
|
+
|
430
|
+
# Other Agent Actions
|
431
|
+
elif action == "get_agent_context":
|
432
|
+
context = await agent.get_message_history()
|
433
|
+
response["payload"] = context
|
434
|
+
|
435
|
+
elif action == "reset_agent_memory":
|
436
|
+
await agent.reset_memory()
|
437
|
+
response["payload"] = {"status": "success"}
|
438
|
+
# Broadcast updates for all memory types
|
439
|
+
await connection_manager.broadcast_to_admins({"type": "core_memory_updated", "payload": await agent.get_memory_blocks()})
|
440
|
+
await connection_manager.broadcast_to_admins({"type": "temp_memory_updated", "payload": await agent.get_temp_memory()})
|
441
|
+
await connection_manager.broadcast_to_admins({"type": "init_memory_updated", "payload": await agent.get_init_memory()})
|
442
|
+
await connection_manager.broadcast_to_admins({"type": "agent_context", "action": "update", "messages": await agent.get_message_history()})
|
443
|
+
|
444
|
+
else:
|
445
|
+
response["payload"] = {"status": "error", "message": f"Unknown action: {action}"}
|
446
|
+
|
447
|
+
# Send the direct response to the requesting client
|
448
|
+
if request_id:
|
449
|
+
await websocket.send_json(response)
|
450
|
+
|
451
|
+
except Exception as e:
|
452
|
+
logger.error(f"Error handling admin WS message (action: {action}): {e}", exc_info=True)
|
453
|
+
if request_id:
|
454
|
+
response["payload"] = {"status": "error", "message": str(e)}
|
455
|
+
await websocket.send_json(response)
|
456
|
+
|
457
|
+
|
265
458
|
# --- Server Entrypoint ---
|
266
459
|
|
267
460
|
def run_server(host: str = None, port: int = None):
|
@@ -275,4 +468,4 @@ def run_server(host: str = None, port: int = None):
|
|
275
468
|
host=server_host,
|
276
469
|
port=server_port,
|
277
470
|
reload=False
|
278
|
-
)
|
471
|
+
)
|
@@ -37,8 +37,8 @@ class BuiltinAgentWrapper(BaseAgent):
|
|
37
37
|
async def reset_memory(self):
|
38
38
|
await self.agent_instance.reset_all_memory()
|
39
39
|
|
40
|
-
async def
|
41
|
-
return await self.agent_instance.
|
40
|
+
async def process_and_respond(self, messages: List[Dict[str, str]]) -> Dict[str, Any]:
|
41
|
+
return await self.agent_instance.process_and_respond(messages)
|
42
42
|
|
43
43
|
# Memory Block Management
|
44
44
|
async def get_memory_blocks(self) -> List[Dict[str, Any]]:
|
@@ -50,13 +50,25 @@ class BuiltinAgentWrapper(BaseAgent):
|
|
50
50
|
|
51
51
|
async def create_memory_block(self, title: str, description: str, content: List[str]) -> Dict[str, str]:
|
52
52
|
block_id = await self.agent_instance.memory_manager.create_core_memory_block(title, description, content)
|
53
|
+
# Broadcast core_memory_updated event
|
54
|
+
updated_blocks = await self.get_memory_blocks()
|
55
|
+
from ..utils.websocket import connection_manager
|
56
|
+
await connection_manager.broadcast_to_admins({"type": "core_memory_updated", "payload": updated_blocks})
|
53
57
|
return {"block_id": block_id}
|
54
58
|
|
55
59
|
async def update_memory_block(self, block_id: str, title: Optional[str], description: Optional[str], content: Optional[List[str]]):
|
56
60
|
await self.agent_instance.memory_manager.update_core_memory_block(block_id, title, description, content)
|
61
|
+
# Broadcast core_memory_updated event
|
62
|
+
updated_blocks = await self.get_memory_blocks()
|
63
|
+
from ..utils.websocket import connection_manager
|
64
|
+
await connection_manager.broadcast_to_admins({"type": "core_memory_updated", "payload": updated_blocks})
|
57
65
|
|
58
66
|
async def delete_memory_block(self, block_id: str):
|
59
67
|
await self.agent_instance.memory_manager.delete_core_memory_block(block_id)
|
68
|
+
# Broadcast core_memory_updated event
|
69
|
+
updated_blocks = await self.get_memory_blocks()
|
70
|
+
from ..utils.websocket import connection_manager
|
71
|
+
await connection_manager.broadcast_to_admins({"type": "core_memory_updated", "payload": updated_blocks})
|
60
72
|
|
61
73
|
# Init Memory Management
|
62
74
|
async def get_init_memory(self) -> Dict[str, Any]:
|
@@ -64,6 +76,10 @@ class BuiltinAgentWrapper(BaseAgent):
|
|
64
76
|
|
65
77
|
async def update_init_memory(self, memory: Dict[str, Any]):
|
66
78
|
await self.agent_instance.memory_manager.update_init_memory(memory)
|
79
|
+
# Broadcast init_memory_updated event
|
80
|
+
updated_init_mem = await self.get_init_memory()
|
81
|
+
from ..utils.websocket import connection_manager
|
82
|
+
await connection_manager.broadcast_to_admins({"type": "init_memory_updated", "payload": updated_init_mem})
|
67
83
|
|
68
84
|
# Temp Memory Management
|
69
85
|
async def get_temp_memory(self) -> List[Dict[str, Any]]:
|
@@ -71,17 +87,31 @@ class BuiltinAgentWrapper(BaseAgent):
|
|
71
87
|
|
72
88
|
async def add_temp_memory(self, content: str, role: str):
|
73
89
|
await self.agent_instance.memory_manager.add_temp_memory(content, role)
|
90
|
+
# Broadcast temp_memory_updated event
|
91
|
+
updated_temp_mem = await self.get_temp_memory()
|
92
|
+
from ..utils.websocket import connection_manager
|
93
|
+
await connection_manager.broadcast_to_admins({"type": "temp_memory_updated", "payload": updated_temp_mem})
|
74
94
|
|
75
95
|
async def clear_temp_memory(self):
|
76
96
|
await self.agent_instance.memory_manager.reset_temp_memory()
|
97
|
+
# Broadcast temp_memory_updated event
|
98
|
+
updated_temp_mem = await self.get_temp_memory()
|
99
|
+
from ..utils.websocket import connection_manager
|
100
|
+
await connection_manager.broadcast_to_admins({"type": "temp_memory_updated", "payload": updated_temp_mem})
|
77
101
|
|
78
102
|
# Tool Management
|
79
103
|
async def get_available_tools(self) -> str:
|
80
104
|
return self.agent_instance.tool_manager.get_tool_descriptions()
|
81
105
|
|
82
106
|
async def execute_tool(self, tool_name: str, params: Dict[str, Any]) -> Any:
|
83
|
-
|
107
|
+
result = await self.agent_instance.execute_tool(tool_name, params)
|
108
|
+
# If the tool was add_temp_memory, broadcast temp_memory_updated event
|
109
|
+
if tool_name == "add_temp_memory":
|
110
|
+
updated_temp_mem = await self.get_temp_memory()
|
111
|
+
from ..utils.websocket import connection_manager
|
112
|
+
await connection_manager.broadcast_to_admins({"type": "temp_memory_updated", "payload": updated_temp_mem})
|
113
|
+
return result
|
84
114
|
|
85
115
|
# Context/Message History
|
86
116
|
async def get_message_history(self, limit: int = 20) -> List[Dict[str, Any]]:
|
87
|
-
return await self.agent_instance.memory_manager.
|
117
|
+
return await self.agent_instance.memory_manager.get_recent_chat(limit)
|
@@ -74,8 +74,38 @@ class LettaAgent(BaseAgent):
|
|
74
74
|
logger.warning(f"Failed to reset Letta Agent message history: {e}")
|
75
75
|
|
76
76
|
async def process_messages(self, messages: List[Dict[str, str]]) -> Dict[str, Any]:
|
77
|
-
if
|
78
|
-
|
77
|
+
# Check if this is a superchat message based on the specific structure
|
78
|
+
# from neuro_response_cycle
|
79
|
+
is_superchat = (
|
80
|
+
len(messages) == 2 and
|
81
|
+
messages[0].get("role") == "system" and
|
82
|
+
messages[1].get("role") == "system" and
|
83
|
+
"HIGHLIGHTED MESSAGE" in messages[1].get("content", "")
|
84
|
+
)
|
85
|
+
|
86
|
+
if is_superchat:
|
87
|
+
try:
|
88
|
+
# Extract username and text from the superchat message
|
89
|
+
# Format is "=== HIGHLIGHTED MESSAGE ===\n{username}: {text}"
|
90
|
+
content_lines = messages[1]["content"].split('\n', 1)
|
91
|
+
user_and_text = content_lines[1]
|
92
|
+
parts = user_and_text.split(':', 1)
|
93
|
+
sc_username = parts[0].strip()
|
94
|
+
sc_text = parts[1].strip()
|
95
|
+
injected_chat_lines = [f"{sc_username}: {sc_text}"]
|
96
|
+
injected_chat_text = (
|
97
|
+
"Here is a highlighted message from my Twitch chat:\n---\n" +
|
98
|
+
"\n".join(injected_chat_lines) +
|
99
|
+
"\n---\nNow, as the streamer Neuro-Sama, please continue the conversation naturally."
|
100
|
+
)
|
101
|
+
logger.info(f"Processing highlighted message for Letta: {injected_chat_lines[0]}")
|
102
|
+
except (IndexError, AttributeError) as e:
|
103
|
+
logger.error(f"Failed to parse superchat for Letta, falling back. Error: {e}")
|
104
|
+
# Fallback to default empty prompt if parsing fails
|
105
|
+
injected_chat_text = "My chat is quiet right now. As Neuro-Sama, what should I say to engage them?"
|
106
|
+
|
107
|
+
elif messages:
|
108
|
+
injected_chat_lines = [f"{chat['username']}: {chat['text']}" for chat in messages if 'username' in chat and 'text' in chat]
|
79
109
|
injected_chat_text = (
|
80
110
|
"Here are some recent messages from my Twitch chat:\n---\n" +
|
81
111
|
"\n".join(injected_chat_lines) +
|
neuro_simulator/utils/queue.py
CHANGED
@@ -6,6 +6,7 @@ from collections import deque
|
|
6
6
|
from pathlib import Path
|
7
7
|
|
8
8
|
from ..core.config import config_manager
|
9
|
+
from ..utils.state import app_state
|
9
10
|
|
10
11
|
logger = logging.getLogger(__name__.replace("neuro_simulator", "server", 1))
|
11
12
|
|
@@ -17,7 +18,8 @@ def clear_all_queues():
|
|
17
18
|
"""Clears all chat queues."""
|
18
19
|
audience_chat_buffer.clear()
|
19
20
|
neuro_input_queue.clear()
|
20
|
-
|
21
|
+
app_state.superchat_queue.clear()
|
22
|
+
logger.info("All chat queues (including superchats) have been cleared.")
|
21
23
|
|
22
24
|
def add_to_audience_buffer(chat_item: dict):
|
23
25
|
"""Adds a chat item to the audience buffer."""
|
neuro_simulator/utils/state.py
CHANGED
@@ -2,6 +2,8 @@
|
|
2
2
|
"""Manages the shared state of the application using a singleton class."""
|
3
3
|
|
4
4
|
import asyncio
|
5
|
+
import time
|
6
|
+
from collections import deque
|
5
7
|
|
6
8
|
class AppState:
|
7
9
|
"""A singleton class to hold all shared application state."""
|
@@ -9,6 +11,8 @@ class AppState:
|
|
9
11
|
self.live_phase_started_event = asyncio.Event()
|
10
12
|
self.neuro_last_speech_lock = asyncio.Lock()
|
11
13
|
self.neuro_last_speech: str = "Neuro-Sama has just started the stream and hasn't said anything yet."
|
14
|
+
self.superchat_queue = deque()
|
15
|
+
self.last_superchat_time: float = 0.0
|
12
16
|
|
13
17
|
# Create a single, globally accessible instance of the AppState.
|
14
|
-
app_state = AppState()
|
18
|
+
app_state = AppState()
|
@@ -12,7 +12,8 @@ logger = logging.getLogger(__name__.replace("neuro_simulator", "server", 1))
|
|
12
12
|
class WebSocketManager:
|
13
13
|
"""Manages all active WebSocket connections and provides broadcasting capabilities."""
|
14
14
|
def __init__(self):
|
15
|
-
self.active_connections:
|
15
|
+
self.active_connections: list[WebSocket] = []
|
16
|
+
self.admin_connections: list[WebSocket] = []
|
16
17
|
logger.info("WebSocketManager initialized.")
|
17
18
|
|
18
19
|
async def connect(self, websocket: WebSocket):
|
@@ -37,19 +38,15 @@ class WebSocketManager:
|
|
37
38
|
self.disconnect(websocket)
|
38
39
|
|
39
40
|
async def broadcast(self, message: dict):
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
disconnected_sockets.append(connection)
|
50
|
-
|
51
|
-
for disconnected_socket in disconnected_sockets:
|
52
|
-
self.disconnect(disconnected_socket)
|
41
|
+
for connection in self.active_connections:
|
42
|
+
await connection.send_json(message)
|
43
|
+
|
44
|
+
async def broadcast_to_admins(self, message: dict):
|
45
|
+
for connection in self.admin_connections:
|
46
|
+
try:
|
47
|
+
await connection.send_json(message)
|
48
|
+
except Exception as e:
|
49
|
+
logger.error(f"Failed to send message to admin connection: {e}")
|
53
50
|
|
54
51
|
# Global singleton instance
|
55
52
|
connection_manager = WebSocketManager()
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.4
|
2
2
|
Name: neuro_simulator
|
3
|
-
Version: 0.
|
3
|
+
Version: 0.3.1
|
4
4
|
Summary: Neuro Simulator Server
|
5
5
|
Author-email: Moha-Master <hongkongreporter@outlook.com>
|
6
6
|
License-Expression: MIT
|
@@ -69,7 +69,7 @@ neuro_simulator/
|
|
69
69
|
│ ├── memory/ # 记忆管理模块
|
70
70
|
│ │ ├── __init__.py
|
71
71
|
│ │ ├── manager.py # 记忆管理器
|
72
|
-
│ │ ├──
|
72
|
+
│ │ ├── chat_history.json # 上下文记忆文件
|
73
73
|
│ │ ├── core_memory.json # 核心记忆文件
|
74
74
|
│ │ ├── init_memory.json # 初始化记忆文件
|
75
75
|
│ │ └── temp_memory.json # 临时记忆文件
|
@@ -109,7 +109,7 @@ working_dir_example/ # 工作目录结构,请将这个目录重命名和
|
|
109
109
|
├── config.yaml.example # 自动生成的配置文件模板,必须手动重命名和填写
|
110
110
|
└── agent/ # Agent相关文件夹
|
111
111
|
└── memory/ # Agent记忆文件夹
|
112
|
-
├──
|
112
|
+
├── chat_history.json # 上下文记忆文件
|
113
113
|
├── core_memory.json # 核心记忆文件
|
114
114
|
├── init_memory.json # 初始化记忆文件
|
115
115
|
└── temp_memory.json # 临时记忆文件
|
@@ -191,7 +191,6 @@ neuro -D /path/to/your/config -H 0.0.0.0 -P 8080
|
|
191
191
|
- `/api/configs/*` - 配置管理接口(获取/更新/重载配置)
|
192
192
|
- `api_keys` `server` 等敏感配置项无法从接口获取和修改
|
193
193
|
- `/api/logs` - 日志获取接口
|
194
|
-
- `/api/tts/synthesize` - TTS 合成接口
|
195
194
|
- `/api/system/health` - 健康检查接口
|
196
195
|
- `/ws/stream` - 客户端使用的直播接口
|
197
196
|
- `/ws/admin` - 日志和内建 Agent的 Context 流接口
|
@@ -1,37 +1,36 @@
|
|
1
1
|
neuro_simulator/__init__.py,sha256=-tposzyvg6UckPcfSvtc03UjxBa9oCe_zRvlKf8splk,31
|
2
|
-
neuro_simulator/cli.py,sha256=
|
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=
|
5
|
+
neuro_simulator/agent/core.py,sha256=2mPVCcNKZDRsyYd6L8RzQYlm8LwzoKoP9FJg0W4qcbc,10702
|
6
6
|
neuro_simulator/agent/factory.py,sha256=e0IBnqJQM7OuKtglrf-pWwqwmg98wh7tOq5LxF2rV-w,1146
|
7
7
|
neuro_simulator/agent/llm.py,sha256=vLz8hp2h2R0JaNfS1RLGYGkri_YoUdlEdNfFVbxeEuI,4261
|
8
8
|
neuro_simulator/agent/memory/__init__.py,sha256=YJ7cynQJI6kD7vjyv3rKc-CZqmoYSuGQtRZl_XdGEps,39
|
9
|
-
neuro_simulator/agent/memory/manager.py,sha256=
|
9
|
+
neuro_simulator/agent/memory/manager.py,sha256=UiUJgjiTV7SHCmb3V5sc4OUa_ksEeXBOyvl-WBQviqs,9266
|
10
10
|
neuro_simulator/agent/tools/__init__.py,sha256=1WZy6PADfi6o1avyy1y-ThWBFAPJ_bBqtkobyYpf5ao,38
|
11
11
|
neuro_simulator/agent/tools/core.py,sha256=o6Oyis-HFD-g6Z_u3T--tkmr9ylKJvybKqMRSMUwi1Q,5555
|
12
12
|
neuro_simulator/api/__init__.py,sha256=5LWyDSayPGdQS8Rv13nmAKLyhPnMVPyTYDdvoMPB4xw,56
|
13
|
-
neuro_simulator/api/
|
14
|
-
neuro_simulator/api/
|
15
|
-
neuro_simulator/api/system.py,sha256=hXznMcThuFhwopYWgpzrRxwtBuFnF_b_vinkOaE5XOs,3712
|
13
|
+
neuro_simulator/api/stream.py,sha256=hM66flSUygpE-NH9X-ZOV6SiGipBzN1-wjd_wZRpQm4,94
|
14
|
+
neuro_simulator/api/system.py,sha256=TV_3DzP-VgLnHdue0pKYFFQNOV_V7tr4aMuxL802vQg,1783
|
16
15
|
neuro_simulator/core/__init__.py,sha256=-ojq25c8XA0CU25b0OxcGjH4IWFEDHR-HXSRSZIuKe8,57
|
17
16
|
neuro_simulator/core/agent_factory.py,sha256=qMFidwT5IrOkyNHwmpO8_fRv20KLbaIBfWF-VTFCLNA,1742
|
18
|
-
neuro_simulator/core/agent_interface.py,sha256=
|
19
|
-
neuro_simulator/core/application.py,sha256=
|
17
|
+
neuro_simulator/core/agent_interface.py,sha256=ZXUCtkQUvsBQ5iCb0gTILJaShn5KmSrEgKhd7PK18HE,2794
|
18
|
+
neuro_simulator/core/application.py,sha256=lhTn5KR7ek8-kuyD1qWtppImUUz8pOExH9KRNW1SQ3M,21115
|
20
19
|
neuro_simulator/core/config.py,sha256=brA8kiekV_995mpz04JiGj1swIWbZZuWWLNYtbroMyE,14884
|
21
20
|
neuro_simulator/services/__init__.py,sha256=s3ZrAHg5TpJakadAAGY1h0wDw_xqN4Je4aJwJyRBmk4,61
|
22
21
|
neuro_simulator/services/audience.py,sha256=0phlhsujh_GMXm_UMiyOntY-ZMtoseRa_FroIfc5A6w,5028
|
23
22
|
neuro_simulator/services/audio.py,sha256=ZscQA25wVYpm9FUl4Hya7tKH8t0TjR3th9-OEZ0G7xk,2934
|
24
|
-
neuro_simulator/services/builtin.py,sha256=
|
25
|
-
neuro_simulator/services/letta.py,sha256=
|
23
|
+
neuro_simulator/services/builtin.py,sha256=nn3sJFPy09JxQkw35icdyGU9hzLTXXazAJkNpdcz6Zs,5848
|
24
|
+
neuro_simulator/services/letta.py,sha256=6jBvOTsLMlRILDv-fvX9fhHMONSYeu-ImJGFcKU00kc,11067
|
26
25
|
neuro_simulator/services/stream.py,sha256=dG7RuNI_ICohPkqKZ-zlBppo54BgWm_KYBs-ezzc73E,5907
|
27
26
|
neuro_simulator/utils/__init__.py,sha256=xSEFzjT827W81mNyQ_DLtr00TgFlttqfFgpz9pSxFXQ,58
|
28
27
|
neuro_simulator/utils/logging.py,sha256=BO-q_cCcoeamsc8eJqq2-L3Z8nhApze_v6LnmD-O8Ww,3411
|
29
28
|
neuro_simulator/utils/process.py,sha256=9w2JQH59Wy6L8ADrig2QF88iajdykGPZIYJVJe6Al2Y,2603
|
30
|
-
neuro_simulator/utils/queue.py,sha256=
|
31
|
-
neuro_simulator/utils/state.py,sha256=
|
32
|
-
neuro_simulator/utils/websocket.py,sha256=
|
33
|
-
neuro_simulator-0.
|
34
|
-
neuro_simulator-0.
|
35
|
-
neuro_simulator-0.
|
36
|
-
neuro_simulator-0.
|
37
|
-
neuro_simulator-0.
|
29
|
+
neuro_simulator/utils/queue.py,sha256=vSkh-BrgfsGN_gDAx2mfK44ydmMapdVyLsDG-7LIJZQ,1643
|
30
|
+
neuro_simulator/utils/state.py,sha256=DdBqSAYfjOFtJfB1hEGhYPh32r1ZvFuVlN_-29_-luA,664
|
31
|
+
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,,
|