neuro-simulator 0.0.4__py3-none-any.whl → 0.1.2__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/__init__.py +10 -1
- neuro_simulator/agent/__init__.py +8 -0
- neuro_simulator/agent/api.py +737 -0
- neuro_simulator/agent/core.py +471 -0
- neuro_simulator/agent/llm.py +104 -0
- neuro_simulator/agent/memory/__init__.py +4 -0
- neuro_simulator/agent/memory/manager.py +370 -0
- neuro_simulator/agent/memory.py +137 -0
- neuro_simulator/agent/tools/__init__.py +4 -0
- neuro_simulator/agent/tools/core.py +112 -0
- neuro_simulator/agent/tools.py +69 -0
- neuro_simulator/builtin_agent.py +83 -0
- neuro_simulator/cli.py +45 -0
- neuro_simulator/config.py +217 -79
- neuro_simulator/config.yaml.example +16 -2
- neuro_simulator/letta.py +71 -45
- neuro_simulator/log_handler.py +30 -16
- neuro_simulator/main.py +167 -30
- neuro_simulator/process_manager.py +5 -2
- neuro_simulator/stream_manager.py +6 -0
- {neuro_simulator-0.0.4.dist-info → neuro_simulator-0.1.2.dist-info}/METADATA +1 -1
- neuro_simulator-0.1.2.dist-info/RECORD +31 -0
- neuro_simulator-0.0.4.dist-info/RECORD +0 -20
- {neuro_simulator-0.0.4.dist-info → neuro_simulator-0.1.2.dist-info}/WHEEL +0 -0
- {neuro_simulator-0.0.4.dist-info → neuro_simulator-0.1.2.dist-info}/entry_points.txt +0 -0
- {neuro_simulator-0.0.4.dist-info → neuro_simulator-0.1.2.dist-info}/top_level.txt +0 -0
neuro_simulator/letta.py
CHANGED
@@ -3,60 +3,85 @@ from letta_client import Letta, MessageCreate, TextContent, LlmConfig, Assistant
|
|
3
3
|
from fastapi import HTTPException, status
|
4
4
|
from .config import config_manager
|
5
5
|
import asyncio
|
6
|
+
from typing import Union
|
6
7
|
|
7
|
-
#
|
8
|
-
letta_client: Letta
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
if
|
35
|
-
llm_model_info =
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
8
|
+
# Global variables
|
9
|
+
letta_client: Union[Letta, None] = None
|
10
|
+
|
11
|
+
def initialize_letta_client():
|
12
|
+
"""Initializes the Letta client if not already initialized."""
|
13
|
+
global letta_client
|
14
|
+
if letta_client:
|
15
|
+
return
|
16
|
+
|
17
|
+
try:
|
18
|
+
if not config_manager.settings.api_keys.letta_token:
|
19
|
+
raise ValueError("LETTA_API_TOKEN is not set. Cannot initialize Letta client.")
|
20
|
+
|
21
|
+
client_args = {'token': config_manager.settings.api_keys.letta_token}
|
22
|
+
if config_manager.settings.api_keys.letta_base_url:
|
23
|
+
client_args['base_url'] = config_manager.settings.api_keys.letta_base_url
|
24
|
+
print(f"Letta client is being initialized for self-hosted URL: {config_manager.settings.api_keys.letta_base_url}")
|
25
|
+
else:
|
26
|
+
print("Letta client is being initialized for Letta Cloud.")
|
27
|
+
|
28
|
+
letta_client = Letta(**client_args)
|
29
|
+
|
30
|
+
if config_manager.settings.api_keys.neuro_agent_id:
|
31
|
+
try:
|
32
|
+
agent_data = letta_client.agents.retrieve(agent_id=config_manager.settings.api_keys.neuro_agent_id)
|
33
|
+
print(f"成功获取 Letta Agent 详情,ID: {agent_data.id}")
|
34
|
+
llm_model_info = "N/A"
|
35
|
+
if hasattr(agent_data, 'model') and agent_data.model:
|
36
|
+
llm_model_info = agent_data.model
|
37
|
+
elif agent_data.llm_config:
|
38
|
+
if isinstance(agent_data.llm_config, LlmConfig):
|
39
|
+
llm_config_dict = agent_data.llm_config.model_dump() if hasattr(agent_data.llm_config, 'model_dump') else agent_data.llm_config.__dict__
|
40
|
+
llm_model_info = llm_config_dict.get('model_name') or llm_config_dict.get('name') or llm_config_dict.get('model')
|
41
|
+
if not llm_model_info:
|
42
|
+
llm_model_info = str(agent_data.llm_config)
|
43
|
+
print(f" -> Agent 名称: {agent_data.name}")
|
44
|
+
print(f" -> LLM 模型: {llm_model_info}")
|
45
|
+
except Exception as e:
|
46
|
+
error_msg = f"错误: 无法获取 Neuro Letta Agent (ID: {config_manager.settings.api_keys.neuro_agent_id})。请确保 ID 正确,且服务可访问。详情: {e}"
|
47
|
+
print(error_msg)
|
48
|
+
raise HTTPException(status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail=error_msg)
|
49
|
+
except Exception as e:
|
50
|
+
print(f"初始化 Letta 客户端失败: {e}")
|
51
|
+
letta_client = None
|
46
52
|
|
47
53
|
def get_letta_client():
|
48
54
|
if letta_client is None: raise ValueError("Letta client is not initialized.")
|
49
55
|
return letta_client
|
50
56
|
|
57
|
+
async def initialize_agent():
|
58
|
+
"""Initialize the appropriate agent based on configuration"""
|
59
|
+
agent_type = config_manager.settings.agent_type
|
60
|
+
|
61
|
+
if agent_type == "letta":
|
62
|
+
initialize_letta_client()
|
63
|
+
print("Using Letta as the agent")
|
64
|
+
else:
|
65
|
+
print(f"Unknown agent type: {agent_type}. Defaulting to Letta.")
|
66
|
+
initialize_letta_client()
|
67
|
+
|
68
|
+
return agent_type
|
69
|
+
|
51
70
|
async def reset_neuro_agent_memory():
|
52
71
|
"""
|
53
72
|
重置 Agent 的记忆,包括:
|
54
73
|
1. 清空所有消息历史记录。
|
55
74
|
2. 清空指定的 'conversation_summary' 核心内存块。
|
56
75
|
"""
|
76
|
+
# Ensure letta client is initialized before using it
|
77
|
+
initialize_letta_client()
|
78
|
+
if letta_client is None:
|
79
|
+
print("Letta client 未初始化,跳过重置。")
|
80
|
+
return
|
81
|
+
|
57
82
|
agent_id = config_manager.settings.api_keys.neuro_agent_id
|
58
|
-
if
|
59
|
-
print("Letta
|
83
|
+
if not agent_id:
|
84
|
+
print("Letta Agent ID 未配置,跳过重置。")
|
60
85
|
return
|
61
86
|
|
62
87
|
# --- 步骤 1: 重置消息历史记录 (上下文) ---
|
@@ -88,11 +113,12 @@ async def reset_neuro_agent_memory():
|
|
88
113
|
else:
|
89
114
|
print(f"警告: 清空核心记忆块 '{block_label_to_clear}' 失败: {e}。")
|
90
115
|
|
91
|
-
|
92
116
|
async def get_neuro_response(chat_messages: list[dict]) -> str:
|
117
|
+
# Ensure letta client is initialized before using it
|
118
|
+
initialize_letta_client()
|
93
119
|
if letta_client is None or not config_manager.settings.api_keys.neuro_agent_id:
|
94
|
-
print("
|
95
|
-
return "
|
120
|
+
print("错误: Letta client 或 Agent ID 未配置,无法获取响应。")
|
121
|
+
return "Someone tell Vedal there is a problem with my AI."
|
96
122
|
|
97
123
|
if chat_messages:
|
98
124
|
injected_chat_lines = [f"{chat['username']}: {chat['text']}" for chat in chat_messages]
|
@@ -128,7 +154,7 @@ async def get_neuro_response(chat_messages: list[dict]) -> str:
|
|
128
154
|
|
129
155
|
if not ai_full_response_text:
|
130
156
|
print(f"警告: 未能从 Letta 响应中解析出有效的文本。响应对象: {response}")
|
131
|
-
return "
|
157
|
+
return "Someone tell Vedal there is a problem with my AI."
|
132
158
|
|
133
159
|
print(f"成功从 Letta 解析到响应: '{ai_full_response_text[:70]}...'")
|
134
160
|
return ai_full_response_text
|
neuro_simulator/log_handler.py
CHANGED
@@ -1,29 +1,43 @@
|
|
1
1
|
# backend/log_handler.py
|
2
2
|
import logging
|
3
|
-
import asyncio
|
4
3
|
from collections import deque
|
4
|
+
from typing import Deque
|
5
5
|
|
6
|
-
#
|
7
|
-
|
6
|
+
# 创建两个独立的、有界限的队列,用于不同来源的日志
|
7
|
+
server_log_queue: Deque[str] = deque(maxlen=1000)
|
8
|
+
agent_log_queue: Deque[str] = deque(maxlen=1000)
|
8
9
|
|
9
10
|
class QueueLogHandler(logging.Handler):
|
10
|
-
"""
|
11
|
+
"""一个将日志记录发送到指定队列的处理器。"""
|
12
|
+
def __init__(self, queue: Deque[str]):
|
13
|
+
super().__init__()
|
14
|
+
self.queue = queue
|
15
|
+
|
11
16
|
def emit(self, record: logging.LogRecord):
|
12
|
-
# 格式化日志消息
|
13
17
|
log_entry = self.format(record)
|
14
|
-
|
18
|
+
self.queue.append(log_entry)
|
15
19
|
|
16
|
-
def
|
17
|
-
"""
|
18
|
-
|
19
|
-
|
20
|
-
formatter = logging.Formatter('%(asctime)s - %(levelname)s - %(message)s', datefmt='%H:%M:%S')
|
21
|
-
|
20
|
+
def configure_server_logging():
|
21
|
+
"""配置服务器(根)日志记录器,将其日志发送到 server_log_queue。"""
|
22
|
+
# 为服务器日志创建一个处理器实例
|
23
|
+
server_queue_handler = QueueLogHandler(server_log_queue)
|
24
|
+
formatter = logging.Formatter('%(asctime)s - [SERVER] - %(levelname)s - %(message)s', datefmt='%H:%M:%S')
|
25
|
+
server_queue_handler.setFormatter(formatter)
|
22
26
|
|
23
|
-
# 获取根 logger
|
24
|
-
#
|
27
|
+
# 获取根 logger 并添加 handler
|
28
|
+
# 这将捕获所有未被专门处理的日志(来自fastapi, uvicorn等)
|
25
29
|
root_logger = logging.getLogger()
|
26
|
-
|
30
|
+
# 清除可能存在的旧handler,以防万一
|
31
|
+
if root_logger.hasHandlers():
|
32
|
+
root_logger.handlers.clear()
|
33
|
+
|
34
|
+
root_logger.addHandler(server_queue_handler)
|
27
35
|
root_logger.setLevel(logging.INFO)
|
28
36
|
|
29
|
-
|
37
|
+
# 将 uvicorn 的日志也引导到我们的 handler
|
38
|
+
logging.getLogger("uvicorn.access").handlers = [server_queue_handler]
|
39
|
+
logging.getLogger("uvicorn.error").handlers = [server_queue_handler]
|
40
|
+
|
41
|
+
print("服务器日志系统已配置,将日志输出到 server_log_queue。")
|
42
|
+
|
43
|
+
# Agent 的日志配置将会在 agent 模块内部完成,以保持解耦
|
neuro_simulator/main.py
CHANGED
@@ -24,11 +24,11 @@ from fastapi.security import APIKeyCookie
|
|
24
24
|
# --- 核心模块导入 ---
|
25
25
|
from .config import config_manager, AppSettings
|
26
26
|
from .process_manager import process_manager
|
27
|
-
from .log_handler import
|
27
|
+
from .log_handler import configure_server_logging, server_log_queue, agent_log_queue
|
28
28
|
|
29
29
|
# --- 功能模块导入 ---
|
30
30
|
from .chatbot import ChatbotManager, get_dynamic_audience_prompt
|
31
|
-
from .letta import get_neuro_response, reset_neuro_agent_memory
|
31
|
+
# from .letta import get_neuro_response, reset_neuro_agent_memory, initialize_agent # This will be imported dynamically
|
32
32
|
from .audio_synthesis import synthesize_audio_segment
|
33
33
|
from .stream_chat import (
|
34
34
|
add_to_audience_buffer, add_to_neuro_input_queue,
|
@@ -39,7 +39,13 @@ from .stream_manager import live_stream_manager
|
|
39
39
|
import neuro_simulator.shared_state as shared_state
|
40
40
|
|
41
41
|
# --- FastAPI 应用和模板设置 ---
|
42
|
-
|
42
|
+
from .agent.api import router as agent_router
|
43
|
+
|
44
|
+
app = FastAPI(title="vedal987 Simulator API", version="1.0.0")
|
45
|
+
|
46
|
+
# 注册API路由
|
47
|
+
app.include_router(agent_router)
|
48
|
+
app.include_router(agent_router) # Include the agent management API router
|
43
49
|
app.add_middleware(
|
44
50
|
CORSMiddleware,
|
45
51
|
allow_origins=config_manager.settings.server.client_origins + ["http://localhost:8080", "https://dashboard.live.jiahui.cafe"], # 添加dashboard_web的地址
|
@@ -142,6 +148,13 @@ async def neuro_response_cycle():
|
|
142
148
|
print("Neuro响应周期: 任务启动。")
|
143
149
|
is_first_response = True
|
144
150
|
|
151
|
+
# Dynamically import get_neuro_response to respect agent_type
|
152
|
+
agent_type = config_manager.settings.agent_type
|
153
|
+
if agent_type == "builtin":
|
154
|
+
from .builtin_agent import get_builtin_response as get_neuro_response
|
155
|
+
else:
|
156
|
+
from .letta import get_neuro_response
|
157
|
+
|
145
158
|
while True:
|
146
159
|
try:
|
147
160
|
if is_first_response:
|
@@ -163,19 +176,34 @@ async def neuro_response_cycle():
|
|
163
176
|
timeout=10.0 # 默认10秒超时
|
164
177
|
)
|
165
178
|
except asyncio.TimeoutError:
|
166
|
-
print("警告:
|
179
|
+
print(f"警告: {agent_type} 响应超时,跳过本轮。")
|
167
180
|
await asyncio.sleep(5)
|
168
181
|
continue
|
169
182
|
|
170
183
|
async with shared_state.neuro_last_speech_lock:
|
171
|
-
|
172
|
-
|
184
|
+
# Handle both string and dict responses
|
185
|
+
response_text = ""
|
186
|
+
if isinstance(ai_full_response_text, dict):
|
187
|
+
# Extract the final response from the dict
|
188
|
+
response_text = ai_full_response_text.get("final_response", "")
|
189
|
+
else:
|
190
|
+
response_text = ai_full_response_text if ai_full_response_text else ""
|
191
|
+
|
192
|
+
if response_text and response_text.strip():
|
193
|
+
shared_state.neuro_last_speech = response_text
|
173
194
|
else:
|
174
195
|
shared_state.neuro_last_speech = "(Neuro-Sama is currently silent...)"
|
175
|
-
print("警告: 从
|
196
|
+
print(f"警告: 从 {agent_type} 获取的响应为空,跳过本轮。")
|
176
197
|
continue
|
177
198
|
|
178
|
-
|
199
|
+
# Handle both string and dict responses for sentence splitting
|
200
|
+
response_text = ""
|
201
|
+
if isinstance(ai_full_response_text, dict):
|
202
|
+
response_text = ai_full_response_text.get("final_response", "")
|
203
|
+
else:
|
204
|
+
response_text = ai_full_response_text if ai_full_response_text else ""
|
205
|
+
|
206
|
+
sentences = [s.strip() for s in re.split(r'(?<=[.!?])\s+', response_text.replace('\n', ' ').strip()) if s.strip()]
|
179
207
|
if not sentences:
|
180
208
|
continue
|
181
209
|
|
@@ -222,7 +250,7 @@ async def neuro_response_cycle():
|
|
222
250
|
async def startup_event():
|
223
251
|
"""应用启动时执行。"""
|
224
252
|
global chatbot_manager
|
225
|
-
|
253
|
+
configure_server_logging()
|
226
254
|
|
227
255
|
# 实例化管理器
|
228
256
|
chatbot_manager = ChatbotManager()
|
@@ -234,6 +262,16 @@ async def startup_event():
|
|
234
262
|
config_manager.register_update_callback(metadata_callback)
|
235
263
|
config_manager.register_update_callback(chatbot_manager.handle_config_update)
|
236
264
|
|
265
|
+
# Initialize the appropriate agent
|
266
|
+
from .letta import initialize_agent
|
267
|
+
from .builtin_agent import initialize_builtin_agent
|
268
|
+
|
269
|
+
agent_type = config_manager.settings.agent_type
|
270
|
+
if agent_type == "builtin":
|
271
|
+
await initialize_builtin_agent()
|
272
|
+
else:
|
273
|
+
await initialize_agent()
|
274
|
+
|
237
275
|
print("FastAPI 应用已启动。请通过外部控制面板控制直播进程。")
|
238
276
|
|
239
277
|
@app.on_event("shutdown")
|
@@ -251,6 +289,13 @@ async def shutdown_event():
|
|
251
289
|
@app.post("/api/stream/start", tags=["Stream Control"], dependencies=[Depends(get_api_token)])
|
252
290
|
async def api_start_stream():
|
253
291
|
"""启动直播"""
|
292
|
+
# If using builtin agent, clear temp memory and context when starting stream
|
293
|
+
agent_type = config_manager.settings.agent_type
|
294
|
+
if agent_type == "builtin":
|
295
|
+
from .builtin_agent import clear_builtin_agent_temp_memory, clear_builtin_agent_context
|
296
|
+
await clear_builtin_agent_temp_memory()
|
297
|
+
await clear_builtin_agent_context()
|
298
|
+
|
254
299
|
if not process_manager.is_running:
|
255
300
|
process_manager.start_live_processes()
|
256
301
|
return {"status": "success", "message": "直播已启动"}
|
@@ -274,6 +319,20 @@ async def api_restart_stream():
|
|
274
319
|
process_manager.start_live_processes()
|
275
320
|
return {"status": "success", "message": "直播已重启"}
|
276
321
|
|
322
|
+
@app.post("/api/agent/reset_memory", tags=["Agent"], dependencies=[Depends(get_api_token)])
|
323
|
+
async def api_reset_agent_memory():
|
324
|
+
"""重置Agent记忆"""
|
325
|
+
agent_type = config_manager.settings.agent_type
|
326
|
+
|
327
|
+
if agent_type == "builtin":
|
328
|
+
from .builtin_agent import reset_builtin_agent_memory
|
329
|
+
await reset_builtin_agent_memory()
|
330
|
+
return {"status": "success", "message": "内置Agent记忆已重置"}
|
331
|
+
else:
|
332
|
+
from .letta import reset_neuro_agent_memory
|
333
|
+
await reset_neuro_agent_memory()
|
334
|
+
return {"status": "success", "message": "Letta Agent记忆已重置"}
|
335
|
+
|
277
336
|
@app.get("/api/stream/status", tags=["Stream Control"], dependencies=[Depends(get_api_token)])
|
278
337
|
async def api_get_stream_status():
|
279
338
|
"""获取直播状态"""
|
@@ -282,16 +341,6 @@ async def api_get_stream_status():
|
|
282
341
|
"backend_status": "running" if process_manager.is_running else "stopped"
|
283
342
|
}
|
284
343
|
|
285
|
-
# -------------------------------------------------------------
|
286
|
-
# --- 日志 API 端点 ---
|
287
|
-
# -------------------------------------------------------------
|
288
|
-
|
289
|
-
@app.get("/api/logs", tags=["Logs"], dependencies=[Depends(get_api_token)])
|
290
|
-
async def api_get_logs(lines: int = 50):
|
291
|
-
"""获取最近的日志行"""
|
292
|
-
logs_list = list(log_queue)
|
293
|
-
return {"logs": logs_list[-lines:] if len(logs_list) > lines else logs_list}
|
294
|
-
|
295
344
|
# -------------------------------------------------------------
|
296
345
|
# --- WebSocket 端点 ---
|
297
346
|
# -------------------------------------------------------------
|
@@ -326,23 +375,102 @@ async def websocket_stream_endpoint(websocket: WebSocket):
|
|
326
375
|
finally:
|
327
376
|
connection_manager.disconnect(websocket)
|
328
377
|
|
329
|
-
@app.websocket("/ws/
|
330
|
-
async def
|
378
|
+
@app.websocket("/ws/admin")
|
379
|
+
async def websocket_admin_endpoint(websocket: WebSocket):
|
331
380
|
await websocket.accept()
|
332
381
|
try:
|
333
|
-
|
334
|
-
|
382
|
+
# Send initial server logs
|
383
|
+
for log_entry in list(server_log_queue):
|
384
|
+
await websocket.send_json({"type": "server_log", "data": log_entry})
|
385
|
+
|
386
|
+
# Send initial agent logs
|
387
|
+
for log_entry in list(agent_log_queue):
|
388
|
+
await websocket.send_json({"type": "agent_log", "data": log_entry})
|
389
|
+
|
390
|
+
# Send initial context
|
391
|
+
# Import the appropriate agent based on config
|
392
|
+
from .config import config_manager
|
393
|
+
agent_type = config_manager.settings.agent_type
|
394
|
+
if agent_type == "builtin":
|
395
|
+
from .builtin_agent import local_agent
|
396
|
+
if local_agent is not None:
|
397
|
+
context_messages = await local_agent.memory_manager.get_recent_context()
|
398
|
+
await websocket.send_json({
|
399
|
+
"type": "agent_context",
|
400
|
+
"action": "update",
|
401
|
+
"messages": context_messages
|
402
|
+
})
|
403
|
+
|
404
|
+
# Keep track of last context messages to detect changes
|
405
|
+
last_context_messages = []
|
406
|
+
|
407
|
+
# Start heartbeat task
|
408
|
+
heartbeat_task = asyncio.create_task(send_heartbeat(websocket))
|
335
409
|
|
336
410
|
while websocket.client_state == WebSocketState.CONNECTED:
|
337
|
-
|
338
|
-
|
339
|
-
|
340
|
-
|
341
|
-
|
411
|
+
# Check for new server logs
|
412
|
+
if server_log_queue:
|
413
|
+
log_entry = server_log_queue.popleft()
|
414
|
+
await websocket.send_json({"type": "server_log", "data": log_entry})
|
415
|
+
|
416
|
+
# Check for new agent logs
|
417
|
+
if agent_log_queue:
|
418
|
+
log_entry = agent_log_queue.popleft()
|
419
|
+
await websocket.send_json({"type": "agent_log", "data": log_entry})
|
420
|
+
|
421
|
+
# Check for context updates (for builtin agent)
|
422
|
+
if agent_type == "builtin" and local_agent is not None:
|
423
|
+
context_messages = await local_agent.memory_manager.get_recent_context()
|
424
|
+
# Compare with last context to detect changes
|
425
|
+
if context_messages != last_context_messages:
|
426
|
+
# Send only new messages
|
427
|
+
if len(context_messages) > len(last_context_messages):
|
428
|
+
new_messages = context_messages[len(last_context_messages):]
|
429
|
+
await websocket.send_json({
|
430
|
+
"type": "agent_context",
|
431
|
+
"action": "append",
|
432
|
+
"messages": new_messages
|
433
|
+
})
|
434
|
+
else:
|
435
|
+
# Only send full update if messages were actually removed (e.g., context reset)
|
436
|
+
# Don't send update if it's just a reordering or modification
|
437
|
+
if len(context_messages) < len(last_context_messages):
|
438
|
+
await websocket.send_json({
|
439
|
+
"type": "agent_context",
|
440
|
+
"action": "update",
|
441
|
+
"messages": context_messages
|
442
|
+
})
|
443
|
+
else:
|
444
|
+
# Send as append if same length but different content
|
445
|
+
await websocket.send_json({
|
446
|
+
"type": "agent_context",
|
447
|
+
"action": "append",
|
448
|
+
"messages": context_messages
|
449
|
+
})
|
450
|
+
last_context_messages = context_messages
|
451
|
+
|
452
|
+
# Small delay to prevent busy waiting
|
453
|
+
await asyncio.sleep(0.1)
|
342
454
|
except WebSocketDisconnect:
|
343
|
-
print("
|
455
|
+
print("管理面板WebSocket客户端已断开连接。")
|
344
456
|
finally:
|
345
|
-
|
457
|
+
# Cancel heartbeat task
|
458
|
+
if 'heartbeat_task' in locals():
|
459
|
+
heartbeat_task.cancel()
|
460
|
+
print("管理面板WebSocket连接关闭。")
|
461
|
+
|
462
|
+
|
463
|
+
# 心跳任务,定期发送心跳消息以保持连接活跃
|
464
|
+
async def send_heartbeat(websocket: WebSocket):
|
465
|
+
while websocket.client_state == WebSocketState.CONNECTED:
|
466
|
+
try:
|
467
|
+
# 发送心跳消息
|
468
|
+
await websocket.send_json({"type": "heartbeat", "timestamp": time.time()})
|
469
|
+
# 每5秒发送一次心跳
|
470
|
+
await asyncio.sleep(5)
|
471
|
+
except Exception as e:
|
472
|
+
print(f"发送心跳消息时出错: {e}")
|
473
|
+
break
|
346
474
|
|
347
475
|
|
348
476
|
# -------------------------------------------------------------
|
@@ -382,6 +510,13 @@ def filter_config_for_frontend(settings):
|
|
382
510
|
'stream_tags': settings.stream_metadata.stream_tags
|
383
511
|
}
|
384
512
|
|
513
|
+
# Agent settings (不包含 agent_type)
|
514
|
+
if hasattr(settings, 'agent'):
|
515
|
+
filtered_settings['agent'] = {
|
516
|
+
'agent_provider': settings.agent.agent_provider,
|
517
|
+
'agent_model': settings.agent.agent_model
|
518
|
+
}
|
519
|
+
|
385
520
|
# Neuro behavior settings
|
386
521
|
if hasattr(settings, 'neuro_behavior'):
|
387
522
|
filtered_settings['neuro_behavior'] = {
|
@@ -431,6 +566,8 @@ async def update_configs(new_settings: dict):
|
|
431
566
|
'stream_metadata.stream_title',
|
432
567
|
'stream_metadata.stream_category',
|
433
568
|
'stream_metadata.stream_tags',
|
569
|
+
'agent.agent_provider', # 添加 agent 配置项(不包含 agent_type)
|
570
|
+
'agent.agent_model',
|
434
571
|
'neuro_behavior.input_chat_sample_size',
|
435
572
|
'neuro_behavior.post_speech_cooldown_sec',
|
436
573
|
'neuro_behavior.initial_greeting',
|
@@ -27,11 +27,14 @@ class ProcessManager:
|
|
27
27
|
from .main import generate_audience_chat_task, neuro_response_cycle, broadcast_events_task
|
28
28
|
from .stream_manager import live_stream_manager
|
29
29
|
from .stream_chat import clear_all_queues
|
30
|
-
|
30
|
+
|
31
|
+
# Initialize Agent and reset memory
|
32
|
+
from .letta import reset_neuro_agent_memory, initialize_agent
|
33
|
+
import asyncio
|
34
|
+
asyncio.create_task(initialize_agent())
|
31
35
|
|
32
36
|
# 清理状态和队列,开始新的直播周期
|
33
37
|
clear_all_queues()
|
34
|
-
asyncio.create_task(reset_neuro_agent_memory())
|
35
38
|
live_stream_manager.reset_stream_state()
|
36
39
|
|
37
40
|
# 创建并存储任务
|
@@ -84,6 +84,12 @@ class LiveStreamManager:
|
|
84
84
|
print("正在启动新的直播周期...")
|
85
85
|
self._stream_start_global_time = time.time()
|
86
86
|
|
87
|
+
# 清除旧的上下文历史
|
88
|
+
from .builtin_agent import local_agent
|
89
|
+
if local_agent is not None:
|
90
|
+
await local_agent.memory_manager.reset_context()
|
91
|
+
print("旧的上下文历史已清除。")
|
92
|
+
|
87
93
|
self._current_phase = self.StreamPhase.INITIALIZING
|
88
94
|
print(f"进入阶段: {self.StreamPhase.INITIALIZING}. 广播 'play_welcome_video' 事件。")
|
89
95
|
await self.event_queue.put({
|
@@ -0,0 +1,31 @@
|
|
1
|
+
neuro_simulator/__init__.py,sha256=PpTff-dZfzIweg_Ld0Zt8JNAMTtvLtGn_OC0Ms2E2Q8,276
|
2
|
+
neuro_simulator/audio_synthesis.py,sha256=fwS0car42-aheCFVQDgRpUTnqLv4DU9Re66PCa6zTBM,2938
|
3
|
+
neuro_simulator/builtin_agent.py,sha256=0TrAmIIjg7aCXiZxgXggyBvBSGR-CsVFUcot71NNfck,2751
|
4
|
+
neuro_simulator/chatbot.py,sha256=qQamPO5QTPwV1avNXrzjZpiJlWN3X_6vxSZPSA17jb0,5152
|
5
|
+
neuro_simulator/cli.py,sha256=rlwUW1ol8_G06OLf0G-ae0WRXofjoTt3eO9l_Ea8XCY,8183
|
6
|
+
neuro_simulator/config.py,sha256=brA8kiekV_995mpz04JiGj1swIWbZZuWWLNYtbroMyE,14884
|
7
|
+
neuro_simulator/config.yaml.example,sha256=RbSHEAFD9NEuIvgl5Smpasfz4jiZYQgzTovL3MrhhIs,6549
|
8
|
+
neuro_simulator/letta.py,sha256=q7ME_idhW6-koOpFfdNZ1-wwr3DEFIyk1hSFKVci24s,7568
|
9
|
+
neuro_simulator/log_handler.py,sha256=w8nQ-91CgiZNI94vJA9Ezgt-x5LRL_AbFZ4-0Mc6ekE,1756
|
10
|
+
neuro_simulator/main.py,sha256=d8kgo3Bc0q7pXzw6mmDXHOBaZ93JLs2Ov-cNJVuT_ps,28820
|
11
|
+
neuro_simulator/process_manager.py,sha256=cQ7XqHz0cbuJQ_ju0PoOxZzzS04ADQVARyKRHIN1bN8,2578
|
12
|
+
neuro_simulator/shared_state.py,sha256=cvYg8vGrkuDmb0e-4J1xvIjXVwspRisuxQaIR9YgsyQ,459
|
13
|
+
neuro_simulator/stream_chat.py,sha256=X655rrOW8oQ77qAFK-dQDhHT3bYiE9w44a9Na_ikM2w,997
|
14
|
+
neuro_simulator/stream_manager.py,sha256=bwY7Dxo_HLPmQw-1IFvptHBZPMeBbBxdw8_oyZHOSkQ,6966
|
15
|
+
neuro_simulator/websocket_manager.py,sha256=a9mMAN7xmgK2w9tVDOOzy4DuWSwdAPatSp6bW9fhE3I,2183
|
16
|
+
neuro_simulator/agent/__init__.py,sha256=H6eIGBRG5y1FdjUw3BcwP1nWzmMEpIE1XG2xMV_e5Y0,113
|
17
|
+
neuro_simulator/agent/api.py,sha256=Oy4xy32B9dPzBeGyjKtw89tAPjKDKnye9fUaGVsHu-s,33483
|
18
|
+
neuro_simulator/agent/core.py,sha256=d0jtRDd1B-UnNcYeEMDJGWMLpI0Lf3iEWzBR7_DqUlw,21447
|
19
|
+
neuro_simulator/agent/llm.py,sha256=ohINZpP6ybRWfxK89XMM1yYXehKvRVBuy14ksoqrm8I,4125
|
20
|
+
neuro_simulator/agent/memory.py,sha256=b41THZj0LCHlW4GApF02NoW1n1WanySLsNl6JMGpYDQ,5416
|
21
|
+
neuro_simulator/agent/tools.py,sha256=hwdJ99soOSOFOqfkBl0oGMYVe4U5rfh1HsGkXd3EUFc,2801
|
22
|
+
neuro_simulator/agent/memory/__init__.py,sha256=Be08Az0lWn9d51TLfE2UoluIT9yXVfzHPekIjxJnGOU,78
|
23
|
+
neuro_simulator/agent/memory/manager.py,sha256=OGFAnd_bLc1Dv0-5JAXCZtIMUBFyk79bfRdQ8ib8ZWA,16352
|
24
|
+
neuro_simulator/agent/tools/__init__.py,sha256=8ztH_OkeXaQDxOaKBb78vKIA8IKkRLxyHLnwGtdk9YU,76
|
25
|
+
neuro_simulator/agent/tools/core.py,sha256=YsXbwjF5be_yrh0w5goaSWux53LOkLJ91rikgWPUKDQ,5974
|
26
|
+
neuro_simulator/media/neuro_start.mp4,sha256=xCLnNzv4THnzRYwkdV6EiqXc-XtFd867R2ZVLDvNp0Y,8226418
|
27
|
+
neuro_simulator-0.1.2.dist-info/METADATA,sha256=-tN71ig_UAfvtywp_q3Rd7D_cJukrjohcrxuq8858B4,6366
|
28
|
+
neuro_simulator-0.1.2.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
29
|
+
neuro_simulator-0.1.2.dist-info/entry_points.txt,sha256=qVd5ypnRRgU8Cw7rWfZ7o0OXyS9P9hgY-cRoN_mgz9g,51
|
30
|
+
neuro_simulator-0.1.2.dist-info/top_level.txt,sha256=V8awSKpcrFnjJDiJxSfy7jtOrnuE2BgAR9hLmfMDWK8,16
|
31
|
+
neuro_simulator-0.1.2.dist-info/RECORD,,
|
@@ -1,20 +0,0 @@
|
|
1
|
-
neuro_simulator/__init__.py,sha256=JJWUFh_lBowSfGMhmC9zixE70cB7GG1pTbjGY4Kg3mU,29
|
2
|
-
neuro_simulator/audio_synthesis.py,sha256=fwS0car42-aheCFVQDgRpUTnqLv4DU9Re66PCa6zTBM,2938
|
3
|
-
neuro_simulator/chatbot.py,sha256=qQamPO5QTPwV1avNXrzjZpiJlWN3X_6vxSZPSA17jb0,5152
|
4
|
-
neuro_simulator/cli.py,sha256=gf_9Ar_uIodL8O2ZhrXhtXXFJzaQ6QCEzWoQub-JOeI,5978
|
5
|
-
neuro_simulator/config.py,sha256=7ek1hKi4mxTRv1IdrkkFgY8Hb_JaPL8a637z71TKHpw,9613
|
6
|
-
neuro_simulator/config.yaml.example,sha256=uywVEnoFZGZ4g6qdp616Qoi78F2AF21rSG99gtFiY-s,6003
|
7
|
-
neuro_simulator/letta.py,sha256=5lcypQE8bnOXVAELSjoRJ27RlLU2C_Skvy4QB85J3Nk,6623
|
8
|
-
neuro_simulator/log_handler.py,sha256=JlCtJ7wJ0pruVGVO5u-kz-F5uaYhlBl4H5xoGUFhT18,1079
|
9
|
-
neuro_simulator/main.py,sha256=1otrYFzSLO6ctYYV04tKWMNJFCwEuulfPTlsdHH2Uwk,22444
|
10
|
-
neuro_simulator/process_manager.py,sha256=KqW11PRJR5e4RSBytaZdkgbd3wxRtWqlz6D0TA4oR1s,2492
|
11
|
-
neuro_simulator/shared_state.py,sha256=cvYg8vGrkuDmb0e-4J1xvIjXVwspRisuxQaIR9YgsyQ,459
|
12
|
-
neuro_simulator/stream_chat.py,sha256=X655rrOW8oQ77qAFK-dQDhHT3bYiE9w44a9Na_ikM2w,997
|
13
|
-
neuro_simulator/stream_manager.py,sha256=vA88HQGgYguFb3_GUDSY-FpTG0rsjt8J-JFtMoCWWCo,6720
|
14
|
-
neuro_simulator/websocket_manager.py,sha256=a9mMAN7xmgK2w9tVDOOzy4DuWSwdAPatSp6bW9fhE3I,2183
|
15
|
-
neuro_simulator/media/neuro_start.mp4,sha256=xCLnNzv4THnzRYwkdV6EiqXc-XtFd867R2ZVLDvNp0Y,8226418
|
16
|
-
neuro_simulator-0.0.4.dist-info/METADATA,sha256=MOHKocm9fsjdBrtzLGVCWQSr2bNQ6NtEJxk5itbH2LE,6366
|
17
|
-
neuro_simulator-0.0.4.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
18
|
-
neuro_simulator-0.0.4.dist-info/entry_points.txt,sha256=qVd5ypnRRgU8Cw7rWfZ7o0OXyS9P9hgY-cRoN_mgz9g,51
|
19
|
-
neuro_simulator-0.0.4.dist-info/top_level.txt,sha256=V8awSKpcrFnjJDiJxSfy7jtOrnuE2BgAR9hLmfMDWK8,16
|
20
|
-
neuro_simulator-0.0.4.dist-info/RECORD,,
|
File without changes
|
File without changes
|
File without changes
|