neuro-simulator 0.5.4__py3-none-any.whl → 0.6.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.
Files changed (75) hide show
  1. neuro_simulator/agent/llm.py +23 -19
  2. neuro_simulator/chatbot/core.py +10 -10
  3. neuro_simulator/chatbot/llm.py +22 -19
  4. neuro_simulator/chatbot/nickname_gen/generator.py +3 -3
  5. neuro_simulator/chatbot/tools/manager.py +10 -8
  6. neuro_simulator/cli.py +7 -12
  7. neuro_simulator/client/assets/index-C_kzLmQy.css +1 -0
  8. neuro_simulator/client/assets/index-DRLWJPZv.js +7 -0
  9. neuro_simulator/client/assets/inter-cyrillic-400-normal-BLGc9T1a.woff2 +0 -0
  10. neuro_simulator/client/assets/inter-cyrillic-400-normal-alAqRL36.woff +0 -0
  11. neuro_simulator/client/assets/inter-cyrillic-ext-400-normal-BE2fNs0E.woff +0 -0
  12. neuro_simulator/client/assets/inter-cyrillic-ext-400-normal-Dc4VJyIJ.woff2 +0 -0
  13. neuro_simulator/client/assets/inter-greek-400-normal-C3I71FoW.woff +0 -0
  14. neuro_simulator/client/assets/inter-greek-400-normal-DxZsaF_h.woff2 +0 -0
  15. neuro_simulator/client/assets/inter-greek-ext-400-normal-Bput3-QP.woff2 +0 -0
  16. neuro_simulator/client/assets/inter-greek-ext-400-normal-XIH6-K3k.woff +0 -0
  17. neuro_simulator/client/assets/inter-latin-400-normal-C38fXH4l.woff2 +0 -0
  18. neuro_simulator/client/assets/inter-latin-400-normal-CyCys3Eg.woff +0 -0
  19. neuro_simulator/client/assets/inter-latin-ext-400-normal-77YHD8bZ.woff +0 -0
  20. neuro_simulator/client/assets/inter-latin-ext-400-normal-C1nco2VV.woff2 +0 -0
  21. neuro_simulator/client/assets/inter-vietnamese-400-normal-Bbgyi5SW.woff +0 -0
  22. neuro_simulator/client/assets/inter-vietnamese-400-normal-DMkecbls.woff2 +0 -0
  23. neuro_simulator/client/avatar.webp +0 -0
  24. neuro_simulator/client/background.webp +0 -0
  25. neuro_simulator/client/background_old.webp +0 -0
  26. neuro_simulator/client/banner.jpeg +0 -0
  27. neuro_simulator/client/channel_points.png +0 -0
  28. neuro_simulator/client/error.mp3 +0 -0
  29. neuro_simulator/client/favicon.ico +0 -0
  30. neuro_simulator/client/fonts/causten.woff2 +0 -0
  31. neuro_simulator/client/fonts/comic.woff2 +0 -0
  32. neuro_simulator/client/fonts/first-coffee.woff2 +0 -0
  33. neuro_simulator/client/fonts/noto-sans-sc.woff2 +0 -0
  34. neuro_simulator/client/index.html +306 -0
  35. neuro_simulator/client/neuro_start.mp4 +0 -0
  36. neuro_simulator/client/neurosama.png +0 -0
  37. neuro_simulator/client/sc_pink.png +0 -0
  38. neuro_simulator/client/sc_purple.png +0 -0
  39. neuro_simulator/client/sub_badge.svg +4 -0
  40. neuro_simulator/client/user_avatar.jpg +0 -0
  41. neuro_simulator/core/agent_factory.py +9 -18
  42. neuro_simulator/core/application.py +86 -56
  43. neuro_simulator/core/config.py +88 -301
  44. neuro_simulator/core/path_manager.py +7 -7
  45. neuro_simulator/dashboard/assets/{AgentView-C6qW7TIe.js → AgentView-DBq2msN_.js} +2 -2
  46. neuro_simulator/dashboard/assets/{ChatBotView-BRYIM_8s.js → ChatBotView-BqQsuJUv.js} +2 -2
  47. neuro_simulator/dashboard/assets/ConfigView-CPYMgl_d.js +2 -0
  48. neuro_simulator/dashboard/assets/ConfigView-aFribfyR.css +1 -0
  49. neuro_simulator/dashboard/assets/{ContextTab-GRHICOS3.js → ContextTab-BSROkcd2.js} +1 -1
  50. neuro_simulator/dashboard/assets/{ControlView-D5vPB_OE.js → ControlView-BvflkxO-.js} +1 -1
  51. neuro_simulator/dashboard/assets/FieldRenderer-DyPAEyOT.js +1 -0
  52. neuro_simulator/dashboard/assets/LogsTab-C-SZhHdN.js +1 -0
  53. neuro_simulator/dashboard/assets/LogsView-82wOs2Pp.js +1 -0
  54. neuro_simulator/dashboard/assets/{MemoryTab-BSUWFbcV.js → MemoryTab-p3Q-Wa4e.js} +3 -3
  55. neuro_simulator/dashboard/assets/{ToolsTab-Bjcm3fFL.js → ToolsTab-BxbFZhXs.js} +1 -1
  56. neuro_simulator/dashboard/assets/index-Ba5ZG3QB.js +52 -0
  57. neuro_simulator/dashboard/assets/{index-C7dox9UB.css → index-CcYt9OR6.css} +1 -1
  58. neuro_simulator/dashboard/index.html +2 -2
  59. neuro_simulator/services/audio.py +55 -47
  60. neuro_simulator/services/builtin.py +3 -0
  61. neuro_simulator/services/stream.py +1 -1
  62. neuro_simulator/utils/queue.py +2 -2
  63. {neuro_simulator-0.5.4.dist-info → neuro_simulator-0.6.1.dist-info}/METADATA +1 -2
  64. {neuro_simulator-0.5.4.dist-info → neuro_simulator-0.6.1.dist-info}/RECORD +68 -35
  65. requirements.txt +1 -1
  66. neuro_simulator/config.yaml.example +0 -117
  67. neuro_simulator/dashboard/assets/ConfigView-Cw-VPFzt.js +0 -2
  68. neuro_simulator/dashboard/assets/FieldRenderer-DaTYxmtO.js +0 -1
  69. neuro_simulator/dashboard/assets/LogsTab-CATao-mZ.js +0 -1
  70. neuro_simulator/dashboard/assets/LogsView-BM419A5R.js +0 -1
  71. neuro_simulator/dashboard/assets/index-BiAhe8fO.js +0 -34
  72. neuro_simulator/services/letta.py +0 -254
  73. {neuro_simulator-0.5.4.dist-info → neuro_simulator-0.6.1.dist-info}/WHEEL +0 -0
  74. {neuro_simulator-0.5.4.dist-info → neuro_simulator-0.6.1.dist-info}/entry_points.txt +0 -0
  75. {neuro_simulator-0.5.4.dist-info → neuro_simulator-0.6.1.dist-info}/licenses/LICENSE +0 -0
