neuro-simulator 0.2.2__py3-none-any.whl → 0.3.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -17,6 +17,8 @@ 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
24
  from ..api.agent import router as agent_router
@@ -130,16 +132,40 @@ async def neuro_response_cycle():
130
132
 
131
133
  while True:
132
134
  try:
133
- if is_first_response:
134
- add_to_neuro_input_queue({"username": "System", "text": config_manager.settings.neuro_behavior.initial_greeting})
135
- is_first_response = False
136
- elif is_neuro_input_queue_empty():
137
- await asyncio.sleep(1)
138
- continue
135
+ selected_chats = []
136
+ # Superchat logic
137
+ if app_state.superchat_queue and (time.time() - app_state.last_superchat_time > 10):
138
+ sc = app_state.superchat_queue.popleft()
139
+ app_state.last_superchat_time = time.time()
140
+ await connection_manager.broadcast({"type": "processing_superchat", "data": sc})
141
+
142
+ # Agent-specific payload generation for superchats
143
+ if isinstance(agent, LettaAgent):
144
+ selected_chats = [
145
+ {"role": "system", "content": "=== RANDOM 10 MSG IN CHATROOM ===\nNO MSG FETCH DUE TO UNPROCESSED HIGHLIGHTED MESSAGE"},
146
+ {"role": "system", "content": f"=== HIGHLIGHTED MESSAGE ===\n{sc['username']}: {sc['text']}"}
147
+ ]
148
+ else: # For BuiltinAgent and any other future agents
149
+ selected_chats = [{'username': sc['username'], 'text': sc['text']}]
150
+
151
+ # Clear the regular input queue to prevent immediate follow-up with normal chats
152
+ get_all_neuro_input_chats()
153
+ else:
154
+ if is_first_response:
155
+ add_to_neuro_input_queue({"username": "System", "text": config_manager.settings.neuro_behavior.initial_greeting})
156
+ is_first_response = False
157
+ elif is_neuro_input_queue_empty():
158
+ await asyncio.sleep(1)
159
+ continue
160
+
161
+ current_queue_snapshot = get_all_neuro_input_chats()
162
+ if not current_queue_snapshot:
163
+ continue
164
+ sample_size = min(config_manager.settings.neuro_behavior.input_chat_sample_size, len(current_queue_snapshot))
165
+ selected_chats = random.sample(current_queue_snapshot, sample_size)
139
166
 
140
- current_queue_snapshot = get_all_neuro_input_chats()
141
- sample_size = min(config_manager.settings.neuro_behavior.input_chat_sample_size, len(current_queue_snapshot))
142
- selected_chats = random.sample(current_queue_snapshot, sample_size)
167
+ if not selected_chats:
168
+ continue
143
169
 
144
170
  response_result = await asyncio.wait_for(agent.process_messages(selected_chats), timeout=20.0)
145
171
 
@@ -208,7 +234,7 @@ async def startup_event():
208
234
  logger.info("FastAPI application has started.")
209
235
 
210
236
  @app.on_event("shutdown")
211
- async def shutdown_event():
237
+ def shutdown_event():
212
238
  """Actions to perform on application shutdown."""
213
239
  if process_manager.is_running:
214
240
  process_manager.stop_live_processes()
@@ -232,11 +258,20 @@ async def websocket_stream_endpoint(websocket: WebSocket):
232
258
  raw_data = await websocket.receive_text()
233
259
  data = json.loads(raw_data)
234
260
  if data.get("type") == "user_message":
235
- user_message = {"username": data.get("username", "User"), "text": data.get("message", "").strip()}
261
+ user_message = {"username": data.get("username", "User"), "text": data.get("text", "").strip()}
236
262
  if user_message["text"]:
237
263
  add_to_audience_buffer(user_message)
238
264
  add_to_neuro_input_queue(user_message)
239
265
  await connection_manager.broadcast({"type": "chat_message", **user_message, "is_user_message": True})
266
+ elif data.get("type") == "superchat":
267
+ sc_message = {
268
+ "username": data.get("username", "User"),
269
+ "text": data.get("text", "").strip(),
270
+ "sc_type": data.get("sc_type", "bits")
271
+ }
272
+ if sc_message["text"]:
273
+ app_state.superchat_queue.append(sc_message)
274
+
240
275
  except WebSocketDisconnect:
241
276
  pass
242
277
  finally:
@@ -275,4 +310,4 @@ def run_server(host: str = None, port: int = None):
275
310
  host=server_host,
276
311
  port=server_port,
277
312
  reload=False
