neuro-simulator 0.6.1__py3-none-any.whl → 0.6.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.
- README.md +40 -45
- neuro_simulator/client/assets/index-rmyQl8Tr.css +1 -0
- neuro_simulator/client/index.html +10 -10
- neuro_simulator/core/application.py +19 -11
- neuro_simulator/dashboard/assets/{AgentView-DBq2msN_.js → AgentView-DGut3feB.js} +2 -2
- neuro_simulator/dashboard/assets/{ChatBotView-BqQsuJUv.js → ChatBotView-bvwHe8hJ.js} +2 -2
- neuro_simulator/dashboard/assets/{ConfigView-CPYMgl_d.js → ConfigView-DLy2_6Tx.js} +2 -2
- neuro_simulator/dashboard/assets/{ContextTab-BSROkcd2.js → ContextTab-Cv2a7xRF.js} +1 -1
- neuro_simulator/dashboard/assets/{ControlView-BvflkxO-.js → ControlView-BVJk6r1d.js} +1 -1
- neuro_simulator/dashboard/assets/{FieldRenderer-DyPAEyOT.js → FieldRenderer-xMOXYQeU.js} +1 -1
- neuro_simulator/dashboard/assets/{LogsTab-C-SZhHdN.js → LogsTab-C7r21Vcz.js} +1 -1
- neuro_simulator/dashboard/assets/{LogsView-82wOs2Pp.js → LogsView-DF62M8uv.js} +1 -1
- neuro_simulator/dashboard/assets/{MemoryTab-p3Q-Wa4e.js → MemoryTab-BMDqRcHX.js} +1 -1
- neuro_simulator/dashboard/assets/{ToolsTab-BxbFZhXs.js → ToolsTab-Ds4M2VeQ.js} +1 -1
- neuro_simulator/dashboard/assets/{index-CcYt9OR6.css → index-D2ait7Ff.css} +2 -2
- neuro_simulator/dashboard/assets/{index-Ba5ZG3QB.js → index-Sav9Djr5.js} +3 -3
- neuro_simulator/dashboard/index.html +3 -3
- {neuro_simulator-0.6.1.dist-info → neuro_simulator-0.6.2.dist-info}/METADATA +37 -39
- {neuro_simulator-0.6.1.dist-info → neuro_simulator-0.6.2.dist-info}/RECORD +23 -23
- neuro_simulator/client/assets/index-C_kzLmQy.css +0 -1
- /neuro_simulator/client/assets/{index-DRLWJPZv.js → index-BHgW05Wx.js} +0 -0
- {neuro_simulator-0.6.1.dist-info → neuro_simulator-0.6.2.dist-info}/WHEEL +0 -0
- {neuro_simulator-0.6.1.dist-info → neuro_simulator-0.6.2.dist-info}/entry_points.txt +0 -0
- {neuro_simulator-0.6.1.dist-info → neuro_simulator-0.6.2.dist-info}/licenses/LICENSE +0 -0
README.md
CHANGED
@@ -2,11 +2,11 @@
|
|
2
2
|
|
3
3
|
*本临时README由AI自动生成*
|
4
4
|
|
5
|
-
这是 Neuro Simulator 的服务端,负责处理直播逻辑、AI 交互、TTS
|
5
|
+
这是 Neuro Simulator 的服务端,负责处理直播逻辑、AI 交互、TTS 合成等核心功能。
|
6
6
|
|
7
7
|
## 功能特性
|
8
8
|
|
9
|
-
-
|
9
|
+
- **动态观众**:调用LLM,基于直播内容动态生成 Chat
|
10
10
|
- **配置管理**:支持通过 API 动态修改和热重载配置
|
11
11
|
- **外部控制**:完全使用外部API端点操控服务端运行
|
12
12
|
|
@@ -15,7 +15,7 @@
|
|
15
15
|
``` main
|
16
16
|
neuro_simulator/
|
17
17
|
├── __init__.py
|
18
|
-
├── cli.py
|
18
|
+
├── cli.py
|
19
19
|
├── agent/
|
20
20
|
│ ├── __init__.py
|
21
21
|
│ ├── core.py
|
@@ -49,7 +49,6 @@ neuro_simulator/
|
|
49
49
|
│ ├── audience.py
|
50
50
|
│ ├── audio.py
|
51
51
|
│ ├── builtin.py
|
52
|
-
│ ├── letta.py
|
53
52
|
│ └── stream.py
|
54
53
|
└── utils/
|
55
54
|
├── __init__.py
|
@@ -61,11 +60,10 @@ neuro_simulator/
|
|
61
60
|
```
|
62
61
|
|
63
62
|
``` workin'dir
|
64
|
-
working_dir_example/ #
|
63
|
+
working_dir_example/ # 工作目录结构,供你参考
|
65
64
|
├── assets/ # 媒体文件夹,如缺失会使用自带资源覆盖
|
66
65
|
│ └── neuro_start.mp4 # 用来计算Start Soon长度,仅读取时长,请和客户端的视频保持一致
|
67
|
-
├── config.yaml #
|
68
|
-
├── config.yaml.example # 自动生成的配置文件模板,必须手动重命名和填写
|
66
|
+
├── config.yaml # 系统配置文件,由服务端自动管理,无需手动填写
|
69
67
|
└── agents/ # Agent相关文件夹
|
70
68
|
├── memories/ # Agent记忆文件夹
|
71
69
|
│ ├── core_memory.json
|
@@ -85,34 +83,35 @@ working_dir_example/ # 工作目录结构,请将这个目录重命名和
|
|
85
83
|
|
86
84
|
## 安装与配置
|
87
85
|
|
88
|
-
1.
|
89
|
-
- 程序会在未指定 `--dir` 的情况下自动生成一个工作目录,路径为 `~/.config/neuro-simulator
|
90
|
-
2.
|
91
|
-
3. 编辑 `config.yaml` 文件,填入必要的 API 密钥和配置项:
|
92
|
-
- 如果使用 Letta Agent,需要配置 Letta Token 和 Agent ID
|
93
|
-
- Gemini/OpenAI API Key(用于观众聊天生成和 Agent)
|
94
|
-
- Azure TTS Key 和 Region
|
86
|
+
1. 自己找一个最好是空的文件夹作为工作目录。
|
87
|
+
- 程序会在未指定 `--dir, -D` 的情况下自动生成一个工作目录,路径为 `~/.config/neuro-simulator/`。
|
88
|
+
2. 启动程序,按照外面那个 README 中的方式完成配置。
|
95
89
|
|
96
|
-
可以自行替换 `$dir/assets/neuro_start.mp4` 为其它视频文件,但记得手动替换 client
|
90
|
+
3. 可以自行替换 `$dir/assets/neuro_start.mp4` 为其它视频文件,但记得手动替换 client 中的同名文件。
|
97
91
|
|
98
92
|
### Agent配置
|
99
93
|
|
100
|
-
服务端支持两种Agent类型:
|
101
|
-
1. **Letta Agent**:需要配置 Letta Cloud 或自托管的 Letta Server
|
102
|
-
2. **内建 Agent**:使用服务端自带的 Agent,支持 Gemini 和OpenAI API
|
94
|
+
服务端支持两种 Agent 类型:
|
95
|
+
~~1. **Letta Agent**:需要配置 Letta Cloud 或自托管的 Letta Server~~ 暂时下线,后会有期。
|
96
|
+
2. **内建 Agent**:使用服务端自带的 Agent,支持 Gemini 和OpenAI API。
|
103
97
|
|
104
|
-
|
105
|
-
- `agent_type: "letta"`:使用 Letta Agent
|
106
|
-
- `agent_type: "builtin"`:使用内建 Agent
|
107
|
-
|
108
|
-
当使用内建Agent时,还需要配置:
|
109
|
-
- `agent.agent_provider`:选择"gemini"或"openai"
|
110
|
-
- `agent.agent_model`:指定具体的模型名称
|
98
|
+
不管用的是什么 Agent,在管理面板中配置和分配好服务商相关就行了。
|
111
99
|
|
112
100
|
### 直接安装方式(无需二次开发)
|
113
101
|
|
114
102
|
若无需二次开发,可以直接使用 pip 安装:
|
115
103
|
```bash
|
104
|
+
# 直接使用 pip 安装为全局软件:
|
105
|
+
pip install neuro-simulator
|
106
|
+
```
|
107
|
+
|
108
|
+
```bash
|
109
|
+
# 系统 Python 环境不宜变动时,建议使用 pipx 安装为全局软件
|
110
|
+
pipx install neuro-simulator
|
111
|
+
```
|
112
|
+
|
113
|
+
```bash
|
114
|
+
# 使用 venv 方式安装:
|
116
115
|
python3 -m venv venv
|
117
116
|
# Windows
|
118
117
|
venv/Scripts/pip install neuro-simulator
|
@@ -132,6 +131,7 @@ venv/Scripts/pip install -e .
|
|
132
131
|
# macOS/Linux
|
133
132
|
venv/bin/pip install -e .
|
134
133
|
```
|
134
|
+
安装时会自动构建 Dashboard 和 Client,请确保系统安装了 Node.js。
|
135
135
|
|
136
136
|
### 运行服务
|
137
137
|
|
@@ -142,43 +142,34 @@ neuro
|
|
142
142
|
# 指定工作目录
|
143
143
|
neuro -D /path/to/your/config
|
144
144
|
|
145
|
-
#
|
145
|
+
# 指定监听地址和端口
|
146
146
|
neuro -H 0.0.0.0 -P 8080
|
147
147
|
|
148
148
|
# 组合使用
|
149
149
|
neuro -D /path/to/your/config -H 0.0.0.0 -P 8080
|
150
150
|
```
|
151
151
|
|
152
|
-
|
152
|
+
手动指定的监听地址和端口会覆盖配置文件中的设置。
|
153
153
|
|
154
|
-
|
154
|
+
如果没有指定,服务默认遵循配置文件中的设置,运行在 `http://127.0.0.1:8000`。
|
155
155
|
|
156
|
-
|
156
|
+
## API 接口
|
157
157
|
|
158
|
-
- `/ws/admin`: 用于控制面板的管理接口,提供直播控制、配置管理、日志监控、Agent交互等所有功能,详细规范请参阅 `WEBSOCKET_API.md
|
159
|
-
- `/ws/stream`:
|
160
|
-
- `/api/system/health`:
|
161
|
-
- `/docs`: 自动生成的API文档 (Swagger UI)
|
158
|
+
- `/ws/admin`: 用于控制面板的管理接口,提供直播控制、配置管理、日志监控、Agent交互等所有功能,详细规范请参阅 `WEBSOCKET_API.md`。
|
159
|
+
- `/ws/stream`: 客户端使用的直播接口。
|
160
|
+
- `/api/system/health`: 健康检查接口。
|
162
161
|
|
163
162
|
## 配置说明
|
164
163
|
|
165
|
-
配置文件 `config.yaml`
|
166
|
-
|
167
|
-
- `api_keys` - 各种服务的 API 密钥
|
168
|
-
- `stream_metadata` - 直播元数据(标题、分类、标签等)
|
169
|
-
- `neuro_behavior` - Neuro 行为设置
|
170
|
-
- `audience_simulation` - 观众模拟设置
|
171
|
-
- `tts` - TTS 语音合成设置
|
172
|
-
- `performance` - 性能相关设置
|
173
|
-
- `server` - 服务器设置(主机、端口、CORS 等)
|
164
|
+
配置文件 `config.yaml` 现在一般无需手动编辑,所有配置项在管理面板中均可进行可视化配置。
|
174
165
|
|
175
166
|
有关配置文件的完整示例,请参阅项目根目录下的 `docs/working_dir_example/` 文件夹
|
176
167
|
|
177
168
|
## 安全说明
|
178
169
|
|
179
|
-
|
180
|
-
|
181
|
-
|
170
|
+
服务端具有 CORS 配置,仅允许预配置的来源访问。如果莫名其妙地连不上服务端(尤其是在外网环境中),可以检查和更改此项设置。
|
171
|
+
|
172
|
+
**公网部署请务必更改管理密钥**,建议更改端口为非常规端口以避免爆破。咱这个程序只是能用就行的水平,永远不要相信它的安保性能。
|
182
173
|
|
183
174
|
## 故障排除
|
184
175
|
|
@@ -186,3 +177,7 @@ neuro -D /path/to/your/config -H 0.0.0.0 -P 8080
|
|
186
177
|
- 检查网络连接是否正常
|
187
178
|
- 查看日志文件获取错误信息
|
188
179
|
- 确保端口未被其他程序占用
|
180
|
+
|
181
|
+
> 都是些 AI 生成的垃圾话,看看就好
|
182
|
+
|
183
|
+
*作为看这篇💩文档的奖励,如果你需要使用外部面板而不是服务器自托管面板,可以直接使用我部署的 https://dashboard.neuro.jiahui.cafe 连接到你的服务端,但是不保证始终能用,而且请配置好 CORS*
|
@@ -0,0 +1 @@
|
|
1
|
+
:root{--twitch-purple: #9147FF;--twitch-dark-bg: #F2F2F2;--twitch-chat-bg: #FFFFFF;--twitch-text-color: #18181B;--twitch-secondary-text-color: #6A6A6D;--twitch-border-color: #D3D3D3;--twitch-hover-bg: #EFEFF1;--twitch-button-bg: #EFEFF1;--twitch-button-text-color: #0E0E10;--twitch-button-hover-bg: #DFDFE1;--twitch-primary-button-bg: var(--twitch-purple);--twitch-primary-button-text-color: #FFFFFF;--twitch-primary-button-hover-bg: #772CE8;--twitch-tag-bg: #EFEFF1;--twitch-tag-text-color: #6A6A6D;--twitch-live-red: #EB0400;--twitch-viewer-red: #971311;--sidebar-width: 340px;--header-height: 50px;--neuro-shadow-color: #32003C;--neuro-avatar-display-width-percent: 50%;--fast-transition: background-color .2s, color .2s;--twitch-dark-button-bg: #333333;--twitch-dark-button-hover-bg: #444444;--twitch-dark-button-text-color: #FFFFFF;--sc-pink-bg-color: #f68795;--sc-purple-bg-color: #b97ad5}@font-face{font-family:First Coffee;src:url(/fonts/first-coffee.woff2) format("woff2");font-weight:400;font-style:normal}@font-face{font-family:Comic Sans MS;src:url(/fonts/comic.woff2) format("woff2");font-weight:400;font-style:normal}@font-face{font-family:Noto Sans SC;src:url(/fonts/noto-sans-sc.woff2) format("woff2");font-weight:400;font-style:normal}@font-face{font-family:Causten;src:url(/fonts/causten.woff2) format("woff2");font-weight:400;font-style:normal}html,body{height:100%;margin:0;padding:0;overflow:hidden}body{font-family:Inter,Arial,sans-serif;display:flex;flex-direction:column;background-color:var(--twitch-dark-bg);color:var(--twitch-text-color)}.twitch-button{display:inline-flex;align-items:center;justify-content:center;gap:6px;padding:0 10px;border-radius:100px;font-size:.9em;font-weight:600;cursor:pointer;border:none;transition:var(--fast-transition);outline:none;background-color:var(--twitch-button-bg);color:var(--twitch-button-text-color);height:32px;box-sizing:border-box;line-height:1}.twitch-button svg{width:1.2em;height:1.2em;fill:currentColor;vertical-align:middle}.twitch-button span{padding-top:2px}.twitch-button:hover:not(:disabled){background-color:var(--twitch-button-hover-bg)}.twitch-button.subscribe-button{background-color:var(--twitch-primary-button-bg);color:var(--twitch-primary-button-text-color)}.twitch-button.subscribe-button:hover:not(:disabled){background-color:var(--twitch-primary-button-hover-bg)}.twitch-button.icon-button{width:32px;padding:0;border-radius:50%}.nav-icon-button{display:inline-flex;align-items:center;justify-content:center;gap:6px;padding:0;border-radius:50%;font-size:.9em;font-weight:600;cursor:pointer;border:none;transition:var(--fast-transition);outline:none;background-color:transparent;color:var(--twitch-button-text-color);width:32px;height:32px;box-sizing:border-box;line-height:1}.nav-icon-button:hover:not(:disabled){background-color:var(--twitch-button-hover-bg);border-radius:50%}.nav-icon-button svg{width:20px;height:20px;fill:currentColor;vertical-align:middle}.search-button{display:inline-flex;align-items:center;justify-content:center;gap:6px;padding:0;font-size:.9em;font-weight:600;cursor:pointer;border:none;transition:var(--fast-transition);outline:none;background-color:transparent;color:var(--twitch-button-text-color);width:32px;height:32px;box-sizing:border-box;line-height:1}.search-button:hover:not(:disabled){background-color:var(--twitch-button-hover-bg)}.search-button svg{width:20px;height:20px;fill:currentColor;vertical-align:middle}.chat-toolbar-button{display:inline-flex;align-items:center;justify-content:center;gap:6px;padding:0;border-radius:50%;font-size:.9em;font-weight:600;cursor:pointer;border:none;transition:var(--fast-transition);outline:none;background-color:transparent;color:var(--twitch-button-text-color);width:32px;height:32px;box-sizing:border-box;line-height:1}.chat-toolbar-button:hover:not(:disabled){background-color:var(--twitch-button-hover-bg);border-radius:50%}.chat-toolbar-button svg{width:20px;height:20px;fill:currentColor;vertical-align:middle}.chat-toolbar-button img{width:20px;height:20px;object-fit:contain}.video-overlay-chat-button{display:inline-flex;align-items:center;justify-content:center;gap:6px;padding:0;border-radius:50%;font-size:.9em;font-weight:600;cursor:pointer;border:none;transition:var(--fast-transition);outline:none;background-color:#0009;color:#fff;width:32px;height:32px;box-sizing:border-box;line-height:1}.video-overlay-chat-button:hover:not(:disabled){background-color:#000c;border-radius:50%}.video-overlay-chat-button svg{width:20px;height:20px;fill:#fff;vertical-align:middle}.mute-button{display:inline-flex;align-items:center;justify-content:center;gap:6px;padding:0;border-radius:50%;font-size:.9em;font-weight:600;cursor:pointer;border:none;transition:var(--fast-transition);outline:none;background-color:#0009;color:#fff;width:32px;height:32px;box-sizing:border-box;line-height:1}.mute-button:hover:not(:disabled){background-color:#000c;border-radius:50%}.mute-button svg{width:20px;height:20px;fill:#fff;vertical-align:middle}.twitch-button.dark{background-color:var(--twitch-dark-button-bg);color:var(--twitch-dark-button-text-color)}.twitch-button.dark:hover:not(:disabled){background-color:var(--twitch-dark-button-hover-bg)}.twitch-button.dark svg,.twitch-button.dark img{fill:var(--twitch-dark-button-text-color)}.twitch-button img{width:20px;height:20px;object-fit:contain;vertical-align:middle}.chat-toolbar-text-button{display:inline-flex;align-items:center;justify-content:center;gap:6px;padding:0 10px;border-radius:100px;font-size:.9em;font-weight:600;cursor:pointer;border:none;transition:var(--fast-transition);outline:none;background-color:transparent;color:var(--twitch-button-text-color);height:32px;box-sizing:border-box;line-height:1}.chat-toolbar-text-button:hover:not(:disabled){background-color:var(--twitch-button-hover-bg)}.chat-toolbar-text-button svg,.chat-toolbar-text-button img{width:1.2em;height:1.2em;fill:currentColor;vertical-align:middle;object-fit:contain}.chat-toolbar-text-button .points-value{padding-top:2px}.chat-toolbar-text-button .channel-points-icon{width:1.2em;height:1.2em;object-fit:contain}#twitch-header{height:var(--header-height);background-color:var(--twitch-chat-bg);display:flex;justify-content:space-between;align-items:center;padding:0 1rem;border-bottom:1px solid var(--twitch-border-color);flex-shrink:0;z-index:100;color:var(--twitch-text-color);gap:1rem}.top-nav-left,.top-nav-right{display:flex;align-items:center;gap:.5rem;flex-shrink:0}.top-nav-center{flex-grow:1;flex-shrink:1;display:flex;justify-content:center;min-width:150px}.twitch-logo-link{display:flex;align-items:center;text-decoration:none;margin-left:.5rem}.twitch-logo-container{margin:0;transform:scaleY(-1);transition:transform .3s ease-out}.twitch-logo-container:hover{transform:scaleY(1)}.twitch-logo-svg .logo-body{fill:var(--twitch-purple)}.twitch-logo-svg .logo-face{fill:var(--twitch-chat-bg)}.twitch-logo-svg .logo-eye-path{fill:var(--twitch-purple)}.nav-links-main{display:flex;gap:1.5rem;margin-left:1.5rem}.nav-link{display:flex;align-items:center;gap:.5rem;text-decoration:none;color:var(--twitch-text-color);font-weight:500;font-size:1.1rem;padding:.25rem .5rem;border-radius:4px;transition:var(--fast-transition)}.nav-link:hover{color:var(--twitch-purple)}.nav-link p{margin:0}.search-container{display:flex;align-items:center;width:100%;max-width:400px;border:1px solid var(--twitch-border-color);border-radius:8px;overflow:hidden;background-color:var(--twitch-button-bg)}.search-input{flex:1 1 0;min-width:50px;padding:.5rem .75rem;border:none;background-color:transparent;color:var(--twitch-text-color);font-size:.95rem;outline:none}.search-input::placeholder{color:var(--twitch-secondary-text-color)}.search-button{flex-shrink:0;display:flex;align-items:center;justify-content:center;padding:0 .5rem}.search-button svg{fill:var(--twitch-text-color)}.top-nav-right{gap:.75rem}.nav-user-avatar-button{background:none;border:none;cursor:pointer;padding:0;border-radius:50%;width:32px;height:32px;overflow:hidden;flex-shrink:0}.user-avatar-wrapper{width:100%;height:100%;display:flex;align-items:center;justify-content:center}.user-avatar-img{width:100%;height:100%;object-fit:cover;border-radius:50%}#main-content-wrapper{flex-grow:1;display:flex;overflow:hidden;position:relative}#stream-and-info-container{flex-grow:1;flex-shrink:1;display:flex;flex-direction:column;overflow-y:auto;min-height:0;scrollbar-width:none}#stream-and-info-container::-webkit-scrollbar{display:none}#stream-display-viewport{flex-grow:1;background-color:#000;display:flex;justify-content:center;align-items:center;overflow:hidden;min-height:0;position:relative}#mute-button{position:absolute;bottom:1rem;left:1rem;z-index:60}#stream-display-area{position:relative;background-color:#000;overflow:hidden;flex-shrink:0;visibility:hidden;opacity:0;transition:opacity .3s ease-in-out;container-type:size;container-name:streamArea}#background-display,#startup-video-overlay{position:absolute;top:0;left:0;width:100%;height:100%}#background-display{z-index:0;pointer-events:none;background:url(/background.webp) center/cover no-repeat}#neuro-static-avatar-container{position:absolute;left:70%;transform:translate(-50%);width:var(--neuro-avatar-display-width-percent);height:auto;overflow:visible;pointer-events:none;z-index:15;visibility:hidden;transition:transform .5s ease-in-out}#neuro-static-avatar-container img{width:100%;height:auto;display:block}.spin-animation{animation:spin-counter-clockwise .75s linear}@keyframes spin-counter-clockwise{0%{transform:translate(-50%) rotate(0)}to{transform:translate(-50%) rotate(-360deg)}}#neuro-static-avatar-container.zoom-in{transform:translate(-50%) scale(1.15)}#neuro-caption{position:absolute;bottom:5%;left:50%;transform:translate(-50%);width:90%;padding:10px 20px;border-radius:10px;text-align:center;font-weight:400;opacity:1;pointer-events:none;z-index:20;color:#fff;transition:none;text-shadow:0 0 4px var(--neuro-shadow-color),0 0 4px var(--neuro-shadow-color),0 0 4px var(--neuro-shadow-color);font-family:First Coffee,Noto Sans SC;font-size:3cqi}#neuro-caption:not(.show){opacity:0}#startup-video-overlay{background-color:#000;z-index:10;display:flex;justify-content:center;align-items:center;opacity:1;transition:none}#startup-video-overlay.hidden{opacity:0;pointer-events:none;visibility:hidden}#startup-video{width:100%;height:100%;object-fit:contain}#show-chat-button{position:absolute;top:1rem;right:1rem;z-index:60;background-color:#0009;border:none;border-radius:50%;padding:0;cursor:pointer;display:flex;align-items:center;justify-content:center;color:#fff;transition:background-color .2s,opacity .3s ease-in-out,visibility .3s ease-in-out;opacity:0;visibility:hidden;pointer-events:none;width:32px;height:32px;box-sizing:border-box}#show-chat-button:hover{background-color:#000c}#show-chat-button svg{width:20px;height:20px;fill:currentColor}body.chat-collapsed #show-chat-button{opacity:1;visibility:visible;pointer-events:auto}#twitch-chat-overlay{position:absolute;top:0;left:0;width:25%;height:50%;background-color:transparent;color:#fff;padding:10px;overflow-y:auto;z-index:5;font-size:1.5cqi;scrollbar-width:none;font-family:Comic Sans MS,Noto Sans SC;pointer-events:none;-webkit-user-select:none;user-select:none;scroll-behavior:auto}#twitch-chat-overlay::-webkit-scrollbar{display:none}#twitch-chat-overlay *{pointer-events:none;-webkit-user-select:none;user-select:none}#twitch-chat-overlay .chat-line__message{padding:2px 0;margin:0;text-shadow:-1px -1px 0 #00000080,1px -1px 0 #00000080,-1px 1px 0 #00000080,1px 1px 0 #00000080,2px 2px 0 rgba(0,0,0,.5);font-size:1.5cqi}#twitch-chat-overlay .chat-line__username{font-weight:700;margin-right:.25rem;font-size:1.5cqi}#twitch-chat-overlay .text-fragment{font-weight:700;color:#fff;font-size:1.5cqi}#twitch-chat-overlay .chat-author__display-name{font-size:1.5cqi}#twitch-chat-overlay .chat-line__message-container>span:not(.chat-line__username){font-weight:700;color:#fff;text-shadow:-1px -1px 0 #00000080,1px -1px 0 #00000080,-1px 1px 0 #00000080,1px 1px 0 #00000080,2px 2px 0 rgba(0,0,0,.5);font-size:1.5cqi}#stream-display-viewport *:not(.mute-button):not(#show-chat-button){pointer-events:none;user-select:none;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none}#stream-info{padding:10px 20px;background-color:var(--twitch-chat-bg);border-bottom:1px solid var(--twitch-border-color)}.stream-info-layout{display:flex;gap:16px;align-items:center}.stream-info-left-column{flex-shrink:0}.streamer-avatar-link{display:block;position:relative}.streamer-avatar-wrapper{position:relative}#streamer-avatar{width:64px;height:64px;border-radius:50%;border:2px solid transparent;transition:border-color .2s}.streamer-avatar-link:hover #streamer-avatar{border-color:var(--twitch-purple)}.live-indicator-wrapper{position:absolute;bottom:-2px;left:50%;transform:translate(-50%);z-index:2}.live-indicator-rect{background-color:var(--twitch-live-red);color:#fff;font-size:.7em;font-weight:600;padding:2px 6px;border-radius:4px;white-space:nowrap;border:2px solid var(--twitch-chat-bg);text-transform:uppercase}.stream-info-right-column{display:flex;flex-direction:column;flex-grow:1;gap:4px;min-width:0}.stream-info-main-row{display:flex;justify-content:space-between;align-items:center}.streamer-info-and-name{display:flex;align-items:center;gap:6px;min-width:0}#streamer-nickname{margin:0;font-size:1.2em;color:var(--twitch-text-color);font-weight:500;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.verified-badge svg{width:16px;height:16px;fill:var(--twitch-purple)}.main-action-buttons{display:flex;gap:6px;align-items:center;flex-shrink:0}#stream-info .twitch-button{display:inline-flex;align-items:center;justify-content:center;height:30px;padding:6px 10px;box-sizing:border-box}#stream-info .twitch-button.follow-button,#stream-info .twitch-button.icon-button{width:30px;padding:0}#stream-info .twitch-button:not(.follow-button):not(.icon-button){padding-top:0;padding-bottom:1px}#stream-info .twitch-button svg{width:18px;height:18px}.stream-info-details-row{display:flex;justify-content:space-between;align-items:flex-start;gap:12px}.stream-details-left{display:flex;flex-direction:column;gap:6px;min-width:0}#stream-title-full{margin:0;font-size:.9em;font-weight:600;color:var(--twitch-text-color);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.stream-category-and-tags{display:flex;align-items:center;gap:6px;min-width:0}.stream-category{font-size:.9em;color:var(--twitch-purple);text-decoration:none;font-weight:500;transition:color .2s;flex-shrink:0}.stream-category:hover{text-decoration:underline}.stream-tags{display:flex;gap:6px;align-items:center;overflow-x:auto;scrollbar-width:none;-ms-overflow-style:none}.stream-tags::-webkit-scrollbar{display:none}.stream-tag{display:inline-flex;align-items:center;padding:3px 8px;border-radius:100px;background-color:var(--twitch-tag-bg);color:var(--twitch-tag-text-color);font-size:.75em;font-weight:600;text-decoration:none;transition:background-color .2s;height:20px;box-sizing:border-box;flex-shrink:0}.stream-tag:hover{background-color:var(--twitch-button-hover-bg)}.stream-details-right{display:flex;align-items:center;gap:12px;flex-shrink:0}.stream-stats-section{display:flex;gap:0px;align-items:center;font-size:.9em;color:var(--twitch-secondary-text-color);justify-content:flex-end}.viewer-count,.stream-duration{display:flex;align-items:center;gap:4px}.viewer-count{color:var(--twitch-viewer-red)}.viewer-count svg{width:18px;height:18px;fill:currentColor}.viewer-count strong{font-weight:600}.stream-duration .duration-text{font-weight:500;color:var(--twitch-text-color);min-width:4.5em;text-align:right}.stream-secondary-actions{display:flex;gap:6px}.stream-secondary-actions .twitch-button{background-color:transparent}.stream-secondary-actions .twitch-button:hover:not(:disabled){background-color:var(--twitch-button-hover-bg)}#chat-sidebar{width:var(--sidebar-width);min-width:var(--sidebar-width);background-color:var(--twitch-chat-bg);flex-shrink:0;display:flex;flex-direction:column;border-left:1px solid var(--twitch-border-color);z-index:50;height:100%;transition:width .3s ease-in-out,min-width .3s ease-in-out}#chat-sidebar.collapsed{width:0;min-width:0;overflow:hidden;visibility:hidden;pointer-events:none}.chat-sidebar-header{height:3.5rem;display:flex;justify-content:space-between;align-items:center;padding:0 1rem;border-bottom:1px solid var(--twitch-border-color);flex-shrink:0;position:relative}.chat-header-left,.chat-header-right{display:flex;align-items:center;gap:.5rem;z-index:1}.chat-sidebar-header .chat-title{position:absolute;left:50%;transform:translate(-50%);margin:0;font-size:1rem;font-weight:600;color:var(--twitch-text-color)}.chat-toggle-button{display:flex;align-items:center;justify-content:center;background:none;border:none;border-radius:50%;padding:0;cursor:pointer;color:var(--twitch-text-color);transition:background-color .2s;width:32px;height:32px;box-sizing:border-box}.chat-toggle-button:hover{background-color:var(--twitch-hover-bg)}.chat-toggle-button svg{width:20px;height:20px;fill:currentColor}#chat-sidebar.collapsed .chat-toggle-button{transform:rotate(180deg)}.messages-display{flex-grow:1;overflow-y:auto;padding:.5rem 1rem;min-height:0;scrollbar-width:thin;scrollbar-color:#B0B0B0 var(--twitch-hover-bg)}.messages-display::-webkit-scrollbar{width:8px}.messages-display::-webkit-scrollbar-track{background:var(--twitch-hover-bg);border-radius:4px}.messages-display::-webkit-scrollbar-thumb{background:#b0b0b0;border-radius:4px}.messages-display::-webkit-scrollbar-thumb:hover{background:#888}.chat-line__message{display:flex;align-items:flex-start;padding:.3rem 0;line-height:1.5;word-wrap:break-word;color:var(--twitch-text-color);border-radius:4px}.chat-line__message:hover{background-color:#ffffff0d}.chat-line__message-container{display:inline}.chat-line__username{font-weight:700;display:inline-block;margin-right:.1rem;cursor:pointer}.chat-author__display-name{font-size:.9rem}.text-fragment{font-size:.9rem;color:var(--twitch-text-color)}.chat-line__message.user-sent-message{background-color:#9147ff1a;border-left:3px solid var(--twitch-purple);padding-left:.75rem}.chat-line__message.system-message{color:var(--twitch-secondary-text-color);font-style:italic;font-size:.85rem;justify-content:center}.chat-line__message.system-message .chat-line__username,.chat-line__message.system-message .text-fragment{font-size:inherit;color:inherit;font-weight:400}.chat-input-area{padding:.75rem 1rem;border-top:1px solid var(--twitch-border-color);flex-shrink:0}.chat-input-and-buttons{display:flex;flex-direction:column;gap:.5rem}.chat-input-textarea-container{display:flex;align-items:center;border:1px solid var(--twitch-border-color);border-radius:4px;background-color:var(--twitch-button-bg);padding:0 .5rem;transition:var(--fast-transition)}.chat-input-textarea-container:focus-within{border-color:var(--twitch-purple);box-shadow:0 0 0 2px #9147ff33}.chat-input-prefix-icons,.chat-input-suffix-icons{display:flex;align-items:center}.chat-input-wrapper{flex-grow:1}.chat-input-element{width:100%;height:2.25rem;padding:0 .5rem;border:none;background-color:transparent;color:var(--twitch-text-color);font-size:.95rem;outline:none}.chat-input-element::placeholder{color:var(--twitch-secondary-text-color)}.chat-input-buttons-container{display:flex;justify-content:space-between;align-items:center}.chat-buttons-left,.chat-buttons-right{display:flex;align-items:center;gap:.5rem}.chat-points-display{display:flex;align-items:center;gap:.25rem;font-size:.85rem;color:var(--twitch-secondary-text-color)}.chat-points-display svg,.chat-points-display .channel-points-icon{width:18px;height:18px;fill:currentColor;object-fit:contain}.chat-points-display .points-value{color:var(--twitch-text-color);font-weight:600}#send-button:disabled{background-color:#b0b0b0;cursor:not-allowed}.chat-toolbar-text-button.selected{background-color:var(--twitch-purple-light);border:1px solid var(--twitch-purple);box-shadow:0 0 5px var(--twitch-purple-light)}#highlight-message-overlay{position:absolute;left:41%;transform:translate(-50%) translateY(-200%);width:24vw;height:auto;z-index:100;transition:transform 1s}#highlight-message-overlay.is-visible{transform:translate(-50%) translateY(0);transition-timing-function:cubic-bezier(.25,1,.5,1)}.sc-background-image{width:100%;height:auto;display:block;border-radius:20px}.sc-content{position:absolute;top:0;left:0;width:100%;height:100%;z-index:2;color:#fff;font-family:Causten}.sc-user{position:absolute;top:20%;left:40%;transform:translate(-50%);font-size:2cqi;text-align:left}.sc-message{position:absolute;top:60%;left:50%;transform:translate(-50%,-50%);width:90%;font-size:2cqi;text-align:center;overflow-wrap:break-word}.settings-modal-container{position:fixed;top:0;left:0;width:100%;height:100%;z-index:1000;display:flex;justify-content:center;align-items:center}.settings-modal-container.hidden{display:none}.modal-overlay{position:absolute;top:0;left:0;width:100%;height:100%;background-color:#0009}.modal-content{position:relative;background-color:var(--twitch-chat-bg);color:var(--twitch-text-color);border-radius:8px;width:90%;max-width:450px;box-shadow:0 4px 20px #0003;display:flex;flex-direction:column}.modal-header{display:flex;justify-content:space-between;align-items:center;padding:1rem 1.5rem;border-bottom:1px solid var(--twitch-border-color)}.modal-header h2{margin:0;font-size:1.25rem}.modal-body{padding:1.5rem;display:flex;flex-direction:column;gap:1.5rem}.setting-item{display:flex;flex-direction:column;gap:.5rem}.setting-item label{font-weight:600;font-size:.9rem}.modal-input{padding:.75rem;border:1px solid var(--twitch-border-color);border-radius:4px;background-color:var(--twitch-button-bg);color:var(--twitch-text-color);font-size:1rem}.modal-input:focus{outline:none;border-color:var(--twitch-purple);box-shadow:0 0 0 2px #9147ff33}.avatar-setting .avatar-preview-container{display:flex;align-items:center;gap:1rem}.avatar-preview{width:60px;height:60px;border-radius:50%;object-fit:cover;border:2px solid var(--twitch-border-color)}.avatar-upload-input{display:none}.modal-footer{padding:1rem 1.5rem;border-top:1px solid var(--twitch-border-color);display:flex;justify-content:flex-end}.setting-description{font-size:.8rem;color:var(--twitch-secondary-text-color);margin:4px 0 0}.turbo-button-icon{display:none}.turbo-button-full{display:inline-flex}@media (max-width: 992px){.turbo-button-full{display:none}.turbo-button-icon{display:inline-flex}}@media (max-width: 767px){body,#main-content-wrapper{flex-direction:column}#stream-and-info-container{width:100%}#chat-sidebar{width:100%;height:40vh;min-height:200px;min-width:unset;border-left:none;border-top:1px solid var(--twitch-border-color)}#chat-sidebar.collapsed{height:0;min-height:0;overflow:hidden;visibility:hidden;pointer-events:none}.nav-links-main,button[aria-label=Prime],button[aria-label=打开通知],button[aria-label=悄悄话],button[aria-label=购买呼币],button[aria-label=免费体验无广告观赏],button#show-chat-button,button[aria-label=通知],button.twitch-button[aria-label=购买呼币],button.twitch-button[aria-label=赠送一次订阅],.stream-details-right,.chat-sidebar-header{display:none!important}*{scrollbar-width:none!important}*::-webkit-scrollbar{display:none!important}*::-webkit-scrollbar-thumb{display:none!important}*::-webkit-scrollbar-track{display:none!important}#streamer-avatar{width:50px;height:50px}}.tooltip{position:absolute;background:#18181b;color:#fff;padding:6px 10px;font-size:12px;border-radius:4px;pointer-events:none;opacity:0;transition:opacity .2s ease;white-space:nowrap;z-index:1000;transform-origin:center bottom;-webkit-user-select:none;user-select:none}.tooltip.show{opacity:1}.tooltip:after{content:"";position:absolute;left:50%;transform:translate(-50%);border-width:6px;border-style:solid;top:100%;border-color:#18181b transparent transparent transparent;transition:border-color .2s ease,top .2s ease}.tooltip.tooltip-arrow-up:after{top:-12px;border-color:transparent transparent #18181b transparent}.hidden{display:none!important}#offline-content-container{display:flex;flex-direction:row;align-items:center;justify-content:center;flex-wrap:wrap;width:100%;min-height:500px;flex-grow:1;background-image:url(/banner.jpeg);background-size:cover;background-position:center;background-repeat:no-repeat;padding:2rem;gap:2rem;box-sizing:border-box}.offline-info-card{display:flex;flex-direction:column;align-items:flex-start;background-color:#fff;padding:1.5rem;border-radius:0;color:#0e0e10;box-shadow:0 4px 10px #0000001a;box-sizing:border-box;flex:0 1 270px}.offline-status-section{display:flex;flex-direction:column;justify-content:center;align-items:flex-start;flex-grow:1;gap:.75rem}.offline-status-badge{background-color:#000;color:#fff;font-size:.9em;font-weight:600;padding:2px 6px;border-radius:4px;white-space:nowrap;text-transform:uppercase}.offline-status-title{font-size:1.6rem;font-weight:600;margin:0}.offline-notification-section{width:100%;margin-left:-10px}.offline-notification-button{background-color:transparent;color:var(--twitch-primary-button-hover-bg);justify-content:flex-start}.offline-notification-button:hover{background-color:var(--twitch-button-hover-bg);color:var(--twitch-button-text-color)}.offline-notification-button svg{fill:currentColor}.offline-video-player{aspect-ratio:16 / 9;flex:0 1 576px;min-width:300px;max-width:576px}@font-face{font-family:Inter;font-style:normal;font-display:swap;font-weight:400;src:url(/assets/inter-cyrillic-ext-400-normal-Dc4VJyIJ.woff2) format("woff2"),url(/assets/inter-cyrillic-ext-400-normal-BE2fNs0E.woff) format("woff");unicode-range:U+0460-052F,U+1C80-1C8A,U+20B4,U+2DE0-2DFF,U+A640-A69F,U+FE2E-FE2F}@font-face{font-family:Inter;font-style:normal;font-display:swap;font-weight:400;src:url(/assets/inter-cyrillic-400-normal-BLGc9T1a.woff2) format("woff2"),url(/assets/inter-cyrillic-400-normal-alAqRL36.woff) format("woff");unicode-range:U+0301,U+0400-045F,U+0490-0491,U+04B0-04B1,U+2116}@font-face{font-family:Inter;font-style:normal;font-display:swap;font-weight:400;src:url(/assets/inter-greek-ext-400-normal-Bput3-QP.woff2) format("woff2"),url(/assets/inter-greek-ext-400-normal-XIH6-K3k.woff) format("woff");unicode-range:U+1F00-1FFF}@font-face{font-family:Inter;font-style:normal;font-display:swap;font-weight:400;src:url(/assets/inter-greek-400-normal-DxZsaF_h.woff2) format("woff2"),url(/assets/inter-greek-400-normal-C3I71FoW.woff) format("woff");unicode-range:U+0370-0377,U+037A-037F,U+0384-038A,U+038C,U+038E-03A1,U+03A3-03FF}@font-face{font-family:Inter;font-style:normal;font-display:swap;font-weight:400;src:url(/assets/inter-vietnamese-400-normal-DMkecbls.woff2) format("woff2"),url(/assets/inter-vietnamese-400-normal-Bbgyi5SW.woff) format("woff");unicode-range:U+0102-0103,U+0110-0111,U+0128-0129,U+0168-0169,U+01A0-01A1,U+01AF-01B0,U+0300-0301,U+0303-0304,U+0308-0309,U+0323,U+0329,U+1EA0-1EF9,U+20AB}@font-face{font-family:Inter;font-style:normal;font-display:swap;font-weight:400;src:url(/assets/inter-latin-ext-400-normal-C1nco2VV.woff2) format("woff2"),url(/assets/inter-latin-ext-400-normal-77YHD8bZ.woff) format("woff");unicode-range:U+0100-02BA,U+02BD-02C5,U+02C7-02CC,U+02CE-02D7,U+02DD-02FF,U+0304,U+0308,U+0329,U+1D00-1DBF,U+1E00-1E9F,U+1EF2-1EFF,U+2020,U+20A0-20AB,U+20AD-20C0,U+2113,U+2C60-2C7F,U+A720-A7FF}@font-face{font-family:Inter;font-style:normal;font-display:swap;font-weight:400;src:url(/assets/inter-latin-400-normal-C38fXH4l.woff2) format("woff2"),url(/assets/inter-latin-400-normal-CyCys3Eg.woff) format("woff");unicode-range:U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+0304,U+0308,U+0329,U+2000-206F,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD}
|
@@ -2,11 +2,11 @@
|
|
2
2
|
<html lang="en">
|
3
3
|
<head>
|
4
4
|
<meta charset="UTF-8" />
|
5
|
-
<link rel="icon" type="image/x-icon" href="
|
5
|
+
<link rel="icon" type="image/x-icon" href="/favicon.ico" />
|
6
6
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
7
7
|
<title>𝐯𝐞𝐝𝐚𝐥𝟗𝟖𝟕 - 𝐓𝐰𝐢𝐭𝐜𝐡</title>
|
8
|
-
<script type="module" crossorigin src="
|
9
|
-
<link rel="stylesheet" crossorigin href="
|
8
|
+
<script type="module" crossorigin src="/assets/index-BHgW05Wx.js"></script>
|
9
|
+
<link rel="stylesheet" crossorigin href="/assets/index-rmyQl8Tr.css">
|
10
10
|
</head>
|
11
11
|
<body>
|
12
12
|
<header id="twitch-header">
|
@@ -69,7 +69,7 @@
|
|
69
69
|
|
70
70
|
<button class="nav-user-avatar-button" aria-expanded="false" aria-label="用户菜单">
|
71
71
|
<div class="user-avatar-wrapper">
|
72
|
-
<img class="user-avatar-img" alt="用户头像" src="
|
72
|
+
<img class="user-avatar-img" alt="用户头像" src="/user_avatar.jpg" style="object-fit: cover;">
|
73
73
|
</div>
|
74
74
|
</button>
|
75
75
|
</div>
|
@@ -81,11 +81,11 @@
|
|
81
81
|
<div id="stream-display-area">
|
82
82
|
<div id="background-display" data-darkreader-ignore></div>
|
83
83
|
<div id="neuro-static-avatar-container">
|
84
|
-
<img id="neuro-static-avatar" src="
|
84
|
+
<img id="neuro-static-avatar" src="/neurosama.png" alt="Neuro-Sama Static Avatar" />
|
85
85
|
</div>
|
86
86
|
<div id="neuro-caption"></div>
|
87
87
|
<div id="startup-video-overlay">
|
88
|
-
<video id="startup-video" src="
|
88
|
+
<video id="startup-video" src="/neuro_start.mp4" playsinline muted preload="auto"></video>
|
89
89
|
</div>
|
90
90
|
<!-- Twitch 风格聊天覆盖层 -->
|
91
91
|
<div id="twitch-chat-overlay" class="messages-display">
|
@@ -124,7 +124,7 @@
|
|
124
124
|
<div class="stream-info-left-column">
|
125
125
|
<a href="https://twitch.tv/vedal987" class="streamer-avatar-link">
|
126
126
|
<div class="streamer-avatar-wrapper">
|
127
|
-
<img id="streamer-avatar" src="
|
127
|
+
<img id="streamer-avatar" src="/avatar.webp" alt="Streamer Avatar" />
|
128
128
|
<div class="live-indicator-wrapper">
|
129
129
|
<div class="live-indicator-rect">
|
130
130
|
<span class="live-text" id="live-indicator-text">LIVE</span>
|
@@ -226,7 +226,7 @@
|
|
226
226
|
<div class="chat-input-textarea-container">
|
227
227
|
<div class="chat-input-prefix-icons">
|
228
228
|
<button class="chat-toolbar-button" aria-label="聊天室身份">
|
229
|
-
<img alt="订阅者徽章" class="chat-badge-icon" src="
|
229
|
+
<img alt="订阅者徽章" class="chat-badge-icon" src="/sub_badge.svg">
|
230
230
|
</button>
|
231
231
|
</div>
|
232
232
|
<div class="chat-input-wrapper">
|
@@ -248,7 +248,7 @@
|
|
248
248
|
<span class="points-value">0</span>
|
249
249
|
</button>
|
250
250
|
<button id="sc-points-button" class="chat-toolbar-text-button" aria-label="频道点数">
|
251
|
-
<img class="channel-points-icon" src="
|
251
|
+
<img class="channel-points-icon" src="/channel_points.png" alt="频道点数">
|
252
252
|
<span class="points-value">0</span>
|
253
253
|
</button>
|
254
254
|
</div>
|
@@ -282,7 +282,7 @@
|
|
282
282
|
<div class="setting-item avatar-setting">
|
283
283
|
<label>头像</label>
|
284
284
|
<div class="avatar-preview-container">
|
285
|
-
<img id="avatar-setting-preview" src="
|
285
|
+
<img id="avatar-setting-preview" src="/user_avatar.jpg" alt="Avatar Preview" class="avatar-preview">
|
286
286
|
<input type="file" id="avatar-setting-upload" accept="image/*" class="avatar-upload-input">
|
287
287
|
<button id="avatar-upload-button" class="twitch-button">上传图片</button>
|
288
288
|
</div>
|
@@ -68,6 +68,14 @@ app.add_middleware(
|
|
68
68
|
|
69
69
|
app.include_router(system_router)
|
70
70
|
|
71
|
+
|
72
|
+
# --- Redirect for trailing slash on dashboard ---
|
73
|
+
from fastapi.responses import RedirectResponse
|
74
|
+
@app.get("/dashboard", include_in_schema=False)
|
75
|
+
async def redirect_dashboard_to_trailing_slash():
|
76
|
+
return RedirectResponse(url="/dashboard/")
|
77
|
+
|
78
|
+
|
71
79
|
# --- Background Task Definitions ---
|
72
80
|
|
73
81
|
chatbot: ChatbotAgent = None
|
@@ -258,8 +266,16 @@ async def startup_event():
|
|
258
266
|
else:
|
259
267
|
frontend_dir = None
|
260
268
|
|
269
|
+
# --- Mount Dashboard Frontend ---
|
270
|
+
# Mount the dashboard frontend at /dashboard path (more specific) - MOUNT THIS FIRST
|
271
|
+
if frontend_dir:
|
272
|
+
app.mount("/dashboard", SPAStaticFiles(directory=frontend_dir, html=True), name="dashboard")
|
273
|
+
logger.info("Dashboard frontend mounted at /dashboard")
|
274
|
+
else:
|
275
|
+
logger.error("Frontend directory not found in either production or development locations.")
|
276
|
+
|
261
277
|
# --- Mount Client Frontend ---
|
262
|
-
# Mount the client frontend at /
|
278
|
+
# Mount the client frontend at / path (more general) - MOUNT THIS AFTER
|
263
279
|
try:
|
264
280
|
# Production/Standard install: find client frontend in the package
|
265
281
|
client_frontend_dir_traversable = files('neuro_simulator').joinpath('client')
|
@@ -277,19 +293,11 @@ async def startup_event():
|
|
277
293
|
client_frontend_dir = None
|
278
294
|
|
279
295
|
if client_frontend_dir:
|
280
|
-
app.mount("/
|
281
|
-
logger.info("Client frontend mounted at /
|
296
|
+
app.mount("/", SPAStaticFiles(directory=client_frontend_dir, html=True), name="client")
|
297
|
+
logger.info("Client frontend mounted at /")
|
282
298
|
else:
|
283
299
|
logger.error("Client frontend directory not found in either production or development locations.")
|
284
300
|
|
285
|
-
# --- Mount Dashboard Frontend ---
|
286
|
-
# Mount the dashboard frontend at / path (more general) - MOUNT THIS AFTER
|
287
|
-
if frontend_dir:
|
288
|
-
app.mount("/", SPAStaticFiles(directory=frontend_dir, html=True), name="dashboard")
|
289
|
-
logger.info("Dashboard frontend mounted at /")
|
290
|
-
else:
|
291
|
-
logger.error("Frontend directory not found in either production or development locations.")
|
292
|
-
|
293
301
|
# 1. Configure logging first
|
294
302
|
configure_server_logging()
|
295
303
|
|
@@ -1,2 +1,2 @@
|
|
1
|
-
const __vite__mapDeps=(i,m=__vite__mapDeps,d=(m.f||(m.f=["assets/ContextTab-
|
2
|
-
import{d as C,j as k,k as I,l as L,n as c,z as P,e as t,g as v,w as o,b as n,f as a,h as d,q as r,s as u,o as f,_ as D}from"./index-
|
1
|
+
const __vite__mapDeps=(i,m=__vite__mapDeps,d=(m.f||(m.f=["assets/ContextTab-Cv2a7xRF.js","assets/index-Sav9Djr5.js","assets/index-D2ait7Ff.css","assets/ContextTab-DyPsixHQ.css","assets/MemoryTab-BMDqRcHX.js","assets/MemoryTab-DPthi6jg.css","assets/ToolsTab-Ds4M2VeQ.js","assets/LogsTab-C7r21Vcz.js","assets/LogsTab-wg3i3S6b.css"])))=>i.map(i=>d[i]);
|
2
|
+
import{d as C,j as k,k as I,l as L,n as c,z as P,e as t,g as v,w as o,b as n,f as a,h as d,q as r,s as u,o as f,_ as D}from"./index-Sav9Djr5.js";const N={class:"agent-view-wrapper"},O={key:0,class:"overlay"},R={class:"overlay-content"},B=C({__name:"AgentView",setup(z){const g=r(()=>u(()=>import("./ContextTab-Cv2a7xRF.js"),__vite__mapDeps([0,1,2,3]))),b=r(()=>u(()=>import("./MemoryTab-BMDqRcHX.js"),__vite__mapDeps([4,1,2,5]))),V=r(()=>u(()=>import("./ToolsTab-Ds4M2VeQ.js"),__vite__mapDeps([6,1,2]))),w=r(()=>u(()=>import("./LogsTab-C7r21Vcz.js"),__vite__mapDeps([7,1,2,8]))),m=k(),s=I("context"),p=L(()=>m.config?.agent_type&&m.config.agent_type!=="builtin");return(S,e)=>{const x=n("v-icon"),l=n("v-tab"),y=n("v-tabs"),_=n("v-window-item"),E=n("v-window"),T=n("v-card-text"),A=n("v-card");return f(),c("div",N,[p.value?(f(),c("div",O,[v("div",R,[t(x,{size:"x-large",class:"mb-4"},{default:o(()=>[...e[2]||(e[2]=[a("mdi-link-variant",-1)])]),_:1}),e[3]||(e[3]=v("h2",{class:"text-h5"},"当前正在调用外部 Agent",-1)),e[4]||(e[4]=v("p",{class:"text-body-1"},"请前往相应平台进行控制",-1))])])):P("",!0),t(A,{disabled:p.value},{default:o(()=>[t(y,{modelValue:s.value,"onUpdate:modelValue":e[0]||(e[0]=i=>s.value=i),"bg-color":"primary",grow:""},{default:o(()=>[t(l,{value:"context"},{default:o(()=>[...e[5]||(e[5]=[a("对话",-1)])]),_:1}),t(l,{value:"memory"},{default:o(()=>[...e[6]||(e[6]=[a("记忆",-1)])]),_:1}),t(l,{value:"tools"},{default:o(()=>[...e[7]||(e[7]=[a("工具",-1)])]),_:1}),t(l,{value:"logs"},{default:o(()=>[...e[8]||(e[8]=[a("日志",-1)])]),_:1})]),_:1},8,["modelValue"]),t(T,null,{default:o(()=>[t(E,{modelValue:s.value,"onUpdate:modelValue":e[1]||(e[1]=i=>s.value=i)},{default:o(()=>[t(_,{value:"context"},{default:o(()=>[t(d(g))]),_:1}),t(_,{value:"memory"},{default:o(()=>[t(d(b))]),_:1}),t(_,{value:"tools"},{default:o(()=>[t(d(V))]),_:1}),t(_,{value:"logs"},{default:o(()=>[t(d(w))]),_:1})]),_:1},8,["modelValue"])]),_:1})]),_:1},8,["disabled"])])}}}),j=D(B,[["__scopeId","data-v-871d2dc1"]]);export{j as default};
|
@@ -1,2 +1,2 @@
|
|
1
|
-
const __vite__mapDeps=(i,m=__vite__mapDeps,d=(m.f||(m.f=["assets/ContextTab-
|
2
|
-
import{d as E,k as y,n as C,g as d,e as o,w as e,b as a,f as n,h as u,q as r,s as v,o as g,_ as A}from"./index-
|
1
|
+
const __vite__mapDeps=(i,m=__vite__mapDeps,d=(m.f||(m.f=["assets/ContextTab-Cv2a7xRF.js","assets/index-Sav9Djr5.js","assets/index-D2ait7Ff.css","assets/ContextTab-DyPsixHQ.css","assets/MemoryTab-BMDqRcHX.js","assets/MemoryTab-DPthi6jg.css","assets/ToolsTab-Ds4M2VeQ.js","assets/LogsTab-C7r21Vcz.js","assets/LogsTab-wg3i3S6b.css"])))=>i.map(i=>d[i]);
|
2
|
+
import{d as E,k as y,n as C,g as d,e as o,w as e,b as a,f as n,h as u,q as r,s as v,o as g,_ as A}from"./index-Sav9Djr5.js";const I={class:"chatbot-view-wrapper"},P={class:"overlay"},B={class:"overlay-content"},L=E({__name:"ChatBotView",setup(D){const i=r(()=>v(()=>import("./ContextTab-Cv2a7xRF.js"),__vite__mapDeps([0,1,2,3]))),p=r(()=>v(()=>import("./MemoryTab-BMDqRcHX.js"),__vite__mapDeps([4,1,2,5]))),f=r(()=>v(()=>import("./ToolsTab-Ds4M2VeQ.js"),__vite__mapDeps([6,1,2]))),c=r(()=>v(()=>import("./LogsTab-C7r21Vcz.js"),__vite__mapDeps([7,1,2,8]))),s=y("context");return(O,t)=>{const b=a("v-icon"),l=a("v-tab"),w=a("v-tabs"),_=a("v-window-item"),V=a("v-window"),x=a("v-card-text"),T=a("v-card");return g(),C("div",I,[d("div",P,[d("div",B,[o(b,{size:"x-large",class:"mb-4"},{default:e(()=>[...t[2]||(t[2]=[n("mdi-dev-to",-1)])]),_:1}),t[3]||(t[3]=d("h2",{class:"text-h5"},"Chatbot 控制开发中",-1)),t[4]||(t[4]=d("p",{class:"text-body-1"},"后端 API 尚未实现",-1))])]),o(T,null,{default:e(()=>[o(w,{modelValue:s.value,"onUpdate:modelValue":t[0]||(t[0]=m=>s.value=m),"bg-color":"primary",grow:""},{default:e(()=>[o(l,{value:"context"},{default:e(()=>[...t[5]||(t[5]=[n("对话",-1)])]),_:1}),o(l,{value:"memory"},{default:e(()=>[...t[6]||(t[6]=[n("记忆",-1)])]),_:1}),o(l,{value:"tools"},{default:e(()=>[...t[7]||(t[7]=[n("工具",-1)])]),_:1}),o(l,{value:"logs"},{default:e(()=>[...t[8]||(t[8]=[n("日志",-1)])]),_:1})]),_:1},8,["modelValue"]),o(x,null,{default:e(()=>[o(V,{modelValue:s.value,"onUpdate:modelValue":t[1]||(t[1]=m=>s.value=m)},{default:e(()=>[o(_,{value:"context"},{default:e(()=>[o(u(i))]),_:1}),o(_,{value:"memory"},{default:e(()=>[o(u(p))]),_:1}),o(_,{value:"tools"},{default:e(()=>[o(u(f))]),_:1}),o(_,{value:"logs"},{default:e(()=>[o(u(c))]),_:1})]),_:1},8,["modelValue"])]),_:1})]),_:1})])}}}),k=A(L,[["__scopeId","data-v-ed63d404"]]);export{k as default};
|
@@ -1,2 +1,2 @@
|
|
1
|
-
const __vite__mapDeps=(i,m=__vite__mapDeps,d=(m.f||(m.f=["assets/FieldRenderer-
|
2
|
-
import{d as U,i as E,j as G,a as $,k as b,l as D,m as I,n as l,e as u,g as O,b as r,t as h,h as p,w as d,F as V,p as x,f as B,o as n,c as j,q as M,s as P}from"./index-
|
1
|
+
const __vite__mapDeps=(i,m=__vite__mapDeps,d=(m.f||(m.f=["assets/FieldRenderer-xMOXYQeU.js","assets/index-Sav9Djr5.js","assets/index-D2ait7Ff.css"])))=>i.map(i=>d[i]);
|
2
|
+
import{d as U,i as E,j as G,a as $,k as b,l as D,m as I,n as l,e as u,g as O,b as r,t as h,h as p,w as d,F as V,p as x,f as B,o as n,c as j,q as M,s as P}from"./index-Sav9Djr5.js";const R={key:0,class:"text-center pa-10"},T={class:"mt-4"},q={key:1},W={class:"text-h4 mb-6"},J=U({__name:"ConfigView",setup(z){const{t:_}=E(),F=M(()=>P(()=>import("./FieldRenderer-xMOXYQeU.js"),__vite__mapDeps([0,1,2]))),f=G(),S=$(),k=b(!0),m=b(null),C=b(!1),A=D(()=>{const s=f.schema;if(!s||!s.properties)return[];const c=["llm_providers","tts_providers","neuro","chatbot","stream","server"],v=Object.keys(s.properties);return v.sort((t,o)=>{const i=c.indexOf(t),a=c.indexOf(o);return i!==-1&&a!==-1?i-a:i!==-1?-1:a!==-1?1:t.localeCompare(o)}),v.map(t=>{const o=s.properties[t];if(o.$ref){const i=o.$ref.split("/").pop()||"",a=s.$defs?.[i]||{},y=a.properties||{};return{key:t,title:o.title||a.title||t.charAt(0).toUpperCase()+t.slice(1),isGroup:!0,properties:Object.keys(y).map(g=>({key:g,schema:y[g]}))}}return{key:t,title:o.title||t.charAt(0).toUpperCase()+t.slice(1),isGroup:!1,properties:[{key:t,schema:o}]}})});async function L(){if(S.isConnected){C.value=!0;try{await S.sendAdminWsMessage("update_configs",f.config),console.log("Config saved successfully!")}catch(s){console.error("Failed to save config:",s)}finally{C.value=!1}}}return I(async()=>{k.value=!0;try{await f.fetchSchema(),await f.fetchConfig()}finally{k.value=!1}}),(s,c)=>{const v=r("v-progress-circular"),t=r("v-tab"),o=r("v-tabs"),i=r("v-btn"),a=r("v-card-actions"),y=r("v-card"),g=r("v-window-item"),N=r("v-window");return n(),l("div",null,[k.value?(n(),l("div",R,[u(v,{indeterminate:"",color:"primary"}),O("p",T,h(p(_)("Loading configuration...")),1)])):(n(),l("div",q,[u(o,{modelValue:m.value,"onUpdate:modelValue":c[0]||(c[0]=e=>m.value=e),"bg-color":"primary",class:"mb-4 config-tabs",grow:""},{default:d(()=>[(n(!0),l(V,null,x(A.value,e=>(n(),j(t,{key:e.key,value:e.key},{default:d(()=>[B(h(p(_)(e.title)),1)]),_:2},1032,["value"]))),128))]),_:1},8,["modelValue"]),u(a,{class:"justify-end pa-4"},{default:d(()=>[u(i,{onClick:L,color:"primary",variant:"flat",loading:C.value},{default:d(()=>[B(h(p(_)("Save Configuration")),1)]),_:1},8,["loading"])]),_:1}),u(N,{modelValue:m.value,"onUpdate:modelValue":c[1]||(c[1]=e=>m.value=e),eager:""},{default:d(()=>[(n(!0),l(V,null,x(A.value,e=>(n(),j(g,{key:e.key,value:e.key},{default:d(()=>[u(y,{flat:"",class:"pa-4"},{default:d(()=>[O("h1",W,h(p(_)(e.title)),1),(n(!0),l(V,null,x(e.properties,w=>(n(),l("div",{key:w.key},[u(p(F),{"group-key":e.isGroup?e.key:null,"prop-key":w.key,"prop-schema":w.schema},null,8,["group-key","prop-key","prop-schema"])]))),128))]),_:2},1024)]),_:2},1032,["value"]))),128))]),_:1},8,["modelValue"])]))])}}});export{J as default};
|
@@ -1 +1 @@
|
|
1
|
-
import{d as m,k as g,A as x,a as v,x as f,b as C,n as o,o as s,g as r,e as h,t as l,h as p,F as y,p as b,f as w,_ as k}from"./index-
|
1
|
+
import{d as m,k as g,A as x,a as v,x as f,b as C,n as o,o as s,g as r,e as h,t as l,h as p,F as y,p as b,f as w,_ as k}from"./index-Sav9Djr5.js";const V={class:"d-flex align-center mb-4"},S={key:0,class:"context-prompt-view"},A={key:1,class:"context-conversation-view"},M=m({__name:"ContextTab",setup(B){const t=g(!1),n=x(),c=v();async function i(){if(c.isConnected)if(t.value)try{const e=await c.sendAdminWsMessage("get_last_prompt");n.agentContext=e.prompt}catch(e){console.error("获取最新Prompt失败:",e),n.agentContext=`获取提示词失败: ${e}`}else try{const e=await c.sendAdminWsMessage("get_agent_context");n.agentContext=e}catch(e){console.error("获取上下文失败:",e)}}return f(t,i),i(),(e,d)=>{const u=C("v-switch");return s(),o("div",null,[r("div",V,[h(u,{modelValue:t.value,"onUpdate:modelValue":d[0]||(d[0]=a=>t.value=a),label:"上下文模式",color:"primary","hide-details":""},null,8,["modelValue"])]),t.value?(s(),o("div",S,[r("pre",null,l(p(n).agentContext),1)])):(s(),o("div",A,[(s(!0),o(y,null,b(p(n).agentContext,(a,_)=>(s(),o("div",{key:_,class:"message-item"},[r("p",null,[r("strong",null,l(a.role)+":",1),w(" "+l(a.content),1)])]))),128))]))])}}}),T=k(M,[["__scopeId","data-v-dd6969bb"]]);export{T as default};
|
@@ -1 +1 @@
|
|
1
|
-
import{d as C,u as S,a as w,r as x,c as k,w as o,b as r,e as a,f as n,g as i,h as _,t as V,o as b,_ as A}from"./index-
|
1
|
+
import{d as C,u as S,a as w,r as x,c as k,w as o,b as r,e as a,f as n,g as i,h as _,t as V,o as b,_ as A}from"./index-Sav9Djr5.js";const B={class:"stream-status"},M={class:"control-buttons"},N=C({__name:"ControlView",setup(W){const d=S(),l=w(),e=x({start:!1,stop:!1,restart:!1});async function u(){e.start=!0;try{await l.sendAdminWsMessage("start_stream")}catch(s){console.error(s)}finally{e.start=!1}}async function f(){e.stop=!0;try{await l.sendAdminWsMessage("stop_stream")}catch(s){console.error(s)}finally{e.stop=!1}}async function p(){e.restart=!0;try{await l.sendAdminWsMessage("restart_stream")}catch(s){console.error(s)}finally{e.restart=!1}}return(s,t)=>{const m=r("v-card-title"),g=r("v-chip"),c=r("v-btn"),v=r("v-card-text"),y=r("v-card");return b(),k(y,null,{default:o(()=>[a(m,null,{default:o(()=>[...t[0]||(t[0]=[n("直播控制",-1)])]),_:1}),a(v,null,{default:o(()=>[i("div",B,[i("p",null,[t[1]||(t[1]=n("当前状态: ",-1)),a(g,{color:_(d).isRunning?"green":"red",dark:""},{default:o(()=>[n(V(_(d).isRunning?"运行中":"已停止"),1)]),_:1},8,["color"])])]),i("div",M,[a(c,{color:"primary",onClick:u,loading:e.start},{default:o(()=>[...t[2]||(t[2]=[n("开始直播",-1)])]),_:1},8,["loading"]),a(c,{color:"error",onClick:f,loading:e.stop},{default:o(()=>[...t[3]||(t[3]=[n("停止直播",-1)])]),_:1},8,["loading"]),a(c,{color:"warning",onClick:p,loading:e.restart},{default:o(()=>[...t[4]||(t[4]=[n("重启直播",-1)])]),_:1},8,["loading"])])]),_:1})]),_:1})}}}),D=A(N,[["__scopeId","data-v-607a7661"]]);export{D as default};
|
@@ -1 +1 @@
|
|
1
|
-
import{d as ee,i as te,j as le,l as U,k as C,r as ne,b as p,n as b,o as d,c as m,z as S,h as r,e as s,F as T,p as J,w as v,g as P,t as f,f as N,q as oe,s as ae}from"./index-
|
1
|
+
import{d as ee,i as te,j as le,l as U,k as C,r as ne,b as p,n as b,o as d,c as m,z as S,h as r,e as s,F as T,p as J,w as v,g as P,t as f,f as N,q as oe,s as ae}from"./index-Sav9Djr5.js";let O;const ie=new Uint8Array(16);function re(){if(!O&&(O=typeof crypto<"u"&&crypto.getRandomValues&&crypto.getRandomValues.bind(crypto),!O))throw new Error("crypto.getRandomValues() not supported. See https://github.com/uuidjs/uuid#getrandomvalues-not-supported");return O(ie)}const u=[];for(let e=0;e<256;++e)u.push((e+256).toString(16).slice(1));function de(e,t=0){return u[e[t+0]]+u[e[t+1]]+u[e[t+2]]+u[e[t+3]]+"-"+u[e[t+4]]+u[e[t+5]]+"-"+u[e[t+6]]+u[e[t+7]]+"-"+u[e[t+8]]+u[e[t+9]]+"-"+u[e[t+10]]+u[e[t+11]]+u[e[t+12]]+u[e[t+13]]+u[e[t+14]]+u[e[t+15]]}const ue=typeof crypto<"u"&&crypto.randomUUID&&crypto.randomUUID.bind(crypto),B={randomUUID:ue};function pe(e,t,j){if(B.randomUUID&&!e)return B.randomUUID();e=e||{};const l=e.random||(e.rng||re)();return l[6]=l[6]&15|64,l[8]=l[8]&63|128,de(l)}const se={class:"mb-6"},ce={key:8},me={key:0,class:"text-body-2"},ve={key:1,class:"text-body-2"},ye=ee({__name:"FieldRenderer",props:{groupKey:{},propKey:{},propSchema:{},isInDialog:{type:Boolean},dialogData:{}},setup(e){const{t}=te(),j=oe(()=>ae(()=>Promise.resolve().then(()=>fe),void 0)),l=e,y=le();function h(i){return l.propSchema.type===i?!0:Array.isArray(l.propSchema.anyOf)?l.propSchema.anyOf.some(o=>o.type===i):!1}const $=U(()=>!h("array")||!l.propSchema.items?!1:l.propSchema.items.type==="object"||!!l.propSchema.items.$ref),V=U(()=>{if(!$.value)return{};const i=l.propSchema.items;if(i.$ref){const o=i.$ref.split("/").pop();return y.schema.$defs?.[o]||{}}return i}),w=U(()=>l.propKey.endsWith("_provider_id")),F=U(()=>l.propKey==="llm_provider_id"?y.config.llm_providers||[]:l.propKey==="tts_provider_id"?y.config.tts_providers||[]:[]),a=U({get(){return l.isInDialog?l.dialogData?.[l.propKey]:l.groupKey?y.config[l.groupKey]?.[l.propKey]:y.config[l.propKey]},set(i){if(l.isInDialog){l.dialogData&&(l.dialogData[l.propKey]=i);return}l.groupKey?(y.config[l.groupKey]||(y.config[l.groupKey]={}),y.config[l.groupKey][l.propKey]=i):y.config[l.propKey]=i}}),k=C(!1),I=C(!1),c=ne({}),_=C(-1);function z(){I.value=!1,Object.keys(c).forEach(K=>delete c[K]);const i=V.value.properties||{};for(const K in i)c[K]=i[K].default??null;const o=c.provider_type||V.value.properties?.provider_type?.enum?.[0]||"unknown",g=l.propKey==="llm_providers"?"llm":"tts";c.provider_id=`${g}-${o}-${pe()}`,k.value=!0}function M(i,o){I.value=!0,_.value=o,Object.keys(c).forEach(g=>delete c[g]),Object.assign(c,JSON.parse(JSON.stringify(i))),k.value=!0}function L(){const i=a.value||[];I.value?i[_.value]=JSON.parse(JSON.stringify(c)):i.push(JSON.parse(JSON.stringify(c))),a.value=i,k.value=!1}function q(i){const o=a.value||[];o.splice(i,1),a.value=o}return(i,o)=>{const g=p("v-text-field"),K=p("v-textarea"),H=p("v-switch"),A=p("v-select"),x=p("v-btn"),W=p("v-card-title"),E=p("v-card-text"),R=p("v-card"),G=p("v-spacer"),Q=p("v-card-actions"),X=p("v-dialog"),Y=p("v-combobox");return d(),b("div",se,[e.propKey==="provider_id"?(d(),m(g,{key:0,modelValue:a.value,"onUpdate:modelValue":o[0]||(o[0]=n=>a.value=n),label:r(t)(e.propSchema.title||e.propKey),hint:e.propSchema.description,"persistent-hint":"",variant:"outlined",density:"compact",disabled:""},null,8,["modelValue","label","hint"])):h("integer")||h("number")?(d(),m(g,{key:1,modelValue:a.value,"onUpdate:modelValue":o[1]||(o[1]=n=>a.value=n),modelModifiers:{number:!0},label:r(t)(e.propSchema.title||e.propKey),hint:e.propSchema.description,type:"number","persistent-hint":"",variant:"outlined",density:"compact"},null,8,["modelValue","label","hint"])):h("string")&&e.propSchema.format==="password"?(d(),m(g,{key:2,modelValue:a.value,"onUpdate:modelValue":o[2]||(o[2]=n=>a.value=n),label:r(t)(e.propSchema.title||e.propKey),hint:e.propSchema.description,type:"password","persistent-hint":"",variant:"outlined",density:"compact"},null,8,["modelValue","label","hint"])):h("string")&&e.propSchema.format==="text-area"?(d(),m(K,{key:3,modelValue:a.value,"onUpdate:modelValue":o[3]||(o[3]=n=>a.value=n),label:r(t)(e.propSchema.title||e.propKey),hint:e.propSchema.description,"persistent-hint":"",variant:"outlined"},null,8,["modelValue","label","hint"])):h("string")&&!e.propSchema.enum&&!w.value?(d(),m(g,{key:4,modelValue:a.value,"onUpdate:modelValue":o[4]||(o[4]=n=>a.value=n),label:r(t)(e.propSchema.title||e.propKey),hint:e.propSchema.description,"persistent-hint":"",variant:"outlined",density:"compact"},null,8,["modelValue","label","hint"])):S("",!0),h("boolean")?(d(),m(H,{key:5,modelValue:a.value,"onUpdate:modelValue":o[5]||(o[5]=n=>a.value=n),label:r(t)(e.propSchema.title||e.propKey),hint:e.propSchema.description,"persistent-hint":"",color:"primary",inset:""},null,8,["modelValue","label","hint"])):S("",!0),e.propSchema.enum?(d(),m(A,{key:6,modelValue:a.value,"onUpdate:modelValue":o[6]||(o[6]=n=>a.value=n),items:e.propSchema.enum,label:r(t)(e.propSchema.title||e.propKey),hint:e.propSchema.description,"persistent-hint":"",variant:"outlined",density:"compact"},null,8,["modelValue","items","label","hint"])):S("",!0),w.value?(d(),m(A,{key:7,modelValue:a.value,"onUpdate:modelValue":o[7]||(o[7]=n=>a.value=n),items:F.value,"item-title":"display_name","item-value":"provider_id",label:r(t)(e.propSchema.title||e.propKey),hint:e.propSchema.description,"persistent-hint":"",variant:"outlined",density:"compact",clearable:""},null,8,["modelValue","items","label","hint"])):$.value?(d(),b("div",ce,[(d(!0),b(T,null,J(a.value,(n,D)=>(d(),m(R,{key:n.provider_id||D,class:"mb-4",variant:"outlined"},{default:v(()=>[s(W,{class:"d-flex justify-space-between align-center text-body-1"},{default:v(()=>[P("span",null,f(n.display_name||r(t)("Item")+" "+(D+1)),1),P("div",null,[s(x,{icon:"mdi-pencil",size:"small",variant:"text",onClick:Z=>M(n,D)},null,8,["onClick"]),s(x,{icon:"mdi-delete",size:"small",variant:"text",onClick:Z=>q(D)},null,8,["onClick"])])]),_:2},1024),n.provider_type||n.model_name?(d(),m(E,{key:0},{default:v(()=>[n.provider_type?(d(),b("p",me,f(r(t)("Provider Type"))+": "+f(n.provider_type),1)):S("",!0),n.model_name?(d(),b("p",ve,f(r(t)("Model Name"))+": "+f(n.model_name),1)):S("",!0)]),_:2},1024)):S("",!0)]),_:2},1024))),128)),s(x,{color:"primary",onClick:z,block:""},{default:v(()=>[N(f(r(t)("Add"))+" "+f(r(t)(V.value.title||"Item")),1)]),_:1}),s(X,{modelValue:k.value,"onUpdate:modelValue":o[9]||(o[9]=n=>k.value=n),"max-width":"800px",persistent:""},{default:v(()=>[s(R,{title:r(t)(I.value?"Edit":"Add")+" "+r(t)(V.value.title||"Item")},{default:v(()=>[s(E,null,{default:v(()=>[(d(!0),b(T,null,J(Object.keys(V.value.properties||{}),n=>(d(),b("div",{key:n},[s(r(j),{"group-key":null,"prop-key":n,"prop-schema":V.value.properties[n],"is-in-dialog":!0,"dialog-data":c},null,8,["prop-key","prop-schema","dialog-data"])]))),128))]),_:1}),s(Q,null,{default:v(()=>[s(G),s(x,{text:"",onClick:o[8]||(o[8]=n=>k.value=!1)},{default:v(()=>[N(f(r(t)("Cancel")),1)]),_:1}),s(x,{color:"primary",onClick:L},{default:v(()=>[N(f(r(t)("Save")),1)]),_:1})]),_:1})]),_:1},8,["title"])]),_:1},8,["modelValue"])])):h("array")?(d(),m(Y,{key:9,modelValue:a.value,"onUpdate:modelValue":o[10]||(o[10]=n=>a.value=n),label:r(t)(e.propSchema.title||e.propKey),hint:e.propSchema.description,"persistent-hint":"",chips:"",multiple:"","closable-chips":"",variant:"outlined",density:"compact"},null,8,["modelValue","label","hint"])):S("",!0)])}}}),fe=Object.freeze(Object.defineProperty({__proto__:null,default:ye},Symbol.toStringTag,{value:"Module"}));export{ye as default};
|
@@ -1 +1 @@
|
|
1
|
-
import{d as r,v as l,k as c,x as g,n as s,o as t,F as u,p as i,t as p,h as _,y as f,_ as d}from"./index-
|
1
|
+
import{d as r,v as l,k as c,x as g,n as s,o as t,F as u,p as i,t as p,h as _,y as f,_ as d}from"./index-Sav9Djr5.js";const m=r({__name:"LogsTab",setup(v){const a=l(),e=c(null);return g(()=>a.agentLogs.length,async()=>{await f(),e.value&&(e.value.scrollTop=e.value.scrollHeight)},{deep:!0}),(k,y)=>(t(),s("div",{ref_key:"logsContainer",ref:e,class:"logs-output"},[(t(!0),s(u,null,i(_(a).agentLogs,(o,n)=>(t(),s("div",{key:`agent-${n}`,class:"log-entry"},p(o),1))),128))],512))}}),h=d(m,[["__scopeId","data-v-20098789"]]);export{h as default};
|
@@ -1 +1 @@
|
|
1
|
-
import{d as p,v,k as f,x as g,c as m,w as t,b as s,e as n,f as x,g as k,n as l,p as w,h as y,F as L,o as a,t as C,y as V,_ as h}from"./index-
|
1
|
+
import{d as p,v,k as f,x as g,c as m,w as t,b as s,e as n,f as x,g as k,n as l,p as w,h as y,F as L,o as a,t as C,y as V,_ as h}from"./index-Sav9Djr5.js";const B=p({__name:"LogsView",setup(S){const o=v(),e=f(null);return g(()=>o.serverLogs.length,async()=>{await V(),e.value&&(e.value.scrollTop=e.value.scrollHeight)},{deep:!0}),(N,r)=>{const c=s("v-card-title"),_=s("v-card-text"),d=s("v-card");return a(),m(d,null,{default:t(()=>[n(c,null,{default:t(()=>[...r[0]||(r[0]=[x("Server 日志",-1)])]),_:1}),n(_,null,{default:t(()=>[k("div",{ref_key:"logsContainer",ref:e,class:"logs-output"},[(a(!0),l(L,null,w(y(o).serverLogs,(u,i)=>(a(),l("div",{key:`server-${i}`,class:"log-entry"},C(u),1))),128))],512)]),_:1})]),_:1})}}}),F=h(B,[["__scopeId","data-v-f9df559f"]]);export{F as default};
|
@@ -1,4 +1,4 @@
|
|
1
|
-
import{d as le,A as ne,a as oe,B as re,k as x,m as ae,b as i,n as f,o as s,e as t,w as l,g as v,c as V,f as d,h as k,F as U,p as F,t as C,_ as ie}from"./index-
|
1
|
+
import{d as le,A as ne,a as oe,B as re,k as x,m as ae,b as i,n as f,o as s,e as t,w as l,g as v,c as V,f as d,h as k,F as U,p as F,t as C,_ as ie}from"./index-Sav9Djr5.js";const se={class:"d-flex mb-4"},de={key:1},ue={class:"d-flex mb-4"},me={key:1},ce={class:"d-flex mb-4"},fe={key:0},ve={key:1},pe={class:"text-h5"},_e={class:"text-h5"},ye=le({__name:"MemoryTab",setup(xe){const p=ne(),a=oe();re();const M=x(!1),y=x({role:"user",content:""}),g=x(!1),m=x({}),b=x(!1),c=x({});function O(n=null){n?m.value={...n,contentStr:n.content.join(`
|
2
2
|
`)}:m.value={title:"",description:"",contentStr:""},g.value=!0}async function G(){if(!a.isConnected)return;const n=m.value,e={title:n.title,description:n.description,content:n.contentStr.split(`
|
3
3
|
`).filter(r=>r.trim()!=="")};try{n.id?await a.sendAdminWsMessage("update_core_memory_block",{block_id:n.id,...e}):await a.sendAdminWsMessage("create_core_memory_block",e),g.value=!1}catch(r){console.error("Failed to save core memory block:",r)}}function z(n=null,e=null){if(n!==null){let r=e;Array.isArray(e)?r=e.join(`
|
4
4
|
`):typeof e=="object"&&(r=JSON.stringify(e,null,2)),c.value={key:n,valueStr:r,isEditing:!0}}else c.value={key:"",valueStr:"",isEditing:!1};b.value=!0}async function H(){if(!a.isConnected||!c.value.key.trim())return;const{key:n,valueStr:e}=c.value;let r=e;try{const u=e.trim();u.startsWith("{")&&u.endsWith("}")||u.startsWith("[")&&u.endsWith("]")?r=JSON.parse(u):u.includes(`
|
@@ -1 +1 @@
|
|
1
|
-
import{d as F,C as D,a as R,k as W,x as E,m as G,b as s,c as b,o as u,w as e,e as t,g as n,f as d,n as m,F as A,p as k,h,t as f}from"./index-
|
1
|
+
import{d as F,C as D,a as R,k as W,x as E,m as G,b as s,c as b,o as u,w as e,e as t,g as n,f as d,n as m,F as A,p as k,h,t as f}from"./index-Sav9Djr5.js";const J={class:"d-flex mb-4"},O={class:"text-center"},I={class:"text-center"},j=F({__name:"ToolsTab",setup(L){const c=D(),r=R(),i=W({neuro_agent:[],memory_agent:[]});E(()=>c.allocations,l=>{i.value=JSON.parse(JSON.stringify(l||{neuro_agent:[],memory_agent:[]}))},{deep:!0,immediate:!0});async function S(){if(r.isConnected)try{await r.sendAdminWsMessage("set_agent_tool_allocations",{allocations:i.value}),console.log("Tool allocations saved successfully!")}catch(l){console.error("Failed to save allocations:",l)}}async function T(){if(r.isConnected)try{await r.sendAdminWsMessage("reload_tools")}catch(l){console.error("Failed to reload tools:",l)}}async function B(){if(r.isConnected)try{const[l,o]=await Promise.all([r.sendAdminWsMessage("get_all_tools"),r.sendAdminWsMessage("get_agent_tool_allocations")]);console.log("DEBUG: toolsResponse:",l),console.log("DEBUG: allocationsResponse:",o),c.handleAvailableToolsUpdate(l.tools),c.handleAllocationsUpdate(o.allocations)}catch(l){console.error("Failed to fetch tools initial data:",l)}}return G(()=>{B()}),(l,o)=>{const C=s("v-btn"),_=s("v-col"),p=s("v-row"),v=s("v-card-title"),V=s("v-checkbox-btn"),N=s("v-table"),g=s("v-card-text"),y=s("v-card"),w=s("v-chip"),M=s("v-chip-group"),U=s("v-container");return u(),b(U,{fluid:""},{default:e(()=>[t(p,null,{default:e(()=>[t(_,{cols:"12"},{default:e(()=>[n("div",J,[t(C,{onClick:T,class:"mr-2"},{default:e(()=>[...o[2]||(o[2]=[d("重载工具",-1)])]),_:1}),t(C,{onClick:S,color:"primary"},{default:e(()=>[...o[3]||(o[3]=[d("保存分配",-1)])]),_:1})]),o[4]||(o[4]=n("p",{class:"text-caption"},"在这里为 Agent 分配可用的工具标签。Neuro Agent 负责与观众互动,Memory Agent 负责在后台整理记忆。",-1))]),_:1})]),_:1}),t(p,null,{default:e(()=>[t(_,{cols:"12"},{default:e(()=>[t(y,{variant:"outlined"},{default:e(()=>[t(v,null,{default:e(()=>[...o[5]||(o[5]=[d("所有可用工具",-1)])]),_:1}),t(g,null,{default:e(()=>[t(N,{density:"compact"},{default:e(()=>[o[6]||(o[6]=n("thead",null,[n("tr",null,[n("th",{class:"text-left"},"工具名称"),n("th",{class:"text-left"},"描述"),n("th",{class:"text-center"},"Neuro Agent"),n("th",{class:"text-center"},"Memory Agent")])],-1)),n("tbody",null,[(u(!0),m(A,null,k(h(c).availableTools,a=>(u(),m("tr",{key:a.name},[n("td",null,f(a.name),1),n("td",null,f(a.description),1),n("td",O,[t(V,{modelValue:i.value.neuro_agent,"onUpdate:modelValue":o[0]||(o[0]=x=>i.value.neuro_agent=x),value:a.name,"hide-details":""},null,8,["modelValue","value"])]),n("td",I,[t(V,{modelValue:i.value.memory_agent,"onUpdate:modelValue":o[1]||(o[1]=x=>i.value.memory_agent=x),value:a.name,"hide-details":""},null,8,["modelValue","value"])])]))),128))])]),_:1})]),_:1})]),_:1})]),_:1})]),_:1}),t(p,null,{default:e(()=>[t(_,{md:"6",cols:"12"},{default:e(()=>[t(y,{variant:"outlined"},{default:e(()=>[t(v,null,{default:e(()=>[...o[7]||(o[7]=[d("Neuro Agent 工具集",-1)])]),_:1}),t(g,null,{default:e(()=>[t(M,{column:""},{default:e(()=>[(u(!0),m(A,null,k(h(c).allocations.neuro_agent,a=>(u(),b(w,{key:a},{default:e(()=>[d(f(a),1)]),_:2},1024))),128))]),_:1})]),_:1})]),_:1})]),_:1}),t(_,{md:"6",cols:"12"},{default:e(()=>[t(y,{variant:"outlined"},{default:e(()=>[t(v,null,{default:e(()=>[...o[8]||(o[8]=[d("Memory Agent 工具集",-1)])]),_:1}),t(g,null,{default:e(()=>[t(M,{column:""},{default:e(()=>[(u(!0),m(A,null,k(h(c).allocations.memory_agent,a=>(u(),b(w,{key:a},{default:e(()=>[d(f(a),1)]),_:2},1024))),128))]),_:1})]),_:1})]),_:1})]),_:1})]),_:1})]),_:1})}}});export{j as default};
|