@@ -0,0 +1,306 @@
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8" />
5
+ <link rel="icon" type="image/x-icon" href="./favicon.ico" />
6
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
7
+ <title>𝐯𝐞𝐝𝐚𝐥𝟗𝟖𝟕 - 𝐓𝐰𝐢𝐭𝐜𝐡</title>
8
+ <script type="module" crossorigin src="./assets/index-DRLWJPZv.js"></script>
9
+ <link rel="stylesheet" crossorigin href="./assets/index-C_kzLmQy.css">
10
+ </head>
11
+ <body>
12
+ <header id="twitch-header">
13
+ <div class="top-nav-left">
14
+ <a href="/" class="twitch-logo-link" aria-label="Twitch 主页">
15
+ <figure class="twitch-logo-container">
16
+ <svg class="twitch-logo-svg" overflow="visible" width="40px" height="40px" version="1.1" viewBox="0 0 40 40" x="0px" y="0px">
17
+ <g>
18
+ <polygon points="13 8 8 13 8 31 14 31 14 36 19 31 23 31 32 22 32 8" class="logo-body"></polygon>
19
+ <polygon points="26 25 30 21 30 10 14 10 14 25 18 25 18 29 22 25" class="logo-face"></polygon>
20
+ <g class="logo-eyes">
21
+ <path d="M20,14 L22,14 L22,20 L20,20 L20,14 Z M27,14 L27,20 L25,20 L25,14 L27,14 Z" class="logo-eye-path"></path>
22
+ </g>
23
+ </g>
24
+ </svg>
25
+ </figure>
26
+ </a>
27
+ <div class="nav-links-main">
28
+ <a class="nav-link" href="#">
29
+ <p>正在关注</p>
30
+ </a>
31
+ <a class="nav-link" href="#">
32
+ <p>浏览</p>
33
+ </a>
34
+ <button class="nav-icon-button" data-tooltip="更多选择">
35
+ <svg width="20" height="20" viewBox="0 0 20 20" focusable="false" aria-hidden="true" role="presentation"><path d="M10 18a2 2 0 1 1 0-4 2 2 0 0 1 0 4zm0-6a2 2 0 1 1 0-4 2 2 0 0 1 0 4zM8 4a2 2 0 1 0 4 0 2 2 0 0 0-4 0z"></path></svg>
36
+ </button>
37
+ </div>
38
+ </div>
39
+
40
+ <div class="top-nav-center">
41
+ <div class="search-container">
42
+ <input type="search" class="search-input" placeholder="搜索" />
43
+ <button class="search-button" aria-label="搜索按钮">
44
+ <svg width="20" height="20" viewBox="0 0 20 20" focusable="false" aria-hidden="true" role="presentation"><path d="M13.192 14.606a7 7 0 1 1 1.414-1.414l3.101 3.1-1.414 1.415-3.1-3.1zM14 9A5 5 0 1 1 4 9a5 5 0 0 1 10 0z" fill-rule="evenodd"></path></svg>
45
+ </button>
46
+ </div>
47
+ </div>
48
+
49
+ <div class="top-nav-right">
50
+ <button class="nav-icon-button" aria-label="Prime" data-tooltip="Prime 新品">
51
+ <svg width="20" height="20" viewBox="0 0 20 20" focusable="false" aria-hidden="true" role="presentation"><path fill-rule="evenodd" d="M13.798 10.456 10 6.657l-3.798 3.799L4 8.805V13h12V8.805l-2.202 1.65zM18 5v8a2 2 0 0 1-2 2H4a2.002 2.002 0 0 1-2-2V5l4 3 4-4 4 4 4-3z" clip-rule="evenodd"></path></svg>
52
+ </button>
53
+ <button class="nav-icon-button" aria-label="通知" data-tooltip="打开通知">
54
+ <svg width="20" height="20" viewBox="0 0 20 20" focusable="false" aria-hidden="true" role="presentation"><path fill-rule="evenodd" d="M4 3h12l2 4v10H2V7l2-4zm.236 4H8v1a1 1 0 0 0 1 1h2a1 1 0 0 0 1-1V7h3.764l-1-2H5.236l-1 2zM16 9h-2.17A3.001 3.001 0 0 1 11 11H9a3.001 3.001 0 0 1-2.83-2H4v6h12V9z" clip-rule="evenodd"></path></svg>
55
+ </button>
56
+ <button class="nav-icon-button" aria-label="悄悄话" data-tooltip="悄悄话">
57
+ <svg width="20" height="20" viewBox="0 0 20 20" focusable="false" aria-hidden="true" role="presentation"><path fill-rule="evenodd" d="M7.828 13 10 15.172 12.172 13H15V5H5v8h2.828zM10 18l-3-3H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h10a2 2 0 0 1 2 2v8a2 2 0 0 1-2 2h-2l-3 3z" clip-rule="evenodd"></path></svg>
58
+ </button>
59
+ <button class="nav-icon-button" aria-label="购买呼币" data-tooltip="购买呼币">
60
+ <svg width="20" height="20" viewBox="0 0 20 20" aria-hidden="true" role="presentation"><path fill-rule="evenodd" d="m3 12 7-10 7 10-7 6-7-6zm2.678-.338L10 5.487l4.322 6.173-.85.728L10 11l-3.473 1.39-.849-.729z" clip-rule="evenodd"></path></svg>
61
+ </button>
62
+ <button class="twitch-button turbo-button-full" aria-label="免费体验无广告观赏" data-tooltip="Turbo">
63
+ <svg width="20" height="20" viewBox="0 0 20 20" focusable="false" aria-hidden="true" role="presentation"><path d="M10.114 9.622 11 7 7.175 9.323a.382.382 0 0 0 .013.65l.698.405L7 13l3.825-2.323a.382.382 0 0 0-.012-.65l-.699-.405z"></path><path fill-rule="evenodd" d="M18 7h-2V4H2v12h14v-3h2V7zm-4-1v3h2v2h-2v3H4V6h10z" clip-rule="evenodd"></path></svg>
64
+ <span>免费体验无广告观赏</span>
65
+ </button>
66
+ <button class="nav-icon-button turbo-button-icon" aria-label="免费体验无广告观赏" data-tooltip="Turbo">
67
+ <svg width="20" height="20" viewBox="0 0 20 20" focusable="false" aria-hidden="true" role="presentation"><path d="M10.114 9.622 11 7 7.175 9.323a.382.382 0 0 0 .013.65l.698.405L7 13l3.825-2.323a.382.382 0 0 0-.012-.65l-.699-.405z"></path><path fill-rule="evenodd" d="M18 7h-2V4H2v12h14v-3h2V7zm-4-1v3h2v2h-2v3H4V6h10z" clip-rule="evenodd"></path></svg>
68
+ </button>
69
+
70
+ <button class="nav-user-avatar-button" aria-expanded="false" aria-label="用户菜单">
71
+ <div class="user-avatar-wrapper">
72
+ <img class="user-avatar-img" alt="用户头像" src="./user_avatar.jpg" style="object-fit: cover;">
73
+ </div>
74
+ </button>
75
+ </div>
76
+ </header>
77
+
78
+ <div id="main-content-wrapper">
79
+ <div id="stream-and-info-container">
80
+ <div id="stream-display-viewport">
81
+ <div id="stream-display-area">
82
+ <div id="background-display" data-darkreader-ignore></div>
83
+ <div id="neuro-static-avatar-container">
84
+ <img id="neuro-static-avatar" src="./neurosama.png" alt="Neuro-Sama Static Avatar" />
85
+ </div>
86
+ <div id="neuro-caption"></div>
87
+ <div id="startup-video-overlay">
88
+ <video id="startup-video" src="./neuro_start.mp4" playsinline muted preload="auto"></video>
89
+ </div>
90
+ <!-- Twitch 风格聊天覆盖层 -->
91
+ <div id="twitch-chat-overlay" class="messages-display">
92
+ <!-- 这里将显示聊天消息 -->
93
+ </div>
94
+ <div id="highlight-message-overlay" class="hidden"></div>
95
+ </div>
96
+ <button id="show-chat-button" class="twitch-button dark icon-button" aria-label="展开聊天">
97
+ <svg width="20" height="20" viewBox="0 0 20 20" aria-hidden="true" role="presentation"><path d="M16 16V4h2v12h-2zM6 9l2.501-2.5-1.5-1.5-5 5 5 5 1.5-1.5-2.5-2.5h8V9H6z"></path></svg>
98
+ </button>
99
+ <button id="mute-button" class="twitch-button dark icon-button" aria-label="取消静音(m)">
100
+ <svg width="20" height="20" viewBox="0 0 20 20" focusable="false" aria-hidden="true" role="presentation">
101
+ <path d="m5 7 4.146-4.146a.5.5 0 0 1 .854.353v13.586a.5.5 0 0 1-.854.353L5 13H4a2 2 0 0 1-2-2V9a2 2 0 0 1 2-2h1zm7 1.414L13.414 7l1.623 1.623L16.66 7l1.414 1.414-1.623 1.623 1.623 1.623-1.414 1.414-1.623-1.623-1.623 1.623L12 11.66l1.623-1.623L12 8.414z"></path>
102
+ </svg>
103
+ </button>
104
+ </div>
105
+
106
+ <div id="offline-content-container" class="hidden">
107
+ <div class="offline-info-card">
108
+ <div class="offline-status-section">
109
+ <strong class="offline-status-badge">离线</strong>
110
+ <h2 class="offline-status-title">当前未连接到直播服务,或直播未开始。</h2>
111
+ </div>
112
+ <div class="offline-notification-section">
113
+ <button class="offline-notification-button twitch-button">
114
+ <svg width="20" height="20" viewBox="0 0 20 20" aria-hidden="true"><path fill-rule="evenodd" d="M17 14v-2c-1-.5-1.75-1-2-2-.095-.38-.154-.905-.221-1.506C14.49 5.936 14.049 2 10 2 5.95 2 5.509 5.936 5.221 8.494 5.154 9.095 5.095 9.62 5 10c-.25 1-1 1.5-2 2v2h14zm-9.002 2h4-4zm4 0v.012V16zm-5.766-4h7.536a4.262 4.262 0 0 1-.708-1.515c-.129-.513-.2-1.154-.26-1.684a32.48 32.48 0 0 0-.009-.083c-.152-1.355-.314-2.606-.78-3.535-.21-.423-.447-.692-.703-.862C11.063 4.158 10.673 4 10 4s-1.063.158-1.308.32c-.256.171-.492.44-.704.863-.465.929-.627 2.18-.78 3.535L7.2 8.8c-.06.53-.131 1.171-.26 1.684-.15.603-.402 1.1-.708 1.515zm1.766 4a2.001 2.001 0 0 0 4 .012" clip-rule="evenodd"></path></svg>
115
+ <span>开启通知</span>
116
+ </button>
117
+ </div>
118
+ </div>
119
+ <iframe class="offline-video-player" scrolling="no" border="0" frameborder="no" framespacing="0" allowfullscreen="true" sandbox="allow-top-navigation allow-same-origin allow-forms allow-scripts"></iframe>
120
+ </div>
121
+
122
+ <section id="stream-info">
123
+ <div class="stream-info-layout">
124
+ <div class="stream-info-left-column">
125
+ <a href="https://twitch.tv/vedal987" class="streamer-avatar-link">
126
+ <div class="streamer-avatar-wrapper">
127
+ <img id="streamer-avatar" src="./avatar.webp" alt="Streamer Avatar" />
128
+ <div class="live-indicator-wrapper">
129
+ <div class="live-indicator-rect">
130
+ <span class="live-text" id="live-indicator-text">LIVE</span>
131
+ </div>
132
+ </div>
133
+ </div>
134
+ </a>
135
+ </div>
136
+
137
+ <div class="stream-info-right-column">
138
+ <div class="stream-info-main-row">
139
+ <div class="streamer-info-and-name">
140
+ <h1 id="streamer-nickname">vedal987</h1>
141
+ <div class="verified-badge">
142
+ <svg width="16" height="16" viewBox="0 0 16 16" aria-label="已验证的合作伙伴"><path fill-rule="evenodd" d="M12.5 3.5 8 2 3.5 3.5 2 8l1.5 4.5L8 14l4.5-1.5L14 8l-1.5-4.5ZM7 11l4.5-4.5L10 5 7 8 5.5 6.5 4 8l3 3Z" clip-rule="evenodd"></path></svg>
143
+ </div>
144
+ </div>
145
+ <div class="main-action-buttons">
146
+ <button class="twitch-button icon-button" aria-label="关注" data-tooltip="取消关注">
147
+ <svg version="1.1" viewBox="0 0 20 20" x="0px" y="0px"><path d="M9.171 4.171A4 4 0 0 0 6.343 3H6a4 4 0 0 0-4 4v.343a4 4 0 0 0 1.172 2.829L10 17l6.828-6.828A4 4 0 0 0 18 7.343V7a4 4 0 0 0-4-4h-.343a4 4 0 0 0-2.829 1.172L10 5l-.829-.829z" fill-rule="evenodd"></path></svg>
148
+ </button>
149
+ <button class="twitch-button icon-button" aria-label="通知" data-tooltip="关闭通知">
150
+ <svg version="1.1" viewBox="0 0 20 20" x="0px" y="0px"><path d="M3 14v-2c1-.5 1.75-1 2-2 .095-.38.154-.905.221-1.506C5.51 5.936 5.951 2 10 2c4.05 0 4.491 3.936 4.779 6.494.067.601.126 1.126.221 1.506.25 1 1 1.5 2 2v2H3zm6.998 4a2 2 0 0 1-2-2h4v.012a2 2 0 0 1-2 1.988z"></path></svg>
151
+ </button>
152
+ <button class="twitch-button" aria-label="购买呼币">
153
+ <svg viewBox="0 0 20 20" aria-hidden="true"><path fill-rule="evenodd" d="m3 12 7-10 7 10-7 6-7-6zm2.678-.338L10 5.487l4.322 6.173-.85.728L10 11l-3.473 1.39-.849-.729z" clip-rule="evenodd"></path></svg>
154
+ <span>购买呼币</span>
155
+ </button>
156
+ <button class="twitch-button" aria-label="赠送一次订阅">
157
+ <svg viewBox="0 0 20 20" aria-hidden="true"><path fill-rule="evenodd" d="M16 6h2v6h-1v6H3v-6H2V6h2V4.793c0-2.507 3.03-3.762 4.803-1.99.131.131.249.275.352.429L10 4.5l.845-1.268a2.81 2.81 0 0 1 .352-.429C12.969 1.031 16 2.286 16 4.793V6zM6 4.793V6h2.596L7.49 4.341A.814.814 0 0 0 6 4.793zm8 0V6h-2.596l1.106-1.659a.814.814 0 0 1 1.49.451zM16 8v2h-5V8h5zm-1 8v-4h-4v4h4zM9 8v2H4V8h5zm0 4H5v4h4v-4z" clip-rule="evenodd"></path></svg>
158
+ <span>赠送一次订阅</span>
159
+ <svg viewBox="0 0 20 20"><path d="M14.5 6.5 10 11 5.5 6.5 4 8l6 6 6-6-1.5-1.5z"></path></svg>
160
+ </button>
161
+ <button class="twitch-button subscribe-button">
162
+ <svg viewBox="0 0 20 20" aria-hidden="true"><path fill-rule="evenodd" d="M11.456 8.255 10 5.125l-1.456 3.13-3.49.485 2.552 2.516-.616 3.485L10 13.064l3.01 1.677-.616-3.485 2.553-2.516-3.491-.485zM7.19 6.424l-4.2.583c-.932.13-1.318 1.209-.664 1.853l3.128 3.083-.755 4.272c-.163.92.876 1.603 1.722 1.132L10 15.354l3.579 1.993c.846.47 1.885-.212 1.722-1.132l-.755-4.272 3.128-3.083c.654-.644.268-1.723-.664-1.853l-4.2-.583-1.754-3.77c-.406-.872-1.706-.872-2.112 0L7.19 6.424z" clip-rule="evenodd"></path></svg>
163
+ <span>订阅</span>
164
+ <svg viewBox="0 0 20 20"><path d="M14.5 6.5 10 11 5.5 6.5 4 8l6 6 6-6-1.5-1.5z"></path></svg>
165
+ </button>
166
+ </div>
167
+ </div>
168
+
169
+ <div class="stream-info-details-row">
170
+ <div class="stream-details-left">
171
+ <p id="stream-title-full" title="neuro stream">neuro stream</p>
172
+ <div class="stream-category-and-tags">
173
+ <a href="#" class="stream-category">谈天说地</a>
174
+ <div class="stream-tags"></div>
175
+ </div>
176
+ </div>
177
+
178
+ <div class="stream-details-right">
179
+ <div class="stream-stats-section">
180
+ <div class="viewer-count">
181
+ <svg width="20" height="20" viewBox="0 0 20 20" aria-hidden="true"><path fill-rule="evenodd" d="M5 7a5 5 0 1 1 6.192 4.857A2 2 0 0 0 13 13h1a3 3 0 0 1 3 3v2h-2v-2a1 1 0 0 0-1-1h-1a3.99 3.99 0 0 1-3-1.354A3.99 3.99 0 0 1 7 15H6a1 1 0 0 0-1 1v2H3v-2a3 3 0 0 1 3-3h1a2 2 0 0 0 1.808-1.143A5.002 5.002 0 0 1 5 7zm5 3a3 3 0 1 1 0-6 3 3 0 0 1 0 6z" clip-rule="evenodd"></path></svg>
182
+ <strong id="avg-viewers">11,290</strong>
183
+ </div>
184
+ <div class="stream-duration">
185
+ <span class="duration-text" id="stream-duration-text">00:00:00</span>
186
+ </div>
187
+ </div>
188
+ <div class="stream-secondary-actions">
189
+ <button class="twitch-button icon-button" aria-label="分享">
190
+ <svg width="20" height="20" viewBox="0 0 20 20"><path d="M2 16v-3h2v3h12v-3h2v3a2 2 0 0 1-2 2H4a2 2 0 0 1-2-2zm13-9-1.5 1.5L11 6v7H9V6L6.5 8.5 5 7l5-5 5 5z"></path></svg>
191
+ </button>
192
+ <button class="twitch-button icon-button" aria-label="更多选项">
193
+ <svg width="20" height="20" viewBox="0 0 20 20"><path d="M10 18a2 2 0 1 1 0-4 2 2 0 0 1 0 4zm0-6a2 2 0 1 1 0-4 2 2 0 0 1 0 4zM8 4a2 2 0 1 0 4 0 2 2 0 0 0-4 0z"></path></svg>
194
+ </button>
195
+ </div>
196
+ </div>
197
+ </div>
198
+ </div>
199
+ </div>
200
+ </section>
201
+ </div>
202
+
203
+ <!-- 右侧聊天侧边栏 -->
204
+ <div id="chat-sidebar" class="chat-sidebar">
205
+ <!-- 顶部收缩按钮和聊天标题 (居中修正) -->
206
+ <div class="chat-sidebar-header">
207
+ <div class="chat-header-left">
208
+ <button id="toggle-chat-button" class="chat-toggle-button" aria-label="重叠聊天">
209
+ <svg width="20" height="20" viewBox="0 0 20 20" aria-hidden="true" role="presentation"><path d="M4 16V4H2v12h2zm9-1-1.5-1.5L14 11H6V9h8l-2.5-2.5L13 5l5 5-5 5z"></path></svg>
210
+ </button>
211
+ </div>
212
+ <h4 class="chat-title">直播聊天</h4>
213
+ <div class="chat-header-right">
214
+ <button class="chat-toolbar-button" aria-label="社区">
215
+ <svg width="20" height="20" viewBox="0 0 20 20"><g><path d="M7 2a4 4 0 0 0-1.015 7.87A1.334 1.334 0 0 1 4.667 11 2.667 2.667 0 0 0 2 13.667V18h2v-4.333c0-.368.298-.667.667-.667A3.32 3.32 0 0 0 7 12.047 3.32 3.32 0 0 0 9.333 13c.369 0 .667.299.667.667V18h2v-4.333A2.667 2.667 0 0 0 9.333 11c-.667 0-1.22-.49-1.318-1.13A4.002 4.002 0 0 0 7 2zM5 6a2 2 0 1 0 4 0 2 2 0 0 0-4 0z" fill-rule="evenodd"></path><path d="M14 11.83V18h4v-3.75c0-.69-.56-1.25-1.25-1.25a.75.75 0 0 1-.75-.75v-.42a3.001 3.001 0 1 0-2 0z"></path></g></svg>
216
+ </button>
217
+ </div>
218
+ </div>
219
+
220
+ <!-- 聊天消息显示区 -->
221
+ <div id="chat-messages" class="messages-display"></div>
222
+
223
+ <!-- 聊天输入区 (重构后) -->
224
+ <div class="chat-input-area">
225
+ <div class="chat-input-and-buttons">
226
+ <div class="chat-input-textarea-container">
227
+ <div class="chat-input-prefix-icons">
228
+ <button class="chat-toolbar-button" aria-label="聊天室身份">
229
+ <img alt="订阅者徽章" class="chat-badge-icon" src="./sub_badge.svg">
230
+ </button>
231
+ </div>
232
+ <div class="chat-input-wrapper">
233
+ <input type="text" id="chat-input" class="chat-input-element" placeholder="发送消息" />
234
+ </div>
235
+ <div class="chat-input-suffix-icons">
236
+ <button class="chat-toolbar-button" aria-label="欢呼">
237
+ <svg width="20" height="20" viewBox="0 0 20 20" aria-hidden="true" role="presentation"><path d="M14 4V2h-2v2h2z"></path><path fill-rule="evenodd" d="M10.293 8.293 16 14 4 18l-2-2L6 4l2.879 2.879A2.99 2.99 0 0 1 11 6h1v2h-1a.997.997 0 0 0-.707.293zm-3.419-.59-.643 1.93 2.67 4.625 2.254-.751-2.08-3.604-2.201-2.2zM4.498 14.83l.887-2.662 1.58 2.735-2.033.677-.434-.75z" clip-rule="evenodd"></path><path d="M16 5V4h2v1a3 3 0 0 1-3 3h-1V6h1a1 1 0 0 0 1-1zm2 5v2h-2v-2h2z"></path></svg>
238
+ </button>
239
+ <button class="chat-toolbar-button" aria-label="表情符号选择器">
240
+ <svg width="20" height="20" viewBox="0 0 20 20" focusable="false" aria-hidden="true" role="presentation"><g><path d="M7 11a1 1 0 1 0 0-2 1 1 0 0 0 0 2zm7-1a1 1 0 1 1-2 0 1 1 0 0 1 2 0zm-4 4a2 2 0 0 0 2-2H8a2 2 0 0 0 2 2z"></path><path d="M18 10a8 8 0 1 1-16 0 8 8 0 0 1 16 0zm-2 0a6 6 0 1 1-12 0 6 6 0 0 1 12 0z" fill-rule="evenodd"></path></g></svg>
241
+ </button>
242
+ </div>
243
+ </div>
244
+ <div class="chat-input-buttons-container">
245
+ <div class="chat-buttons-left">
246
+ <button id="sc-bits-button" class="chat-toolbar-text-button" aria-label="呼币">
247
+ <svg width="20" height="20" viewBox="0 0 20 20"><path fill-rule="evenodd" d="m3 12 7-10 7 10-7 6-7-6zm2.678-.338L10 5.487l4.322 6.173-.85.728L10 11l-3.473 1.39-.849-.729z" clip-rule="evenodd"></path></svg>
248
+ <span class="points-value">0</span>
249
+ </button>
250
+ <button id="sc-points-button" class="chat-toolbar-text-button" aria-label="频道点数">
251
+ <img class="channel-points-icon" src="./channel_points.png" alt="频道点数">
252
+ <span class="points-value">0</span>
253
+ </button>
254
+ </div>
255
+ <div class="chat-buttons-right">
256
+ <button class="chat-toolbar-button" aria-label="聊天设置">
257
+ <svg width="20" height="20" viewBox="0 0 20 20" focusable="false" aria-hidden="true" role="presentation"><path d="M10 8a2 2 0 1 0 0 4 2 2 0 0 0 0-4z"></path><path fill-rule="evenodd" d="M9 2h2a2.01 2.01 0 0 0 1.235 1.855l.53.22a2.01 2.01 0 0 0 2.185-.439l1.414 1.414a2.01 2.01 0 0 0-.439 2.185l.22.53A2.01 2.01 0 0 0 18 9v2a2.01 2.01 0 0 0-1.855 1.235l-.22.53a2.01 2.01 0 0 0 .44 2.185l-1.415 1.414a2.01 2.01 0 0 0-2.184-.439l-.531.22A2.01 2.01 0 0 0 11 18H9a2.01 2.01 0 0 0-1.235-1.854l-.53-.22a2.009 2.009 0 0 0-2.185.438L3.636 14.95a2.009 2.009 0 0 0 .438-2.184l-.22-.531A2.01 2.01 0 0 0 2 11V9c.809 0 1.545-.487 1.854-1.235l.22-.53a2.009 2.009 0 0 0-.438-2.185L5.05 3.636a2.01 2.01 0 0 0 2.185.438l.53-.22A2.01 2.01 0 0 0 9 2zm-4 8 1.464 3.536L10 15l3.535-1.464L15 10l-1.465-3.536L10 5 6.464 6.464 5 10z" clip-rule="evenodd"></path></svg>
258
+ </button>
259
+ <button id="send-button" class="twitch-button subscribe-button">聊天</button>
260
+ </div>
261
+ </div>
262
+ </div>
263
+ </div>
264
+ </div>
265
+ </div>
266
+ <div id="settings-modal" class="settings-modal-container hidden">
267
+ <div id="settings-modal-overlay" class="modal-overlay"></div>
268
+ <div class="modal-content">
269
+ <header class="modal-header">
270
+ <h2>设置</h2>
271
+ <button id="settings-close-button" class="twitch-button icon-button">
272
+ <svg width="20" height="20" viewBox="0 0 20 20" focusable="false" aria-hidden="true" role="presentation">
273
+ <path d="M8.5 10 4 5.5 5.5 4 10 8.5 14.5 4 16 5.5 11.5 10l4.5 4.5-1.5 1.5-4.5-4.5L5.5 16 4 14.5 8.5 10z"></path>
274
+ </svg>
275
+ </button>
276
+ </header>
277
+ <main class="modal-body">
278
+ <div class="setting-item">
279
+ <label for="username-setting-input">用户名</label>
280
+ <input type="text" id="username-setting-input" class="modal-input" placeholder="输入你的聊天昵称">
281
+ </div>
282
+ <div class="setting-item avatar-setting">
283
+ <label>头像</label>
284
+ <div class="avatar-preview-container">
285
+ <img id="avatar-setting-preview" src="./user_avatar.jpg" alt="Avatar Preview" class="avatar-preview">
286
+ <input type="file" id="avatar-setting-upload" accept="image/*" class="avatar-upload-input">
287
+ <button id="avatar-upload-button" class="twitch-button">上传图片</button>
288
+ </div>
289
+ </div>
290
+ <div class="setting-item">
291
+ <label for="backend-url-input">后端地址</label>
292
+ <input type="text" id="backend-url-input" class="modal-input" placeholder="例如:ws://127.0.0.1:8000">
293
+ </div>
294
+ <div class="setting-item">
295
+ <label for="reconnect-attempts-input">最大重连次数</label>
296
+ <input type="number" id="reconnect-attempts-input" class="modal-input" placeholder="输入一个数字">
297
+ <p class="setting-description">连接失败后的自动重试次数。设置为 -1 可无限重试。</p>
298
+ </div>
299
+ </main>
300
+ <footer class="modal-footer">
301
+ <button id="settings-save-button" class="twitch-button subscribe-button"><span>保存并重连</span></button>
302
+ </footer>
303
+ </div>
304
+ </div>
305
+ </body>
306
+ </html>
Binary file
Binary file
Binary file
Binary file
@@ -0,0 +1,4 @@
1
+ <svg width="72" height="72" viewBox="0 0 72 72" fill="none" xmlns="http://www.w3.org/2000/svg">
2
+ <rect width="72" height="72" rx="2" fill="black" fill-opacity="0.1"/>
3
+ <path fill-rule="evenodd" clip-rule="evenodd" d="M40.7282 30.4598L36.001 19.933L31.2738 30.4598L19.8197 31.6598L28.3735 39.3116L25.9724 50.5487L36.001 44.8071L46.0296 50.5487L43.6285 39.3116L52.1823 31.6598L40.7282 30.4598ZM28.1168 26.0984L15.2257 27.4489C12.7694 27.7062 11.7798 30.7292 13.6147 32.3705L23.244 40.9844L20.5545 53.5714C20.042 55.9698 22.6326 57.8381 24.7722 56.6132L36.001 50.1845L47.2298 56.6132C49.3694 57.8381 51.96 55.9698 51.4475 53.5714L48.7579 40.9844L58.3873 32.3705C60.2221 30.7292 59.2326 27.7062 56.7763 27.4489L43.8852 26.0984L38.6077 14.346C37.6021 12.1067 34.3999 12.1067 33.3943 14.346L28.1168 26.0984Z" fill="#53535F"/>
4
+ </svg>
Binary file
@@ -19,32 +19,23 @@ config_manager.register_update_callback(_reset_agent_on_config_update)
19
19
 