278
- )
313
+ )
@@ -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 messages:
78
- injected_chat_lines = [f"{chat['username']}: {chat['text']}" for chat in messages]
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) +
@@ -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
- logger.info("All chat queues have been cleared.")
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."""
@@ -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()
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: neuro_simulator
3
- Version: 0.2.2
3
+ Version: 0.3.0
4
4
  Summary: Neuro Simulator Server
5
5
  Author-email: Moha-Master <hongkongreporter@outlook.com>
6
6
  License-Expression: MIT
@@ -16,22 +16,22 @@ neuro_simulator/api/system.py,sha256=hXznMcThuFhwopYWgpzrRxwtBuFnF_b_vinkOaE5XOs
16
16
  neuro_simulator/core/__init__.py,sha256=-ojq25c8XA0CU25b0OxcGjH4IWFEDHR-HXSRSZIuKe8,57
17
17
  neuro_simulator/core/agent_factory.py,sha256=qMFidwT5IrOkyNHwmpO8_fRv20KLbaIBfWF-VTFCLNA,1742
18
18
  neuro_simulator/core/agent_interface.py,sha256=r58Opcgs7SWVovYTjMWuxF8AiTy9QfRz276_YGmSei0,2791
19
- neuro_simulator/core/application.py,sha256=zN8KOR_nwsYsR1ZcyZU1GlW3-ijFLbjw5lz1g8DYVIU,11578
19
+ neuro_simulator/core/application.py,sha256=fM9XgYBvx-mNB173rYDezbKdLJRTJ5Mr0nOtxNlSfSc,13365
20
20
  neuro_simulator/core/config.py,sha256=brA8kiekV_995mpz04JiGj1swIWbZZuWWLNYtbroMyE,14884
21
21
  neuro_simulator/services/__init__.py,sha256=s3ZrAHg5TpJakadAAGY1h0wDw_xqN4Je4aJwJyRBmk4,61
22
22
  neuro_simulator/services/audience.py,sha256=0phlhsujh_GMXm_UMiyOntY-ZMtoseRa_FroIfc5A6w,5028
23
23
  neuro_simulator/services/audio.py,sha256=ZscQA25wVYpm9FUl4Hya7tKH8t0TjR3th9-OEZ0G7xk,2934
24
24
  neuro_simulator/services/builtin.py,sha256=7ePxEom5HIK6wGto_H5M8JOnAjyiHqUuE381DEGgzjE,3821
25
- neuro_simulator/services/letta.py,sha256=iXyzFyPVty3SjYCHOJAF1QqhUFmxTQbqk8lYJOwB5pc,9415
25
+ neuro_simulator/services/letta.py,sha256=6jBvOTsLMlRILDv-fvX9fhHMONSYeu-ImJGFcKU00kc,11067
26
26
  neuro_simulator/services/stream.py,sha256=dG7RuNI_ICohPkqKZ-zlBppo54BgWm_KYBs-ezzc73E,5907
27
27
  neuro_simulator/utils/__init__.py,sha256=xSEFzjT827W81mNyQ_DLtr00TgFlttqfFgpz9pSxFXQ,58
28
28
  neuro_simulator/utils/logging.py,sha256=BO-q_cCcoeamsc8eJqq2-L3Z8nhApze_v6LnmD-O8Ww,3411
29
29
  neuro_simulator/utils/process.py,sha256=9w2JQH59Wy6L8ADrig2QF88iajdykGPZIYJVJe6Al2Y,2603
30
- neuro_simulator/utils/queue.py,sha256=7SSnUnrqxHkqSeYVjp1S5kpr8qlo4IVdU2NsGwFQAVw,1546
31
- neuro_simulator/utils/state.py,sha256=E1ilecgMTOAhiw_kNvLG0akkhdZKhzKLrA3oB4NNVTA,538
30
+ neuro_simulator/utils/queue.py,sha256=vSkh-BrgfsGN_gDAx2mfK44ydmMapdVyLsDG-7LIJZQ,1643
31
+ neuro_simulator/utils/state.py,sha256=DdBqSAYfjOFtJfB1hEGhYPh32r1ZvFuVlN_-29_-luA,664
32
32
  neuro_simulator/utils/websocket.py,sha256=yOdFvJzbNhcUn5EAuyS55G_R8q-snas5OvkOtS8g19E,2292
33
- neuro_simulator-0.2.2.dist-info/METADATA,sha256=lBjb1G0VhfbDCQhRWQOxJzlt9Ut07XL6zApn6X6G_jo,8676
34
- neuro_simulator-0.2.2.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
35
- neuro_simulator-0.2.2.dist-info/entry_points.txt,sha256=qVd5ypnRRgU8Cw7rWfZ7o0OXyS9P9hgY-cRoN_mgz9g,51
36
- neuro_simulator-0.2.2.dist-info/top_level.txt,sha256=V8awSKpcrFnjJDiJxSfy7jtOrnuE2BgAR9hLmfMDWK8,16
37
- neuro_simulator-0.2.2.dist-info/RECORD,,
33
+ neuro_simulator-0.3.0.dist-info/METADATA,sha256=PELlUyyTi3i1u1_EOLIntPwd7-FAXAoy6uiSs2QA2uE,8676
34
+ neuro_simulator-0.3.0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
35
+ neuro_simulator-0.3.0.dist-info/entry_points.txt,sha256=qVd5ypnRRgU8Cw7rWfZ7o0OXyS9P9hgY-cRoN_mgz9g,51
36
+ neuro_simulator-0.3.0.dist-info/top_level.txt,sha256=V8awSKpcrFnjJDiJxSfy7jtOrnuE2BgAR9hLmfMDWK8,16
37
+ neuro_simulator-0.3.0.dist-info/RECORD,,