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.
- neuro_simulator/core/application.py +47 -12
- neuro_simulator/services/letta.py +32 -2
- neuro_simulator/utils/queue.py +3 -1
- neuro_simulator/utils/state.py +5 -1
- {neuro_simulator-0.2.2.dist-info → neuro_simulator-0.3.0.dist-info}/METADATA +1 -1
- {neuro_simulator-0.2.2.dist-info → neuro_simulator-0.3.0.dist-info}/RECORD +9 -9
- {neuro_simulator-0.2.2.dist-info → neuro_simulator-0.3.0.dist-info}/WHEEL +0 -0
- {neuro_simulator-0.2.2.dist-info → neuro_simulator-0.3.0.dist-info}/entry_points.txt +0 -0
- {neuro_simulator-0.2.2.dist-info → neuro_simulator-0.3.0.dist-info}/top_level.txt +0 -0
@@ -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
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
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
|
-
|
141
|
-
|
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
|
-
|
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("
|
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
|
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()
|
@@ -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=
|
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=
|
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=
|
31
|
-
neuro_simulator/utils/state.py,sha256=
|
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.
|
34
|
-
neuro_simulator-0.
|
35
|
-
neuro_simulator-0.
|
36
|
-
neuro_simulator-0.
|
37
|
-
neuro_simulator-0.
|
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,,
|
File without changes
|
File without changes
|
File without changes
|