20
20
  async def create_agent() -> BaseAgent:
21
21
  """
22
- Factory function to create and initialize an agent instance based on the configuration.
22
+ Factory function to create and initialize the agent instance.
23
23
  Returns a cached instance unless the configuration has changed.
24
24
  """
25
25
  global _agent_instance
26
26
  if _agent_instance is not None:
27
27
  return _agent_instance
28
28
 
29
- agent_type = config_manager.settings.agent_type
30
- logger.info(f"Creating new agent instance of type: {agent_type}")
29
+ logger.info(f"Creating new agent instance...")
31
30
 
32
- if agent_type == "builtin":
33
- from ..services.builtin import BuiltinAgentWrapper, initialize_builtin_agent
34
-
35
- agent_impl = await initialize_builtin_agent()
36
-
37
- if agent_impl is None:
38
- raise RuntimeError("Failed to initialize the Builtin agent implementation.")
39
-
40
- _agent_instance = BuiltinAgentWrapper(agent_impl)
41
-
42
- elif agent_type == "letta":
43
- from ..services.letta import LettaAgent
44
- _agent_instance = LettaAgent()
31
+ from ..services.builtin import BuiltinAgentWrapper, initialize_builtin_agent
45
32
 
46
- else:
47
- raise ValueError(f"Unknown agent type: {agent_type}")
33
+ agent_impl = await initialize_builtin_agent()
34
+
35
+ if agent_impl is None:
36
+ raise RuntimeError("Failed to initialize the Builtin agent implementation.")
37
+
38
+ _agent_instance = BuiltinAgentWrapper(agent_impl)
48
39
 
49
40
  await _agent_instance.initialize()
50
41
 
@@ -18,7 +18,6 @@ from .config import config_manager, AppSettings
18
18
  from ..core.agent_factory import create_agent
19
19
  from ..agent.core import Agent as LocalAgent
20
20
  from ..chatbot.core import ChatbotAgent
21
- from ..services.letta import LettaAgent
22
21
  from ..services.builtin import BuiltinAgentWrapper
23
22
 
24
23
  # --- API Routers ---
@@ -71,7 +70,7 @@ app.include_router(system_router)
71
70
 
72
71
  # --- Background Task Definitions ---
73
72
 
74
- chatbot_agent: ChatbotAgent = None
73
+ chatbot: ChatbotAgent = None
75
74
 
76
75
  async def broadcast_events_task():
77
76
  """Broadcasts events from the live_stream_manager's queue to all clients."""
@@ -87,7 +86,7 @@ async def broadcast_events_task():
87
86
 
88
87
  async def fetch_and_process_audience_chats():
89
88
  """Generates a batch of audience chat messages using the new ChatbotAgent."""
90
- if not chatbot_agent:
89
+ if not chatbot:
91
90
  return
92
91
  try:
93
92
  # Get context for the chatbot
@@ -96,7 +95,7 @@ async def fetch_and_process_audience_chats():
96
95
  recent_history = get_recent_audience_chats_for_chatbot(limit=10)
97
96
 
98
97
  # Generate messages
99
- generated_messages = await chatbot_agent.generate_chat_messages(
98
+ generated_messages = await chatbot.generate_chat_messages(
100
99
  neuro_speech=context_message,
101
100
  recent_history=recent_history
102
101
  )
@@ -125,8 +124,8 @@ async def generate_audience_chat_task():
125
124
 
126
125
  asyncio.create_task(fetch_and_process_audience_chats())
127
126
 
128
- # Use the interval from the new chatbot_agent config
129
- await asyncio.sleep(config_manager.settings.chatbot_agent.generation_interval_sec)
127
+ # Use the interval from the new chatbot config
128
+ await asyncio.sleep(config_manager.settings.chatbot.generation_interval_sec)
130
129
  except asyncio.CancelledError:
131
130
  break
132
131
  except Exception as e:
@@ -148,20 +147,14 @@ async def neuro_response_cycle():
148
147
  app_state.last_superchat_time = time.time()
149
148
  await connection_manager.broadcast({"type": "processing_superchat", "data": sc})
150
149
 
151
- # Agent-specific payload generation for superchats
152
- if isinstance(agent, LettaAgent):
153
- selected_chats = [
154
- {"role": "system", "content": "=== RANDOM 10 MSG IN CHATROOM ===\nNO MSG FETCH DUE TO UNPROCESSED HIGHLIGHTED MESSAGE"},
155
- {"role": "system", "content": f"=== HIGHLIGHTED MESSAGE ===\n{sc['username']}: {sc['text']}"}
156
- ]
157
- else: # For BuiltinAgent and any other future agents
158
- selected_chats = [{'username': sc['username'], 'text': sc['text']}]
150
+ # For BuiltinAgent and any other future agents
151
+ selected_chats = [{'username': sc['username'], 'text': sc['text']}]
159
152
 
160
153
  # Clear the regular input queue to prevent immediate follow-up with normal chats
161
154
  get_all_neuro_input_chats()
162
155
  else:
163
156
  if is_first_response:
164
- add_to_neuro_input_queue({"username": "System", "text": config_manager.settings.neuro_behavior.initial_greeting})
157
+ add_to_neuro_input_queue({"username": "System", "text": config_manager.settings.neuro.initial_greeting})
165
158
  is_first_response = False
166
159
  elif is_neuro_input_queue_empty():
167
160
  await asyncio.sleep(1)
@@ -170,7 +163,7 @@ async def neuro_response_cycle():
170
163
  current_queue_snapshot = get_all_neuro_input_chats()
171
164
  if not current_queue_snapshot:
172
165
  continue
173
- sample_size = min(config_manager.settings.neuro_behavior.input_chat_sample_size, len(current_queue_snapshot))
166
+ sample_size = min(config_manager.settings.neuro.input_chat_sample_size, len(current_queue_snapshot))
174
167
  selected_chats = random.sample(current_queue_snapshot, sample_size)
175
168
 
176
169
  if not selected_chats:
@@ -192,7 +185,12 @@ async def neuro_response_cycle():
192
185
  sentences = [s.strip() for s in re.split(r'(?<=[.!?])\s+', response_text.replace('\n', ' ')) if s.strip()]
193
186
  if not sentences: continue
194
187
 
195
- synthesis_tasks = [synthesize_audio_segment(s) for s in sentences]
188
+ tts_id = config_manager.settings.neuro.tts_provider_id
189
+ if not tts_id:
190
+ logger.warning("TTS Provider ID is not set for the agent. Skipping speech synthesis.")
191
+ continue
192
+
193
+ synthesis_tasks = [synthesize_audio_segment(s, tts_provider_id=tts_id) for s in sentences]
196
194
  synthesis_results = await asyncio.gather(*synthesis_tasks, return_exceptions=True)
197
195
 
198
196
  speech_packages = [
@@ -210,7 +208,7 @@ async def neuro_response_cycle():
210
208
  await connection_manager.broadcast({"type": "neuro_speech_segment", "is_end": True})
211
209
  live_stream_manager.set_neuro_speaking_status(False)
212
210
 
213
- await asyncio.sleep(config_manager.settings.neuro_behavior.post_speech_cooldown_sec)
211
+ await asyncio.sleep(config_manager.settings.neuro.post_speech_cooldown_sec)
214
212
 
215
213
  except asyncio.TimeoutError:
216
214
  logger.warning("Agent response timed out, skipping this cycle.")
@@ -260,8 +258,35 @@ async def startup_event():
260
258
  else:
261
259
  frontend_dir = None
262
260
 
261
+ # --- Mount Client Frontend ---
262
+ # Mount the client frontend at /client path (more specific) - MOUNT THIS FIRST
263
+ try:
264
+ # Production/Standard install: find client frontend in the package
265
+ client_frontend_dir_traversable = files('neuro_simulator').joinpath('client')
266
+ if not client_frontend_dir_traversable.is_dir(): raise FileNotFoundError
267
+ client_frontend_dir = str(client_frontend_dir_traversable)
268
+ logger.info(f"Found client frontend via package resources (production mode): '{client_frontend_dir}'")
269
+ except (ModuleNotFoundError, FileNotFoundError):
270
+ # Editable/Development install: fall back to relative path from source
271
+ logger.info("Could not find client frontend via package resources, falling back to development mode path.")
272
+ dev_client_path = os.path.abspath(os.path.join(os.path.dirname(__file__), '..', '..', '..', 'client', 'dist'))
273
+ if os.path.isdir(dev_client_path):
274
+ client_frontend_dir = dev_client_path
275
+ logger.info(f"Found client frontend via relative path (development mode): '{client_frontend_dir}'")
276
+ else:
277
+ client_frontend_dir = None
278
+
279
+ if client_frontend_dir:
280
+ app.mount("/client", SPAStaticFiles(directory=client_frontend_dir, html=True), name="client")
281
+ logger.info("Client frontend mounted at /client")
282
+ else:
283
+ logger.error("Client frontend directory not found in either production or development locations.")
284
+
285
+ # --- Mount Dashboard Frontend ---
286
+ # Mount the dashboard frontend at / path (more general) - MOUNT THIS AFTER
263
287
  if frontend_dir:
264
288
  app.mount("/", SPAStaticFiles(directory=frontend_dir, html=True), name="dashboard")
289
+ logger.info("Dashboard frontend mounted at /")
265
290
  else:
266
291
  logger.error("Frontend directory not found in either production or development locations.")
267
292
 
@@ -271,10 +296,7 @@ async def startup_event():
271
296
  # 2. Initialize queues now that config is loaded
272
297
  initialize_queues()
273
298
 
274
- # 3. Initialize the new Chatbot Agent
275
- global chatbot_agent
276
- chatbot_agent = ChatbotAgent()
277
- await chatbot_agent.initialize()
299
+ # 3. Chatbot Agent will be initialized on stream start.
278
300
 
279
301
  # 4. Register callbacks
280
302
  async def metadata_callback(settings: AppSettings):
@@ -285,7 +307,7 @@ async def startup_event():
285
307
  # 5. Initialize main agent (which will load its own configs)
286
308
  try:
287
309
  await create_agent()
288
- logger.info(f"Successfully initialized agent type: {config_manager.settings.agent_type}")
310
+ logger.info(f"Successfully initialized agent.")
289
311
  except Exception as e:
290
312
  logger.critical(f"Agent initialization failed on startup: {e}", exc_info=True)
291
313
 
@@ -305,9 +327,9 @@ async def websocket_stream_endpoint(websocket: WebSocket):
305
327
  await connection_manager.connect(websocket)
306
328
  try:
307
329
  await connection_manager.send_personal_message(live_stream_manager.get_initial_state_for_client(), websocket)
308
- await connection_manager.send_personal_message({"type": "update_stream_metadata", **config_manager.settings.stream_metadata.model_dump()}, websocket)
330
+ await connection_manager.send_personal_message({"type": "update_stream_metadata", **config_manager.settings.stream.model_dump()}, websocket)
309
331
 
310
- initial_chats = get_recent_audience_chats(config_manager.settings.performance.initial_chat_backlog_limit)
332
+ initial_chats = get_recent_audience_chats(config_manager.settings.server.initial_chat_backlog_limit)
311
333
  for chat in initial_chats:
312
334
  await connection_manager.send_personal_message({"type": "chat_message", **chat, "is_user_message": False}, websocket)
313
335
  await asyncio.sleep(0.01)
@@ -494,6 +516,23 @@ async def handle_admin_ws_message(websocket: WebSocket, data: dict):
494
516
 
495
517
  # Stream Control Actions
496
518
  elif action == "start_stream":
519
+ # Validate that required providers are set before starting
520
+ agent_cfg = config_manager.settings.neuro
521
+ chatbot_cfg = config_manager.settings.chatbot
522
+ if not agent_cfg.llm_provider_id:
523
+ raise ValueError("Agent (Neuro) does not have an LLM Provider configured.")
524
+ if not agent_cfg.tts_provider_id:
525
+ raise ValueError("Agent (Neuro) does not have a TTS Provider configured.")
526
+ if not chatbot_cfg.llm_provider_id:
527
+ raise ValueError("Chatbot does not have an LLM Provider configured.")
528
+
529
+ # Initialize chatbot agent on first stream start if not already initialized
530
+ global chatbot
531
+ if chatbot is None:
532
+ logger.info("Initializing ChatbotAgent for the first time...")
533
+ chatbot = ChatbotAgent()
534
+ await chatbot.initialize()
535
+
497
536
  logger.info("Start stream action received. Resetting agent memory before starting processes...")
498
537
  await agent.reset_memory()
499
538
  if not process_manager.is_running:
@@ -549,34 +588,28 @@ async def handle_admin_ws_message(websocket: WebSocket, data: dict):
549
588
  response["payload"] = context
550
589
 
551
590
  elif action == "get_last_prompt":
552
- # This is specific to the builtin agent, as Letta doesn't expose its prompt.
553
- agent_instance = getattr(agent, 'agent_instance', agent)
554
- if not isinstance(agent_instance, LocalAgent):
555
- response["payload"] = {"prompt": "The active agent does not support prompt generation introspection."}
556
- else:
557
- try:
558
- # 1. Get the recent history from the agent itself
559
- history = await agent_instance.get_neuro_history(limit=10)
560
-
561
- # 2. Reconstruct the 'messages' list that _build_neuro_prompt expects
562
- messages_for_prompt = []
563
- for entry in history:
564
- if entry.get('role') == 'user':
565
- # Content is in the format "username: text"
566
- content = entry.get('content', '')
567
- parts = content.split(':', 1)
568
- if len(parts) == 2:
569
- messages_for_prompt.append({'username': parts[0].strip(), 'text': parts[1].strip()})
570
- elif content: # Handle cases where there's no colon
571
- messages_for_prompt.append({'username': 'user', 'text': content})
572
-
573
- # 3. Build the prompt using the agent's own internal logic
574
- prompt = await agent_instance._build_neuro_prompt(messages_for_prompt)
575
- response["payload"] = {"prompt": prompt}
576
- except Exception as e:
577
- logger.error(f"Error generating last prompt: {e}", exc_info=True)
578
- response["payload"] = {"prompt": f"Failed to generate prompt: {e}"}
579
-
591
+ try:
592
+ # 1. Get the recent history from the agent itself
593
+ history = await agent.get_message_history(limit=10)
594
+
595
+ # 2. Reconstruct the 'messages' list that _build_neuro_prompt expects
596
+ messages_for_prompt = []
597
+ for entry in history:
598
+ if entry.get('role') == 'user':
599
+ # Content is in the format "username: text"
600
+ content = entry.get('content', '')
601
+ parts = content.split(':', 1)
602
+ if len(parts) == 2:
603
+ messages_for_prompt.append({'username': parts[0].strip(), 'text': parts[1].strip()})
604
+ elif content: # Handle cases where there's no colon
605
+ messages_for_prompt.append({'username': 'user', 'text': content})
606
+
607
+ # 3. Build the prompt using the agent's own internal logic
608
+ prompt = await agent.build_neuro_prompt(messages_for_prompt)
609
+ response["payload"] = {"prompt": prompt}
610
+ except Exception as e:
611
+ logger.error(f"Error generating last prompt: {e}", exc_info=True)
612
+ response["payload"] = {"prompt": f"Failed to generate prompt: {e}"}
580
613
  elif action == "reset_agent_memory":
581
614
  await agent.reset_memory()
582
615
  response["payload"] = {"status": "success"}
@@ -598,6 +631,3 @@ async def handle_admin_ws_message(websocket: WebSocket, data: dict):
598
631
  if request_id:
599
632
  response["payload"] = {"status": "error", "message": str(e)}
600
633
  await websocket.send_json(response)
601
-
602
-
603
-