Compare commits

...
3 Commits
Author SHA1 Message Date
root d84bb729f5 chore: 停止跟踪运行时日志
logs/ 三个文件是运行产物(server.log 实测 1MB / 3.3 万行滚动输出),
每次运行都产生巨量 diff 且无信息价值。改为 .gitignore 忽略,本地文件保留。

本次改动仅此一项:其余工作区无源码变更。
2026-09-21 10:11:38 +08:00
root 5590c674fe docs: README用例数88对齐+probe探测说明+报告目录忽略 2026-09-14 10:10:33 +08:00
root ad81cac4d5 feat: I-01模型能力探测脚本(只读settings+基线/思考/视觉三组+readonly自证) 2026-09-14 10:08:36 +08:00
6 changed files with 423 additions and 17406 deletions
+1
View File
@@ -8,3 +8,4 @@ data/
.pg/
android/app/build/
android/.gradle/
tools/probe-reports/
+13 -3
View File
@@ -2,7 +2,7 @@
为 DeepSeek Harness 提供 **团队设置 / 用户设置 / 插件分发** 的私有同步服务。
架构设计见 [ARCHITECTURE.md](ARCHITECTURE.md),移动端见 [MOBILE_APP.md](MOBILE_APP.md)。
**已完成并通过 55 个 pytest 用例(PostgreSQL 16)+ 真实 HTTP 冒烟验证。**
**已完成并通过 88 个 pytest 用例(PostgreSQL 16)+ 真实 HTTP 冒烟验证。**
## 目录结构
@@ -17,7 +17,7 @@ server/
ratelimit.py # 邀请码兑换限流
routers/ # 按资源拆分的路由(pages/connect/tokens/users/settings/plugins/audit)
static/ # mobile.html(PWA)、apk.html(安装页)、sw.js
tests/ # pytest 套件(55 例,跑在专用测试库 postgres_test 上)
tests/ # pytest 套件(88 例,跑在专用测试库 postgres_test 上)
client/
dsh_sync_client.py # 参考客户端(移植进 harness 用)
android/ # 安卓 WebView 壳工程 + build.sh
@@ -146,6 +146,16 @@ python client/dsh_sync_client.py status
## 已验证
- `server/tests`:27 个用例(认证、token 撤销、设置合并/冲突/权限、插件上传下载/哈希/路径穿越/版本过滤/渠道优先、扫码连接 info/qr/权限/HTTPS 场景、审计)在 **PostgreSQL 16** 上全部通过。
- `server/tests`:88 个用例(认证、token 撤销、设置合并/冲突/权限、插件上传下载/哈希/路径穿越/版本过滤/渠道优先、扫码连接 info/qr/权限/HTTPS 场景、备份恢复、APK 分发、客户端协议、平台矩阵、审计)在 **PostgreSQL 16** 上全部通过。
- 真实 uvicorn + PostgreSQL 启动 + curl 全链路冒烟:健康检查、admin 认证、团队设置写入、邀请码注册、插件上传、成员下载,sha256 两侧一致。
- 扫码连接(PWA「设置 → 连接手机」):二维码深链 + 候选地址切换 + `#invite=` 自动预填,已在 390×844 手机视口实测(截图 `dsh-app-connect-qr.png`)。
## 模型探测(I-01,最小闭环)
`tools/probe_models.py` 只读 `~/.dsh/settings.yaml`,对每组模型跑基线/思考/视觉三组探测,
报告写 `tools/probe-reports/`(本地留存,不进仓):
```bash
python tools/probe_models.py --all --compare # 全量探测 + 写报告 + 与上次对比
python tools/probe_models.py --check-readonly # 自证无 settings 写入路径
```
-105
View File
@@ -1,105 +0,0 @@
2026-09-10 21:23:34.018 CST [38100] 日志: 正在启动 PostgreSQL 16.4, compiled by Visual C++ build 1940, 64-bit
2026-09-10 21:23:34.020 CST [38100] 日志: 正在监听IPv4地址"127.0.0.1",端口 15433
2026-09-10 21:23:34.090 CST [51528] 日志: 数据库上次关闭时间为 2026-09-10 21:21:30 CST
2026-09-10 21:23:34.115 CST [38100] 日志: 数据库系统准备接受连接
2026-09-10 21:28:34.081 CST [57000] 日志: checkpoint starting: time
2026-09-10 21:28:34.195 CST [57000] 日志: checkpoint complete: wrote 4 buffers (0.0%); 0 WAL file(s) added, 0 removed, 0 recycled; write=0.104 s, sync=0.002 s, total=0.116 s; sync files=3, longest=0.001 s, average=0.001 s; distance=3 kB, estimate=3 kB; lsn=0/28F99F8, redo lsn=0/28F99C0
2026-09-10 21:33:34.195 CST [57000] 日志: checkpoint starting: time
2026-09-10 21:33:34.609 CST [57000] 日志: checkpoint complete: wrote 5 buffers (0.0%); 0 WAL file(s) added, 0 removed, 0 recycled; write=0.405 s, sync=0.002 s, total=0.415 s; sync files=5, longest=0.001 s, average=0.001 s; distance=4 kB, estimate=4 kB; lsn=0/28FAB48, redo lsn=0/28FAB10
2026-09-10 21:38:34.610 CST [57000] 日志: checkpoint starting: time
2026-09-10 21:39:14.758 CST [57000] 日志: checkpoint complete: wrote 399 buffers (2.4%); 0 WAL file(s) added, 0 removed, 0 recycled; write=39.944 s, sync=0.086 s, total=40.148 s; sync files=938, longest=0.001 s, average=0.001 s; distance=5643 kB, estimate=5643 kB; lsn=0/2E7E248, redo lsn=0/2E7DA20
2026-09-10 21:43:34.769 CST [57000] 日志: checkpoint starting: time
2026-09-10 21:43:34.908 CST [57000] 日志: checkpoint complete: wrote 2 buffers (0.0%); 0 WAL file(s) added, 0 removed, 0 recycled; write=0.111 s, sync=0.016 s, total=0.139 s; sync files=2, longest=0.015 s, average=0.008 s; distance=2 kB, estimate=5079 kB; lsn=0/2E7E3E0, redo lsn=0/2E7E3A8
2026-09-10 21:48:34.908 CST [57000] 日志: checkpoint starting: time
2026-09-10 21:48:35.322 CST [57000] 日志: checkpoint complete: wrote 5 buffers (0.0%); 0 WAL file(s) added, 0 removed, 0 recycled; write=0.404 s, sync=0.002 s, total=0.414 s; sync files=5, longest=0.001 s, average=0.001 s; distance=4 kB, estimate=4572 kB; lsn=0/2E7F408, redo lsn=0/2E7F3D0
2026-09-10 21:50:34.574 CST [83728] 閿欒�: 瀛楁� "revoked_at" 涓嶅瓨鍦� 绗� 32 涓�瓧绗﹀�
2026-09-10 21:50:34.574 CST [83728] 鎻愮ず: 涔熻�鎮ㄦ兂瑕佸紩鐢ㄥ垪"tokens.revoked"銆�
2026-09-10 21:50:34.574 CST [83728] 璇�彞: select token_hash,name,user_id,revoked_at from tokens order by user_id
2026-09-10 21:51:38.027 CST [79788] 致命错误: 数据库 "postgres_test" 不存在
2026-09-10 21:53:34.823 CST [57000] 日志: checkpoint starting: time
2026-09-10 21:56:14.171 CST [57000] 日志: checkpoint complete: wrote 1586 buffers (9.7%); 0 WAL file(s) added, 0 removed, 1 recycled; write=158.947 s, sync=0.171 s, total=159.349 s; sync files=2139, longest=0.001 s, average=0.001 s; distance=15535 kB, estimate=15535 kB; lsn=0/3DC8E48, redo lsn=0/3DAB1F8
2026-09-10 21:58:34.203 CST [57000] 日志: checkpoint starting: time
2026-09-10 21:59:06.903 CST [57000] 日志: checkpoint complete: wrote 287 buffers (1.8%); 0 WAL file(s) added, 0 removed, 1 recycled; write=32.528 s, sync=0.066 s, total=32.700 s; sync files=937, longest=0.001 s, average=0.001 s; distance=5329 kB, estimate=14514 kB; lsn=0/43EFE90, redo lsn=0/42DF5F8
2026-09-10 22:03:34.910 CST [57000] 日志: checkpoint starting: time
2026-09-10 22:03:46.948 CST [57000] 日志: checkpoint complete: wrote 121 buffers (0.7%); 0 WAL file(s) added, 0 removed, 0 recycled; write=12.025 s, sync=0.006 s, total=12.039 s; sync files=35, longest=0.001 s, average=0.001 s; distance=1102 kB, estimate=13173 kB; lsn=0/43F30F8, redo lsn=0/43F30C0
2026-09-10 22:38:34.950 CST [57000] 日志: checkpoint starting: time
2026-09-10 22:38:35.058 CST [57000] 日志: checkpoint complete: wrote 2 buffers (0.0%); 0 WAL file(s) added, 0 removed, 0 recycled; write=0.102 s, sync=0.002 s, total=0.108 s; sync files=2, longest=0.001 s, average=0.001 s; distance=6 kB, estimate=11856 kB; lsn=0/43F4BC8, redo lsn=0/43F4B90
2026-09-10 22:43:34.058 CST [57000] 日志: checkpoint starting: time
2026-09-10 22:44:13.419 CST [57000] 日志: checkpoint complete: wrote 392 buffers (2.4%); 0 WAL file(s) added, 0 removed, 0 recycled; write=39.199 s, sync=0.062 s, total=39.362 s; sync files=943, longest=0.001 s, average=0.001 s; distance=5672 kB, estimate=11238 kB; lsn=0/497EE28, redo lsn=0/497EDF0
2026-09-10 22:53:34.424 CST [57000] 日志: checkpoint starting: time
2026-09-10 22:54:11.103 CST [57000] 日志: checkpoint complete: wrote 342 buffers (2.1%); 0 WAL file(s) added, 0 removed, 0 recycled; write=36.423 s, sync=0.147 s, total=36.679 s; sync files=2423, longest=0.001 s, average=0.001 s; distance=6486 kB, estimate=10763 kB; lsn=0/584A4F0, redo lsn=0/4FD4678
2026-09-10 22:58:34.557 CST [57000] 日志: checkpoint starting: time
2026-09-10 22:59:36.197 CST [57000] 日志: checkpoint complete: wrote 612 buffers (3.7%); 0 WAL file(s) added, 0 removed, 2 recycled; write=61.132 s, sync=0.127 s, total=61.640 s; sync files=1656, longest=0.001 s, average=0.001 s; distance=18086 kB, estimate=18086 kB; lsn=0/617DF30, redo lsn=0/617DEF8
2026-09-11 13:52:06.016 CST [39848] 日志: 正在启动 PostgreSQL 16.4, compiled by Visual C++ build 1940, 64-bit
2026-09-11 13:52:06.016 CST [39848] 日志: 正在监听IPv4地址"127.0.0.1",端口 15433
2026-09-11 13:52:06.090 CST [5516] 日志: 数据库系统中断;上一次的启动时间是在2026-09-10 22:59:35 CST
2026-09-11 13:52:08.295 CST [5516] 日志: 数据库系统没有正确的关闭; 处于自动恢复状态中
2026-09-11 13:52:08.298 CST [5516] 日志: redo 在 0/617DEF8 开始
2026-09-11 13:52:08.298 CST [5516] 日志: invalid record length at 0/617DFE0: expected at least 24, got 0
2026-09-11 13:52:08.299 CST [5516] 日志: redo done at 0/617DFA8 system usage: CPU:用户:0.00 s,系统:0.00 s,已用时间:0.00 s
2026-09-11 13:52:08.304 CST [43748] 日志: checkpoint starting: end-of-recovery immediate wait
2026-09-11 13:52:08.313 CST [43748] 日志: checkpoint complete: wrote 3 buffers (0.0%); 0 WAL file(s) added, 0 removed, 0 recycled; write=0.003 s, sync=0.002 s, total=0.010 s; sync files=2, longest=0.001 s, average=0.001 s; distance=0 kB, estimate=0 kB; lsn=0/617DFE0, redo lsn=0/617DFE0
2026-09-11 13:52:08.335 CST [39848] 日志: 数据库系统准备接受连接
2026-09-11 13:57:08.313 CST [43748] 日志: checkpoint starting: time
2026-09-11 13:57:09.428 CST [43748] 日志: checkpoint complete: wrote 12 buffers (0.1%); 0 WAL file(s) added, 0 removed, 0 recycled; write=1.107 s, sync=0.002 s, total=1.116 s; sync files=12, longest=0.001 s, average=0.001 s; distance=7 kB, estimate=7 kB; lsn=0/617FDD0, redo lsn=0/617FD98
2026-09-11 14:02:08.429 CST [43748] 日志: checkpoint starting: time
2026-09-11 14:02:08.639 CST [43748] 日志: checkpoint complete: wrote 3 buffers (0.0%); 0 WAL file(s) added, 0 removed, 0 recycled; write=0.202 s, sync=0.002 s, total=0.211 s; sync files=3, longest=0.001 s, average=0.001 s; distance=6 kB, estimate=7 kB; lsn=0/6181798, redo lsn=0/6181760
2026-09-11 14:05:17.304 CST [39848] 日志: 服务器进程 (PID 30312) 被异常 0xC0000142 终止
2026-09-11 14:05:17.304 CST [39848] 提示: 关于对16进制值的描述, 参见C语言的引用文件 "ntstatus.h"
2026-09-11 14:05:17.304 CST [39848] 日志: 中断任何其它已激活的服务器进程
2026-09-11 14:05:17.309 CST [39848] 日志: 所有的服务器进程被中止; 重新初始化
2026-09-11 14:05:17.329 CST [39848] 日志: 启动进程 (PID 8576) 被异常 0xC0000142 终止
2026-09-11 14:05:17.329 CST [39848] 提示: 关于对16进制值的描述, 参见C语言的引用文件 "ntstatus.h"
2026-09-11 14:05:17.329 CST [39848] 日志: 由于启动进程失败, 终止启动
2026-09-11 14:05:17.331 CST [39848] 日志: 数据库系统已关闭
2026-09-11 14:06:14.742 CST [28292] 日志: 正在启动 PostgreSQL 16.4, compiled by Visual C++ build 1940, 64-bit
2026-09-11 14:06:14.743 CST [28292] 日志: 正在监听IPv4地址"127.0.0.1",端口 15433
2026-09-11 14:06:14.802 CST [1992] 日志: 数据库系统中断;上一次的启动时间是在2026-09-11 14:02:08 CST
2026-09-11 14:06:15.127 CST [1992] 日志: 数据库系统没有正确的关闭; 处于自动恢复状态中
2026-09-11 14:06:15.129 CST [1992] 日志: redo 在 0/6181760 开始
2026-09-11 14:06:15.130 CST [1992] 日志: invalid record length at 0/6181DF8: expected at least 24, got 0
2026-09-11 14:06:15.130 CST [1992] 日志: redo done at 0/6181DC0 system usage: CPU:用户:0.00 s,系统:0.00 s,已用时间:0.00 s
2026-09-11 14:06:15.134 CST [30364] 日志: checkpoint starting: end-of-recovery immediate wait
2026-09-11 14:06:15.141 CST [30364] 日志: checkpoint complete: wrote 4 buffers (0.0%); 0 WAL file(s) added, 0 removed, 0 recycled; write=0.003 s, sync=0.002 s, total=0.009 s; sync files=3, longest=0.001 s, average=0.001 s; distance=1 kB, estimate=1 kB; lsn=0/6181DF8, redo lsn=0/6181DF8
2026-09-11 14:06:15.161 CST [28292] 日志: 数据库系统准备接受连接
2026-09-11 14:11:15.142 CST [30364] 日志: checkpoint starting: time
2026-09-11 14:11:48.465 CST [30364] 日志: checkpoint complete: wrote 305 buffers (1.9%); 0 WAL file(s) added, 0 removed, 0 recycled; write=33.083 s, sync=0.146 s, total=33.324 s; sync files=1715, longest=0.001 s, average=0.001 s; distance=4357 kB, estimate=4357 kB; lsn=0/6C149E0, redo lsn=0/65C34B8
2026-09-11 14:16:15.469 CST [30364] 日志: checkpoint starting: time
2026-09-11 14:17:17.385 CST [30364] 日志: checkpoint complete: wrote 616 buffers (3.8%); 0 WAL file(s) added, 0 removed, 1 recycled; write=61.481 s, sync=0.119 s, total=61.916 s; sync files=1448, longest=0.001 s, average=0.001 s; distance=14695 kB, estimate=14695 kB; lsn=0/741D228, redo lsn=0/741D1F0
2026-09-11 14:56:15.399 CST [30364] 日志: checkpoint starting: time
2026-09-11 14:56:16.617 CST [30364] 日志: checkpoint complete: wrote 13 buffers (0.1%); 0 WAL file(s) added, 0 removed, 0 recycled; write=1.208 s, sync=0.003 s, total=1.219 s; sync files=13, longest=0.001 s, average=0.001 s; distance=9 kB, estimate=13226 kB; lsn=0/741F6F8, redo lsn=0/741F6C0
2026-09-11 15:01:15.618 CST [30364] 日志: checkpoint starting: time
2026-09-11 15:01:17.236 CST [30364] 日志: checkpoint complete: wrote 17 buffers (0.1%); 0 WAL file(s) added, 0 removed, 0 recycled; write=1.612 s, sync=0.003 s, total=1.619 s; sync files=17, longest=0.001 s, average=0.001 s; distance=33 kB, estimate=11907 kB; lsn=0/7427BF0, redo lsn=0/7427BB8
2026-09-11 15:06:15.237 CST [30364] 日志: checkpoint starting: time
2026-09-11 15:06:16.956 CST [30364] 日志: checkpoint complete: wrote 19 buffers (0.1%); 0 WAL file(s) added, 0 removed, 0 recycled; write=1.712 s, sync=0.004 s, total=1.720 s; sync files=17, longest=0.001 s, average=0.001 s; distance=59 kB, estimate=10722 kB; lsn=0/743B608, redo lsn=0/7436A78
2026-09-11 15:11:15.958 CST [30364] 日志: checkpoint starting: time
2026-09-11 15:11:53.884 CST [30364] 日志: checkpoint complete: wrote 379 buffers (2.3%); 0 WAL file(s) added, 0 removed, 0 recycled; write=37.768 s, sync=0.073 s, total=37.927 s; sync files=787, longest=0.001 s, average=0.001 s; distance=4674 kB, estimate=10117 kB; lsn=0/78C7668, redo lsn=0/78C7630
2026-09-11 17:11:56.167 CST [2392] 閿欒�: 閲嶅�閿�繚鍙嶅敮涓€绾︽潫"users_pkey"
2026-09-11 17:11:56.167 CST [2392] 璇︾粏淇℃伅: 閿�€�"(id)=(1)" 宸茬粡瀛樺湪
2026-09-11 17:11:56.167 CST [2392] 璇�彞: INSERT INTO users (id, username, role, created_at) VALUES (1, 'admin', 'admin', 't')
2026-09-11 17:11:56.695 CST [39632] 閿欒�: 閲嶅�閿�繚鍙嶅敮涓€绾︽潫"users_pkey"
2026-09-11 17:11:56.695 CST [39632] 璇︾粏淇℃伅: 閿�€�"(id)=(1)" 宸茬粡瀛樺湪
2026-09-11 17:11:56.695 CST [39632] 璇�彞: INSERT INTO users (id, username, role, created_at) VALUES (1, 'admin', 'admin', 't')
2026-09-11 17:11:57.557 CST [9852] 閿欒�: 閲嶅�閿�繚鍙嶅敮涓€绾︽潫"users_pkey"
2026-09-11 17:11:57.557 CST [9852] 璇︾粏淇℃伅: 閿�€�"(id)=(1)" 宸茬粡瀛樺湪
2026-09-11 17:11:57.557 CST [9852] 璇�彞: INSERT INTO users (id, username, role, created_at) VALUES (1, 'admin', 'admin', 't')
2026-09-11 17:11:57.869 CST [11332] 閿欒�: 閲嶅�閿�繚鍙嶅敮涓€绾︽潫"users_pkey"
2026-09-11 17:11:57.869 CST [11332] 璇︾粏淇℃伅: 閿�€�"(id)=(1)" 宸茬粡瀛樺湪
2026-09-11 17:11:57.869 CST [11332] 璇�彞: INSERT INTO users (id, username, role, created_at) VALUES (1, 'admin', 'admin', 't')
2026-09-11 17:11:58.407 CST [9580] 閿欒�: 閲嶅�閿�繚鍙嶅敮涓€绾︽潫"users_pkey"
2026-09-11 17:11:58.407 CST [9580] 璇︾粏淇℃伅: 閿�€�"(id)=(1)" 宸茬粡瀛樺湪
2026-09-11 17:11:58.407 CST [9580] 璇�彞: INSERT INTO users (id, username, role, created_at) VALUES (1, 'admin', 'admin', 't')
2026-09-11 17:11:58.802 CST [49864] 閿欒�: 閲嶅�閿�繚鍙嶅敮涓€绾︽潫"users_pkey"
2026-09-11 17:11:58.802 CST [49864] 璇︾粏淇℃伅: 閿�€�"(id)=(1)" 宸茬粡瀛樺湪
2026-09-11 17:11:58.802 CST [49864] 璇�彞: INSERT INTO users (id, username, role, created_at) VALUES (1, 'admin', 'admin', 't')
2026-09-11 17:12:10.032 CST [48692] 閿欒�: 閲嶅�閿�繚鍙嶅敮涓€绾︽潫"users_pkey"
2026-09-11 17:12:10.032 CST [48692] 璇︾粏淇℃伅: 閿�€�"(id)=(1)" 宸茬粡瀛樺湪
2026-09-11 17:12:10.032 CST [48692] 璇�彞: INSERT INTO users (id, username, role, created_at) VALUES (1, 'admin', 'admin', 't')
2026-09-11 17:16:15.902 CST [30364] 日志: checkpoint starting: time
2026-09-11 17:16:38.549 CST [30364] 日志: checkpoint complete: wrote 210 buffers (1.3%); 0 WAL file(s) added, 0 removed, 0 recycled; write=22.576 s, sync=0.032 s, total=22.647 s; sync files=287, longest=0.001 s, average=0.001 s; distance=1768 kB, estimate=9282 kB; lsn=0/7A81938, redo lsn=0/7A81900
2026-09-12 01:56:18.134 CST [30364] 日志: checkpoint starting: time
2026-09-12 01:57:18.066 CST [30364] 日志: checkpoint complete: wrote 594 buffers (3.6%); 0 WAL file(s) added, 0 removed, 1 recycled; write=59.549 s, sync=0.154 s, total=59.933 s; sync files=1699, longest=0.001 s, average=0.001 s; distance=10098 kB, estimate=10098 kB; lsn=0/845E508, redo lsn=0/845E4D0
2026-09-12 02:26:18.119 CST [30364] 日志: checkpoint starting: time
2026-09-12 02:27:18.409 CST [30364] 日志: checkpoint complete: wrote 600 buffers (3.7%); 0 WAL file(s) added, 0 removed, 0 recycled; write=59.991 s, sync=0.128 s, total=60.290 s; sync files=1671, longest=0.001 s, average=0.001 s; distance=9995 kB, estimate=10088 kB; lsn=0/8E212F0, redo lsn=0/8E212B8
2026-09-12 02:31:18.415 CST [30364] 日志: checkpoint starting: time
2026-09-12 02:31:18.521 CST [30364] 日志: checkpoint complete: wrote 2 buffers (0.0%); 0 WAL file(s) added, 0 removed, 0 recycled; write=0.102 s, sync=0.001 s, total=0.107 s; sync files=2, longest=0.001 s, average=0.001 s; distance=6 kB, estimate=9080 kB; lsn=0/8E22B90, redo lsn=0/8E22B58
-16556
View File
File diff suppressed because it is too large Load Diff
-742
View File
@@ -1,742 +0,0 @@
INFO: Started server process [58924]
INFO: Waiting for application startup.
INFO: Application startup complete.
INFO: Uvicorn running on http://0.0.0.0:8000 (Press CTRL+C to quit)
INFO: 127.0.0.1:52813 - "GET /healthz HTTP/1.1" 200 OK
INFO: 127.0.0.1:52814 - "GET /healthz HTTP/1.1" 200 OK
INFO: 127.0.0.1:53128 - "GET /v1/me HTTP/1.1" 200 OK
INFO: 127.0.0.1:53131 - "GET /app HTTP/1.1" 200 OK
INFO: 127.0.0.1:53133 - "GET /v1/connect/info HTTP/1.1" 200 OK
INFO: Started server process [62588]
INFO: Waiting for application startup.
INFO: Application startup complete.
INFO: Uvicorn running on http://0.0.0.0:8000 (Press CTRL+C to quit)
INFO: 127.0.0.1:50706 - "GET /healthz HTTP/1.1" 200 OK
INFO: 127.0.0.1:57783 - "GET /v1/connect/info HTTP/1.1" 200 OK
INFO: 127.0.0.1:57785 - "GET /v1/connect/qr HTTP/1.1" 200 OK
INFO: 127.0.0.1:57787 - "GET /app HTTP/1.1" 200 OK
INFO: 127.0.0.1:61541 - "GET /api/v1/ping/ HTTP/1.1" 404 Not Found
INFO: 127.0.0.1:61542 - "GET / HTTP/1.1" 404 Not Found
文件名、目录名或卷标语法不正确。
INFO: Started server process [40552]
INFO: Waiting for application startup.
INFO: Application startup complete.
INFO: Uvicorn running on http://0.0.0.0:8000 (Press CTRL+C to quit)
INFO: Started server process [86540]
INFO: Waiting for application startup.
INFO: Application startup complete.
INFO: Uvicorn running on http://0.0.0.0:8010 (Press CTRL+C to quit)
INFO: 127.0.0.1:57353 - "GET /healthz HTTP/1.1" 200 OK
INFO: 127.0.0.1:57354 - "GET /v1/me HTTP/1.1" 200 OK
INFO: 127.0.0.1:57356 - "GET /v1/connect/info HTTP/1.1" 200 OK
INFO: 127.0.0.1:57358 - "GET /app HTTP/1.1" 200 OK
INFO: 127.0.0.1:57609 - "GET /healthz HTTP/1.1" 200 OK
INFO: 127.0.0.1:57610 - "GET /v1/connect/info HTTP/1.1" 200 OK
INFO: 127.0.0.1:57612 - "GET /v1/connect/qr HTTP/1.1" 200 OK
INFO: 192.168.5.2:52632 - "GET /app HTTP/1.1" 200 OK
INFO: 192.168.5.2:52632 - "GET /favicon.ico HTTP/1.1" 404 Not Found
INFO: 192.168.5.2:50397 - "GET /v1/me HTTP/1.1" 200 OK
INFO: 192.168.5.2:50397 - "GET /v1/settings HTTP/1.1" 200 OK
INFO: 192.168.5.2:50397 - "GET /v1/plugins HTTP/1.1" 200 OK
INFO: 192.168.5.2:52862 - "GET /v1/me HTTP/1.1" 200 OK
INFO: 192.168.5.2:50397 - "GET /v1/audit?limit=30 HTTP/1.1" 200 OK
INFO: 192.168.5.2:52862 - "GET /v1/tokens HTTP/1.1" 200 OK
INFO: 192.168.5.2:61320 - "GET /v1/connect/info HTTP/1.1" 200 OK
INFO: 192.168.5.2:61320 - "GET /v1/connect/qr?host=192.168.5.2 HTTP/1.1" 200 OK
INFO: Started server process [79908]
INFO: Waiting for application startup.
INFO: Application startup complete.
INFO: Uvicorn running on http://0.0.0.0:8020 (Press CTRL+C to quit)
INFO: 192.168.5.2:53747 - "GET /healthz HTTP/1.1" 200 OK
INFO: 192.168.5.2:53748 - "GET /v1/me HTTP/1.1" 200 OK
INFO: 192.168.5.2:53750 - "GET /v1/connect/info HTTP/1.1" 200 OK
INFO: 192.168.5.2:53752 - "GET /app HTTP/1.1" 200 OK
INFO: Started server process [61696]
INFO: Waiting for application startup.
INFO: Application startup complete.
INFO: Uvicorn running on http://192.168.5.2:8020 (Press CTRL+C to quit)
INFO: 192.168.5.2:53176 - "GET /healthz HTTP/1.1" 200 OK
INFO: 192.168.5.2:53179 - "GET /v1/me HTTP/1.1" 200 OK
INFO: 192.168.5.2:53181 - "GET /v1/connect/info HTTP/1.1" 200 OK
INFO: 192.168.5.2:58384 - "GET /healthz HTTP/1.1" 200 OK
INFO: 192.168.5.2:58390 - "GET /app HTTP/1.1" 200 OK
INFO: 192.168.5.2:65100 - "GET /healthz HTTP/1.1" 200 OK
INFO: 192.168.5.2:65101 - "GET /v1/connect/info HTTP/1.1" 200 OK
INFO: 192.168.5.2:63725 - "GET /app HTTP/1.1" 200 OK
INFO: 192.168.5.2:63726 - "GET /v1/connect/info HTTP/1.1" 401 Unauthorized
INFO: 192.168.5.2:63756 - "GET /v1/me HTTP/1.1" 200 OK
INFO: 192.168.5.2:63758 - "GET /v1/connect/info HTTP/1.1" 200 OK
INFO: 192.168.5.2:63760 - "GET /v1/connect/qr?host=192.168.5.2 HTTP/1.1" 200 OK
INFO: Started server process [60672]
INFO: Waiting for application startup.
INFO: Application startup complete.
INFO: Uvicorn running on http://192.168.5.2:8020 (Press CTRL+C to quit)
INFO: 192.168.5.2:64419 - "GET /app HTTP/1.1" 200 OK
INFO: 192.168.5.2:55021 - "GET /manifest.webmanifest HTTP/1.1" 200 OK
INFO: 192.168.5.2:64419 - "GET /icon.svg HTTP/1.1" 200 OK
INFO: 192.168.5.2:62493 - "GET /v1/me HTTP/1.1" 200 OK
INFO: 192.168.5.2:62493 - "GET /v1/settings HTTP/1.1" 200 OK
INFO: 192.168.5.2:62493 - "GET /v1/plugins HTTP/1.1" 200 OK
INFO: 192.168.5.2:53272 - "GET /v1/me HTTP/1.1" 200 OK
INFO: 192.168.5.2:62493 - "GET /v1/audit?limit=30 HTTP/1.1" 200 OK
INFO: 192.168.5.2:62493 - "GET /v1/tokens HTTP/1.1" 200 OK
INFO: 192.168.5.2:55950 - "GET /v1/connect/info HTTP/1.1" 200 OK
INFO: 192.168.5.2:55950 - "GET /v1/connect/qr?host=192.168.5.2 HTTP/1.1" 200 OK
INFO: 192.168.5.2:56687 - "GET /v1/connect/info HTTP/1.1" 200 OK
INFO: 192.168.5.2:65009 - "GET /healthz HTTP/1.1" 200 OK
INFO: 192.168.5.2:54444 - "GET /app HTTP/1.1" 200 OK
INFO: 192.168.5.2:58062 - "GET /manifest.webmanifest HTTP/1.1" 200 OK
INFO: 192.168.5.2:58076 - "GET /v1/connect/qr?host=192.168.5.2 HTTP/1.1" 200 OK
INFO: 192.168.5.2:50524 - "GET /v1/connect/qr?host=192.168.5.2 HTTP/1.1" 401 Unauthorized
INFO: 192.168.5.2:50535 - "GET /v1/connect/qr?host=192.168.5.2 HTTP/1.1" 401 Unauthorized
INFO: 192.168.5.2:50537 - "GET /healthz HTTP/1.1" 200 OK
INFO: 192.168.5.2:50060 - "GET /healthz HTTP/1.1" 200 OK
INFO: Started server process [47936]
INFO: Waiting for application startup.
INFO: Application startup complete.
INFO: Uvicorn running on http://192.168.5.2:8020 (Press CTRL+C to quit)
INFO: 192.168.5.2:52426 - "GET /healthz HTTP/1.1" 200 OK
INFO: 192.168.5.2:61075 - "GET /healthz HTTP/1.1" 200 OK
INFO: 192.168.5.2:49377 - "GET /v1/me HTTP/1.1" 200 OK
INFO: 192.168.5.2:49379 - "GET /v1/connect/info HTTP/1.1" 200 OK
INFO: 192.168.5.2:49381 - "GET /v1/connect/qr?host=192.168.5.2 HTTP/1.1" 200 OK
INFO: 192.168.5.2:49383 - "GET /v1/plugins HTTP/1.1" 200 OK
INFO: 192.168.5.2:57054 - "GET /app HTTP/1.1" 200 OK
INFO: 192.168.5.2:51629 - "GET /manifest.webmanifest HTTP/1.1" 200 OK
INFO: 192.168.5.2:57054 - "GET /v1/me HTTP/1.1" 200 OK
INFO: 192.168.5.2:57054 - "GET /v1/settings HTTP/1.1" 200 OK
INFO: 192.168.5.2:57054 - "GET /v1/plugins HTTP/1.1" 200 OK
INFO: 192.168.5.2:49194 - "GET /v1/me HTTP/1.1" 200 OK
INFO: 192.168.5.2:57054 - "GET /v1/audit?limit=30 HTTP/1.1" 200 OK
INFO: 192.168.5.2:57054 - "GET /v1/tokens HTTP/1.1" 200 OK
INFO: 192.168.5.2:57054 - "GET /v1/me HTTP/1.1" 200 OK
INFO: 192.168.5.2:52573 - "POST /v1/tokens HTTP/1.1" 200 OK
INFO: 192.168.5.2:52573 - "GET /v1/me HTTP/1.1" 200 OK
INFO: 192.168.5.2:52573 - "GET /v1/settings HTTP/1.1" 304 Not Modified
INFO: 192.168.5.2:52573 - "GET /v1/plugins HTTP/1.1" 200 OK
INFO: 192.168.5.2:52573 - "GET /v1/me HTTP/1.1" 200 OK
INFO: 192.168.5.2:52573 - "GET /v1/tokens HTTP/1.1" 200 OK
INFO: 192.168.5.2:60805 - "GET /healthz HTTP/1.1" 200 OK
INFO: 192.168.5.2:60806 - "GET /v1/me HTTP/1.1" 200 OK
INFO: 192.168.5.2:60808 - "GET /app HTTP/1.1" 200 OK
INFO: 192.168.5.2:49576 - "GET /v1/me HTTP/1.1" 401 Unauthorized
INFO: 192.168.5.2:49576 - "GET /app HTTP/1.1" 200 OK
INFO: 192.168.5.2:49576 - "GET /icon.svg HTTP/1.1" 200 OK
INFO: 192.168.5.2:53850 - "GET /v1/me HTTP/1.1" 200 OK
INFO: 192.168.5.2:53850 - "GET /v1/settings HTTP/1.1" 304 Not Modified
INFO: 192.168.5.2:53850 - "GET /v1/plugins HTTP/1.1" 200 OK
INFO: 192.168.5.2:56679 - "GET /v1/me HTTP/1.1" 200 OK
INFO: 192.168.5.2:53850 - "GET /v1/audit?limit=30 HTTP/1.1" 200 OK
INFO: 192.168.5.2:56679 - "GET /v1/tokens HTTP/1.1" 200 OK
INFO: 192.168.5.2:55981 - "GET /v1/connect/info HTTP/1.1" 200 OK
INFO: 192.168.5.2:55981 - "GET /v1/connect/qr?host=192.168.5.2 HTTP/1.1" 200 OK
INFO: 192.168.5.2:60051 - "GET /healthz HTTP/1.1" 200 OK
INFO: 192.168.5.2:50921 - "GET /healthz HTTP/1.1" 200 OK
INFO: 192.168.5.2:50922 - "GET /v1/me HTTP/1.1" 200 OK
INFO: 192.168.5.2:50924 - "GET /v1/connect/qr?host=192.168.5.2 HTTP/1.1" 200 OK
INFO: Started server process [85928]
INFO: Waiting for application startup.
INFO: Application startup complete.
INFO: Uvicorn running on http://192.168.5.2:8020 (Press CTRL+C to quit)
INFO: 192.168.5.2:59200 - "GET /v1/plugins/demo/1.0.0/download HTTP/1.1" 404 Not Found
INFO: 192.168.5.2:59202 - "GET /v1/plugins/demo/1.0.0/download HTTP/1.1" 404 Not Found
INFO: Started server process [37636]
INFO: Waiting for application startup.
INFO: Application startup complete.
INFO: Uvicorn running on http://192.168.5.2:8020 (Press CTRL+C to quit)
INFO: 192.168.5.2:54678 - "GET /healthz HTTP/1.1" 200 OK
INFO: 192.168.5.2:54679 - "GET /v1/plugins/demo/1.0.0/download HTTP/1.1" 200 OK
INFO: 192.168.5.2:54681 - "GET /healthz HTTP/1.1" 200 OK
INFO: Started server process [54636]
INFO: Waiting for application startup.
INFO: Application startup complete.
INFO: Uvicorn running on http://192.168.5.2:8020 (Press CTRL+C to quit)
INFO: 192.168.5.2:56428 - "GET /apk HTTP/1.1" 200 OK
INFO: 192.168.5.2:56430 - "GET /apk/info HTTP/1.1" 200 OK
INFO: 192.168.5.2:56432 - "GET /apk/dsh-sync.apk HTTP/1.1" 200 OK
INFO: 192.168.5.2:56434 - "GET /apk/qr HTTP/1.1" 200 OK
INFO: 192.168.5.2:51786 - "GET /apk HTTP/1.1" 200 OK
INFO: 192.168.5.2:53279 - "GET /apk HTTP/1.1" 200 OK
INFO: 192.168.5.2:53279 - "GET /apk/info HTTP/1.1" 200 OK
INFO: 192.168.5.2:54615 - "GET /favicon.ico HTTP/1.1" 404 Not Found
INFO: 192.168.5.2:53279 - "GET /apk/qr HTTP/1.1" 200 OK
INFO: 192.168.5.2:62711 - "GET /apk/qr HTTP/1.1" 200 OK
INFO: 192.168.5.2:62712 - "GET /apk/dsh-sync.apk HTTP/1.1" 200 OK
INFO: 192.168.5.2:55463 - "GET /healthz HTTP/1.1" 200 OK
INFO: 192.168.5.2:55464 - "GET /app HTTP/1.1" 200 OK
INFO: 192.168.5.2:55465 - "GET /apk HTTP/1.1" 200 OK
INFO: 192.168.5.2:55466 - "GET /icon.svg HTTP/1.1" 200 OK
INFO: 192.168.5.2:55467 - "GET /manifest.webmanifest HTTP/1.1" 200 OK
INFO: 192.168.5.2:55468 - "GET /sw.js HTTP/1.1" 200 OK
INFO: 192.168.5.2:55469 - "GET /apk/info HTTP/1.1" 200 OK
INFO: 192.168.5.2:55470 - "GET /apk/qr HTTP/1.1" 200 OK
INFO: 192.168.5.2:55471 - "GET /v1/me HTTP/1.1" 200 OK
INFO: 192.168.5.2:55474 - "GET /v1/settings HTTP/1.1" 200 OK
INFO: 192.168.5.2:55476 - "GET /v1/plugins HTTP/1.1" 200 OK
INFO: 192.168.5.2:55478 - "GET /v1/tokens HTTP/1.1" 200 OK
INFO: 192.168.5.2:55480 - "GET /v1/audit HTTP/1.1" 200 OK
INFO: 192.168.5.2:55482 - "GET /v1/connect/info HTTP/1.1" 200 OK
INFO: 192.168.5.2:55484 - "GET /apk/dsh-sync.apk HTTP/1.1" 200 OK
INFO: 192.168.5.2:55485 - "GET /v1/plugins/demo/1.0.0/download HTTP/1.1" 200 OK
INFO: 192.168.5.2:50207 - "POST /v1/tokens HTTP/1.1" 403 Forbidden
INFO: 192.168.5.2:50209 - "POST /v1/tokens HTTP/1.1" 403 Forbidden
INFO: 192.168.5.2:50211 - "POST /v1/tokens HTTP/1.1" 403 Forbidden
INFO: 192.168.5.2:50213 - "POST /v1/tokens HTTP/1.1" 403 Forbidden
INFO: 192.168.5.2:50215 - "POST /v1/tokens HTTP/1.1" 403 Forbidden
INFO: 192.168.5.2:50217 - "POST /v1/tokens HTTP/1.1" 403 Forbidden
INFO: 192.168.5.2:50219 - "POST /v1/tokens HTTP/1.1" 403 Forbidden
INFO: 192.168.5.2:50221 - "POST /v1/tokens HTTP/1.1" 403 Forbidden
INFO: 192.168.5.2:50223 - "POST /v1/tokens HTTP/1.1" 403 Forbidden
INFO: 192.168.5.2:50225 - "POST /v1/tokens HTTP/1.1" 403 Forbidden
INFO: 192.168.5.2:50227 - "POST /v1/tokens HTTP/1.1" 429 Too Many Requests
INFO: 192.168.5.2:50229 - "POST /v1/tokens HTTP/1.1" 429 Too Many Requests
INFO: 192.168.5.2:50231 - "POST /v1/tokens HTTP/1.1" 429 Too Many Requests
INFO: Started server process [83200]
INFO: Waiting for application startup.
INFO: Application startup complete.
INFO: Uvicorn running on http://192.168.5.2:8020 (Press CTRL+C to quit)
INFO: 192.168.5.2:50413 - "POST /v1/tokens HTTP/1.1" 403 Forbidden
INFO: 192.168.5.2:50415 - "POST /v1/tokens HTTP/1.1" 403 Forbidden
INFO: 192.168.5.2:50417 - "POST /v1/tokens HTTP/1.1" 403 Forbidden
INFO: 192.168.5.2:50419 - "POST /v1/tokens HTTP/1.1" 403 Forbidden
INFO: 192.168.5.2:50421 - "POST /v1/tokens HTTP/1.1" 403 Forbidden
INFO: 192.168.5.2:50423 - "POST /v1/tokens HTTP/1.1" 200 OK
INFO: 192.168.5.2:50425 - "POST /v1/tokens HTTP/1.1" 200 OK
INFO: 192.168.5.2:50960 - "GET /healthz HTTP/1.1" 200 OK
INFO: 192.168.5.2:50961 - "GET /app HTTP/1.1" 200 OK
INFO: 192.168.5.2:50962 - "GET /apk HTTP/1.1" 200 OK
INFO: 192.168.5.2:50963 - "GET /apk/info HTTP/1.1" 200 OK
INFO: 192.168.5.2:50964 - "GET /apk/dsh-sync.apk HTTP/1.1" 200 OK
WARNING: Invalid HTTP request received.
INFO: 192.168.5.2:61028 - "GET /app HTTP/1.1" 200 OK
INFO: 192.168.5.2:61028 - "GET /icon.svg HTTP/1.1" 200 OK
INFO: Started server process [44388]
INFO: Waiting for application startup.
INFO: Application startup complete.
INFO: Uvicorn running on http://192.168.5.2:8020 (Press CTRL+C to quit)
INFO: 192.168.5.2:55901 - "GET /healthz HTTP/1.1" 200 OK
INFO: 192.168.5.2:57398 - "GET /app HTTP/1.1" 200 OK
INFO: 192.168.5.2:56528 - "GET /app HTTP/1.1" 200 OK
INFO: 192.168.5.2:56528 - "GET /icon.svg HTTP/1.1" 200 OK
INFO: 192.168.5.2:58458 - "POST /v1/tokens HTTP/1.1" 200 OK
INFO: 192.168.5.2:58458 - "GET /v1/me HTTP/1.1" 200 OK
INFO: 192.168.5.2:58458 - "GET /v1/settings HTTP/1.1" 200 OK
INFO: 192.168.5.2:58458 - "GET /v1/plugins HTTP/1.1" 200 OK
INFO: 192.168.5.2:58458 - "GET /v1/me HTTP/1.1" 200 OK
INFO: 192.168.5.2:58458 - "GET /v1/tokens HTTP/1.1" 200 OK
INFO: 192.168.5.2:54167 - "GET /v1/plugins HTTP/1.1" 200 OK
INFO: 192.168.5.2:61651 - "GET /v1/me HTTP/1.1" 200 OK
INFO: 192.168.5.2:61651 - "GET /v1/tokens HTTP/1.1" 200 OK
INFO: 192.168.5.2:49961 - "GET /v1/settings HTTP/1.1" 304 Not Modified
INFO: 192.168.5.2:58849 - "GET /app HTTP/1.1" 200 OK
INFO: 192.168.5.2:58849 - "GET /icon.svg HTTP/1.1" 200 OK
INFO: 192.168.5.2:63050 - "GET /v1/me HTTP/1.1" 200 OK
INFO: 192.168.5.2:63050 - "GET /v1/settings HTTP/1.1" 200 OK
INFO: 192.168.5.2:63050 - "GET /v1/plugins HTTP/1.1" 200 OK
INFO: 192.168.5.2:63054 - "GET /v1/me HTTP/1.1" 200 OK
INFO: 192.168.5.2:63050 - "GET /v1/audit?limit=30 HTTP/1.1" 200 OK
INFO: 192.168.5.2:63050 - "GET /v1/tokens HTTP/1.1" 200 OK
INFO: 192.168.5.2:63506 - "GET /v1/connect/info HTTP/1.1" 200 OK
INFO: 192.168.5.2:63506 - "GET /v1/connect/qr?host=192.168.5.2 HTTP/1.1" 200 OK
INFO: 192.168.5.2:52112 - "GET /app HTTP/1.1" 200 OK
INFO: 192.168.5.2:52112 - "GET /v1/me HTTP/1.1" 200 OK
INFO: 192.168.5.2:52112 - "GET /v1/settings HTTP/1.1" 304 Not Modified
INFO: 192.168.5.2:52112 - "GET /v1/plugins HTTP/1.1" 200 OK
INFO: 192.168.5.2:52117 - "GET /v1/me HTTP/1.1" 200 OK
INFO: 192.168.5.2:52112 - "GET /v1/audit?limit=30 HTTP/1.1" 200 OK
INFO: 192.168.5.2:52112 - "GET /v1/tokens HTTP/1.1" 200 OK
INFO: 192.168.5.2:56474 - "GET /app HTTP/1.1" 200 OK
INFO: 192.168.5.2:56474 - "GET /v1/me HTTP/1.1" 200 OK
INFO: 192.168.5.2:56474 - "GET /v1/settings HTTP/1.1" 304 Not Modified
INFO: 192.168.5.2:56474 - "GET /v1/plugins HTTP/1.1" 200 OK
INFO: 192.168.5.2:56474 - "GET /v1/audit?limit=30 HTTP/1.1" 200 OK
INFO: 192.168.5.2:56479 - "GET /v1/me HTTP/1.1" 200 OK
INFO: 192.168.5.2:56479 - "GET /v1/tokens HTTP/1.1" 200 OK
INFO: 192.168.5.2:56479 - "GET /app HTTP/1.1" 200 OK
INFO: 192.168.5.2:56479 - "GET /v1/me HTTP/1.1" 200 OK
INFO: 192.168.5.2:56479 - "GET /v1/settings HTTP/1.1" 304 Not Modified
INFO: 192.168.5.2:56479 - "GET /v1/plugins HTTP/1.1" 200 OK
INFO: 192.168.5.2:56479 - "GET /v1/audit?limit=30 HTTP/1.1" 200 OK
INFO: 192.168.5.2:56494 - "GET /v1/me HTTP/1.1" 200 OK
INFO: 192.168.5.2:56494 - "GET /v1/tokens HTTP/1.1" 200 OK
INFO: 192.168.5.2:56574 - "GET /app HTTP/1.1" 200 OK
INFO: 192.168.5.2:56574 - "GET /v1/me HTTP/1.1" 200 OK
INFO: 192.168.5.2:56574 - "GET /v1/settings HTTP/1.1" 304 Not Modified
INFO: 192.168.5.2:56574 - "GET /v1/plugins HTTP/1.1" 200 OK
INFO: 192.168.5.2:56574 - "GET /v1/me HTTP/1.1" 200 OK
INFO: 192.168.5.2:56584 - "GET /v1/audit?limit=30 HTTP/1.1" 200 OK
INFO: 192.168.5.2:56574 - "GET /v1/tokens HTTP/1.1" 200 OK
INFO: 192.168.5.2:58710 - "GET /apk HTTP/1.1" 200 OK
INFO: 192.168.5.2:58710 - "GET /apk/info HTTP/1.1" 200 OK
INFO: 192.168.5.2:58710 - "GET /apk/qr HTTP/1.1" 200 OK
INFO: 192.168.5.2:58710 - "GET /favicon.ico HTTP/1.1" 404 Not Found
INFO: 192.168.5.2:49511 - "GET /apk/dsh-sync.apk HTTP/1.1" 200 OK
Traceback (most recent call last):
File "<frozen runpy>", line 198, in _run_module_as_main
File "<frozen runpy>", line 88, in _run_code
File "C:\Users\12914\Desktop\dsh\server\.venv\Lib\site-packages\uvicorn\__main__.py", line 4, in <module>
uvicorn.main()
File "C:\Users\12914\Desktop\dsh\server\.venv\Lib\site-packages\click\core.py", line 1631, in __call__
return self.main(*args, **kwargs)
^^^^^^^^^^^^^^^^^^^^^^^^^^
File "C:\Users\12914\Desktop\dsh\server\.venv\Lib\site-packages\click\core.py", line 1552, in main
rv = self.invoke(ctx)
^^^^^^^^^^^^^^^^
File "C:\Users\12914\Desktop\dsh\server\.venv\Lib\site-packages\click\core.py", line 1415, in invoke
return ctx.invoke(self.callback, **ctx.params)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "C:\Users\12914\Desktop\dsh\server\.venv\Lib\site-packages\click\core.py", line 910, in invoke
return callback(*args, **kwargs)
^^^^^^^^^^^^^^^^^^^^^^^^^
File "C:\Users\12914\Desktop\dsh\server\.venv\Lib\site-packages\uvicorn\main.py", line 440, in main
run(
File "C:\Users\12914\Desktop\dsh\server\.venv\Lib\site-packages\uvicorn\main.py", line 609, in run
config.load_app()
File "C:\Users\12914\Desktop\dsh\server\.venv\Lib\site-packages\uvicorn\config.py", line 428, in load_app
return import_from_string(self.app)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "C:\Users\12914\Desktop\dsh\server\.venv\Lib\site-packages\uvicorn\importer.py", line 19, in import_from_string
module = importlib.import_module(module_str)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "C:\Users\12914\AppData\Local\Programs\Python\Python312\Lib\importlib\__init__.py", line 90, in import_module
return _bootstrap._gcd_import(name[level:], package, level)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "<frozen importlib._bootstrap>", line 1387, in _gcd_import
File "<frozen importlib._bootstrap>", line 1360, in _find_and_load
File "<frozen importlib._bootstrap>", line 1331, in _find_and_load_unlocked
File "<frozen importlib._bootstrap>", line 935, in _load_unlocked
File "<frozen importlib._bootstrap_external>", line 999, in exec_module
File "<frozen importlib._bootstrap>", line 488, in _call_with_frames_removed
File "C:\Users\12914\Desktop\dsh\server\dsh_sync\main.py", line 55, in <module>
app = create_app()
^^^^^^^^^^^^
File "C:\Users\12914\Desktop\dsh\server\dsh_sync\main.py", line 31, in create_app
init_db(cfg)
File "C:\Users\12914\Desktop\dsh\server\dsh_sync\db.py", line 77, in init_db
conn = connect(cfg)
^^^^^^^^^^^^
File "C:\Users\12914\Desktop\dsh\server\dsh_sync\db.py", line 72, in connect
conn = psycopg.connect(dsn, row_factory=dict_row)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "C:\Users\12914\Desktop\dsh\server\.venv\Lib\site-packages\psycopg\connection.py", line 122, in connect
raise last_ex.with_traceback(None)
psycopg.OperationalError: connection failed: connection to server at "127.0.0.1", port 15433 failed: server closed the connection unexpectedly
This probably means the server terminated abnormally
before or while processing the request.
Traceback (most recent call last):
File "<frozen runpy>", line 198, in _run_module_as_main
File "<frozen runpy>", line 88, in _run_code
File "C:\Users\12914\Desktop\dsh\server\.venv\Lib\site-packages\uvicorn\__main__.py", line 4, in <module>
uvicorn.main()
File "C:\Users\12914\Desktop\dsh\server\.venv\Lib\site-packages\click\core.py", line 1631, in __call__
return self.main(*args, **kwargs)
^^^^^^^^^^^^^^^^^^^^^^^^^^
File "C:\Users\12914\Desktop\dsh\server\.venv\Lib\site-packages\click\core.py", line 1552, in main
rv = self.invoke(ctx)
^^^^^^^^^^^^^^^^
File "C:\Users\12914\Desktop\dsh\server\.venv\Lib\site-packages\click\core.py", line 1415, in invoke
return ctx.invoke(self.callback, **ctx.params)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "C:\Users\12914\Desktop\dsh\server\.venv\Lib\site-packages\click\core.py", line 910, in invoke
return callback(*args, **kwargs)
^^^^^^^^^^^^^^^^^^^^^^^^^
File "C:\Users\12914\Desktop\dsh\server\.venv\Lib\site-packages\uvicorn\main.py", line 440, in main
run(
File "C:\Users\12914\Desktop\dsh\server\.venv\Lib\site-packages\uvicorn\main.py", line 609, in run
config.load_app()
File "C:\Users\12914\Desktop\dsh\server\.venv\Lib\site-packages\uvicorn\config.py", line 428, in load_app
return import_from_string(self.app)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "C:\Users\12914\Desktop\dsh\server\.venv\Lib\site-packages\uvicorn\importer.py", line 19, in import_from_string
module = importlib.import_module(module_str)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "C:\Users\12914\AppData\Local\Programs\Python\Python312\Lib\importlib\__init__.py", line 90, in import_module
return _bootstrap._gcd_import(name[level:], package, level)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "<frozen importlib._bootstrap>", line 1387, in _gcd_import
File "<frozen importlib._bootstrap>", line 1360, in _find_and_load
File "<frozen importlib._bootstrap>", line 1331, in _find_and_load_unlocked
File "<frozen importlib._bootstrap>", line 935, in _load_unlocked
File "<frozen importlib._bootstrap_external>", line 999, in exec_module
File "<frozen importlib._bootstrap>", line 488, in _call_with_frames_removed
File "C:\Users\12914\Desktop\dsh\server\dsh_sync\main.py", line 55, in <module>
app = create_app()
^^^^^^^^^^^^
File "C:\Users\12914\Desktop\dsh\server\dsh_sync\main.py", line 31, in create_app
init_db(cfg)
File "C:\Users\12914\Desktop\dsh\server\dsh_sync\db.py", line 77, in init_db
conn = connect(cfg)
^^^^^^^^^^^^
File "C:\Users\12914\Desktop\dsh\server\dsh_sync\db.py", line 72, in connect
conn = psycopg.connect(dsn, row_factory=dict_row)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "C:\Users\12914\Desktop\dsh\server\.venv\Lib\site-packages\psycopg\connection.py", line 122, in connect
raise last_ex.with_traceback(None)
psycopg.errors.ConnectionTimeout: connection timeout expired
Traceback (most recent call last):
File "<frozen runpy>", line 198, in _run_module_as_main
File "<frozen runpy>", line 88, in _run_code
File "C:\Users\12914\Desktop\dsh\server\.venv\Lib\site-packages\uvicorn\__main__.py", line 4, in <module>
uvicorn.main()
File "C:\Users\12914\Desktop\dsh\server\.venv\Lib\site-packages\click\core.py", line 1631, in __call__
return self.main(*args, **kwargs)
^^^^^^^^^^^^^^^^^^^^^^^^^^
File "C:\Users\12914\Desktop\dsh\server\.venv\Lib\site-packages\click\core.py", line 1552, in main
rv = self.invoke(ctx)
^^^^^^^^^^^^^^^^
File "C:\Users\12914\Desktop\dsh\server\.venv\Lib\site-packages\click\core.py", line 1415, in invoke
return ctx.invoke(self.callback, **ctx.params)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "C:\Users\12914\Desktop\dsh\server\.venv\Lib\site-packages\click\core.py", line 910, in invoke
return callback(*args, **kwargs)
^^^^^^^^^^^^^^^^^^^^^^^^^
File "C:\Users\12914\Desktop\dsh\server\.venv\Lib\site-packages\uvicorn\main.py", line 440, in main
run(
File "C:\Users\12914\Desktop\dsh\server\.venv\Lib\site-packages\uvicorn\main.py", line 609, in run
config.load_app()
File "C:\Users\12914\Desktop\dsh\server\.venv\Lib\site-packages\uvicorn\config.py", line 428, in load_app
return import_from_string(self.app)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "C:\Users\12914\Desktop\dsh\server\.venv\Lib\site-packages\uvicorn\importer.py", line 19, in import_from_string
module = importlib.import_module(module_str)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "C:\Users\12914\AppData\Local\Programs\Python\Python312\Lib\importlib\__init__.py", line 90, in import_module
return _bootstrap._gcd_import(name[level:], package, level)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "<frozen importlib._bootstrap>", line 1387, in _gcd_import
File "<frozen importlib._bootstrap>", line 1360, in _find_and_load
File "<frozen importlib._bootstrap>", line 1331, in _find_and_load_unlocked
File "<frozen importlib._bootstrap>", line 935, in _load_unlocked
File "<frozen importlib._bootstrap_external>", line 999, in exec_module
File "<frozen importlib._bootstrap>", line 488, in _call_with_frames_removed
File "C:\Users\12914\Desktop\dsh\server\dsh_sync\main.py", line 55, in <module>
app = create_app()
^^^^^^^^^^^^
File "C:\Users\12914\Desktop\dsh\server\dsh_sync\main.py", line 31, in create_app
init_db(cfg)
File "C:\Users\12914\Desktop\dsh\server\dsh_sync\db.py", line 77, in init_db
conn = connect(cfg)
^^^^^^^^^^^^
File "C:\Users\12914\Desktop\dsh\server\dsh_sync\db.py", line 72, in connect
conn = psycopg.connect(dsn, row_factory=dict_row)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "C:\Users\12914\Desktop\dsh\server\.venv\Lib\site-packages\psycopg\connection.py", line 122, in connect
raise last_ex.with_traceback(None)
psycopg.errors.ConnectionTimeout: connection timeout expired
Traceback (most recent call last):
File "<frozen runpy>", line 198, in _run_module_as_main
File "<frozen runpy>", line 88, in _run_code
File "C:\Users\12914\Desktop\dsh\server\.venv\Lib\site-packages\uvicorn\__main__.py", line 4, in <module>
uvicorn.main()
File "C:\Users\12914\Desktop\dsh\server\.venv\Lib\site-packages\click\core.py", line 1631, in __call__
return self.main(*args, **kwargs)
^^^^^^^^^^^^^^^^^^^^^^^^^^
File "C:\Users\12914\Desktop\dsh\server\.venv\Lib\site-packages\click\core.py", line 1552, in main
rv = self.invoke(ctx)
^^^^^^^^^^^^^^^^
File "C:\Users\12914\Desktop\dsh\server\.venv\Lib\site-packages\click\core.py", line 1415, in invoke
return ctx.invoke(self.callback, **ctx.params)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "C:\Users\12914\Desktop\dsh\server\.venv\Lib\site-packages\click\core.py", line 910, in invoke
return callback(*args, **kwargs)
^^^^^^^^^^^^^^^^^^^^^^^^^
File "C:\Users\12914\Desktop\dsh\server\.venv\Lib\site-packages\uvicorn\main.py", line 440, in main
run(
File "C:\Users\12914\Desktop\dsh\server\.venv\Lib\site-packages\uvicorn\main.py", line 609, in run
config.load_app()
File "C:\Users\12914\Desktop\dsh\server\.venv\Lib\site-packages\uvicorn\config.py", line 428, in load_app
return import_from_string(self.app)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "C:\Users\12914\Desktop\dsh\server\.venv\Lib\site-packages\uvicorn\importer.py", line 19, in import_from_string
module = importlib.import_module(module_str)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "C:\Users\12914\AppData\Local\Programs\Python\Python312\Lib\importlib\__init__.py", line 90, in import_module
return _bootstrap._gcd_import(name[level:], package, level)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "<frozen importlib._bootstrap>", line 1387, in _gcd_import
File "<frozen importlib._bootstrap>", line 1360, in _find_and_load
File "<frozen importlib._bootstrap>", line 1331, in _find_and_load_unlocked
File "<frozen importlib._bootstrap>", line 935, in _load_unlocked
File "<frozen importlib._bootstrap_external>", line 999, in exec_module
File "<frozen importlib._bootstrap>", line 488, in _call_with_frames_removed
File "C:\Users\12914\Desktop\dsh\server\dsh_sync\main.py", line 55, in <module>
app = create_app()
^^^^^^^^^^^^
File "C:\Users\12914\Desktop\dsh\server\dsh_sync\main.py", line 31, in create_app
init_db(cfg)
File "C:\Users\12914\Desktop\dsh\server\dsh_sync\db.py", line 77, in init_db
conn = connect(cfg)
^^^^^^^^^^^^
File "C:\Users\12914\Desktop\dsh\server\dsh_sync\db.py", line 72, in connect
conn = psycopg.connect(dsn, row_factory=dict_row)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "C:\Users\12914\Desktop\dsh\server\.venv\Lib\site-packages\psycopg\connection.py", line 122, in connect
raise last_ex.with_traceback(None)
psycopg.errors.ConnectionTimeout: connection timeout expired
Traceback (most recent call last):
File "<frozen runpy>", line 198, in _run_module_as_main
File "<frozen runpy>", line 88, in _run_code
File "C:\Users\12914\Desktop\dsh\server\.venv\Lib\site-packages\uvicorn\__main__.py", line 4, in <module>
uvicorn.main()
File "C:\Users\12914\Desktop\dsh\server\.venv\Lib\site-packages\click\core.py", line 1631, in __call__
return self.main(*args, **kwargs)
^^^^^^^^^^^^^^^^^^^^^^^^^^
File "C:\Users\12914\Desktop\dsh\server\.venv\Lib\site-packages\click\core.py", line 1552, in main
rv = self.invoke(ctx)
^^^^^^^^^^^^^^^^
File "C:\Users\12914\Desktop\dsh\server\.venv\Lib\site-packages\click\core.py", line 1415, in invoke
return ctx.invoke(self.callback, **ctx.params)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "C:\Users\12914\Desktop\dsh\server\.venv\Lib\site-packages\click\core.py", line 910, in invoke
return callback(*args, **kwargs)
^^^^^^^^^^^^^^^^^^^^^^^^^
File "C:\Users\12914\Desktop\dsh\server\.venv\Lib\site-packages\uvicorn\main.py", line 440, in main
run(
File "C:\Users\12914\Desktop\dsh\server\.venv\Lib\site-packages\uvicorn\main.py", line 609, in run
config.load_app()
File "C:\Users\12914\Desktop\dsh\server\.venv\Lib\site-packages\uvicorn\config.py", line 428, in load_app
return import_from_string(self.app)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "C:\Users\12914\Desktop\dsh\server\.venv\Lib\site-packages\uvicorn\importer.py", line 19, in import_from_string
module = importlib.import_module(module_str)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "C:\Users\12914\AppData\Local\Programs\Python\Python312\Lib\importlib\__init__.py", line 90, in import_module
return _bootstrap._gcd_import(name[level:], package, level)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "<frozen importlib._bootstrap>", line 1387, in _gcd_import
File "<frozen importlib._bootstrap>", line 1360, in _find_and_load
File "<frozen importlib._bootstrap>", line 1331, in _find_and_load_unlocked
File "<frozen importlib._bootstrap>", line 935, in _load_unlocked
File "<frozen importlib._bootstrap_external>", line 999, in exec_module
File "<frozen importlib._bootstrap>", line 488, in _call_with_frames_removed
File "C:\Users\12914\Desktop\dsh\server\dsh_sync\main.py", line 55, in <module>
app = create_app()
^^^^^^^^^^^^
File "C:\Users\12914\Desktop\dsh\server\dsh_sync\main.py", line 31, in create_app
init_db(cfg)
File "C:\Users\12914\Desktop\dsh\server\dsh_sync\db.py", line 77, in init_db
conn = connect(cfg)
^^^^^^^^^^^^
File "C:\Users\12914\Desktop\dsh\server\dsh_sync\db.py", line 72, in connect
conn = psycopg.connect(dsn, row_factory=dict_row)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "C:\Users\12914\Desktop\dsh\server\.venv\Lib\site-packages\psycopg\connection.py", line 122, in connect
raise last_ex.with_traceback(None)
psycopg.errors.ConnectionTimeout: connection timeout expired
INFO: Started server process [44668]
INFO: Waiting for application startup.
INFO: Application startup complete.
INFO: Uvicorn running on http://192.168.5.2:8020 (Press CTRL+C to quit)
INFO: 192.168.5.2:50214 - "GET /healthz HTTP/1.1" 200 OK
INFO: 192.168.5.2:51190 - "GET /favicon.ico HTTP/1.1" 307 Temporary Redirect
INFO: 192.168.5.2:51197 - "GET /apk HTTP/1.1" 200 OK
INFO: 192.168.5.2:54957 - "GET /apk HTTP/1.1" 200 OK
INFO: 192.168.5.2:54957 - "GET /apk/info HTTP/1.1" 200 OK
INFO: 192.168.5.2:54957 - "GET /favicon.ico HTTP/1.1" 307 Temporary Redirect
INFO: 192.168.5.2:54965 - "GET /icon.svg HTTP/1.1" 200 OK
INFO: 192.168.5.2:54957 - "GET /apk/qr HTTP/1.1" 200 OK
INFO: 192.168.5.2:54173 - "GET /apk/dsh-sync.apk HTTP/1.1" 200 OK
INFO: 192.168.5.2:54175 - "GET /apk/dsh-sync.apk HTTP/1.1" 200 OK
INFO: 192.168.5.2:50033 - "GET /healthz HTTP/1.1" 200 OK
INFO: 192.168.5.2:50034 - "GET /app HTTP/1.1" 200 OK
INFO: 192.168.5.2:50035 - "GET /apk HTTP/1.1" 200 OK
INFO: 192.168.5.2:50037 - "GET /apk/info HTTP/1.1" 200 OK
INFO: 192.168.5.2:50041 - "GET /apk/qr HTTP/1.1" 200 OK
INFO: 192.168.5.2:50042 - "GET /favicon.ico HTTP/1.1" 307 Temporary Redirect
INFO: 192.168.5.2:50043 - "GET /icon.svg HTTP/1.1" 200 OK
INFO: 192.168.5.2:50044 - "GET /apk/dsh-sync.apk HTTP/1.1" 200 OK
INFO: 192.168.5.2:50045 - "GET /v1/me HTTP/1.1" 200 OK
INFO: 192.168.5.2:50047 - "GET /v1/settings HTTP/1.1" 200 OK
INFO: 192.168.5.2:50049 - "GET /v1/plugins HTTP/1.1" 200 OK
INFO: 192.168.5.2:50051 - "GET /v1/connect/info HTTP/1.1" 200 OK
INFO: 192.168.5.2:50825 - "POST /v1/tokens HTTP/1.1" 200 OK
INFO: 192.168.5.2:50839 - "GET /v1/settings HTTP/1.1" 200 OK
INFO: 192.168.5.2:50841 - "PUT /v1/settings/user/model.temperature HTTP/1.1" 200 OK
INFO: 192.168.5.2:50843 - "GET /v1/settings HTTP/1.1" 200 OK
INFO: 192.168.5.2:51093 - "GET /v1/settings HTTP/1.1" 304 Not Modified
INFO: 192.168.5.2:49946 - "GET /v1/settings HTTP/1.1" 304 Not Modified
INFO: 192.168.5.2:49956 - "GET /v1/plugins HTTP/1.1" 200 OK
INFO: 192.168.5.2:53420 - "GET /v1/plugins HTTP/1.1" 200 OK
INFO: 192.168.5.2:53422 - "GET /v1/plugins/demo/1.0.0/download HTTP/1.1" 200 OK
INFO: Started server process [44088]
INFO: Waiting for application startup.
INFO: Application startup complete.
INFO: Uvicorn running on http://192.168.5.2:8020 (Press CTRL+C to quit)
INFO: 192.168.5.2:56121 - "GET /healthz HTTP/1.1" 200 OK
INFO: 192.168.5.2:56122 - "GET /v1/backup/info HTTP/1.1" 200 OK
INFO: 192.168.5.2:56124 - "GET /v1/backup HTTP/1.1" 200 OK
INFO: 192.168.5.2:60321 - "POST /v1/tokens HTTP/1.1" 200 OK
INFO: 192.168.5.2:60323 - "GET /v1/plugins HTTP/1.1" 200 OK
INFO: 192.168.5.2:60325 - "GET /v1/plugins/demo/1.0.0/download HTTP/1.1" 200 OK
INFO: 192.168.5.2:58456 - "GET /v1/settings HTTP/1.1" 200 OK
INFO: 192.168.5.2:58474 - "GET /v1/settings HTTP/1.1" 200 OK
INFO: 192.168.5.2:58476 - "PUT /v1/settings/team/model.default HTTP/1.1" 200 OK
INFO: 192.168.5.2:58478 - "PUT /v1/settings/team/model.temperature HTTP/1.1" 200 OK
INFO: 192.168.5.2:58481 - "PUT /v1/settings/team/model.max_tokens HTTP/1.1" 200 OK
INFO: 192.168.5.2:58483 - "PUT /v1/settings/team/model.top_p HTTP/1.1" 200 OK
INFO: 192.168.5.2:58485 - "PUT /v1/settings/team/provider.base_url HTTP/1.1" 200 OK
INFO: 192.168.5.2:58487 - "PUT /v1/settings/team/provider.timeout_seconds HTTP/1.1" 200 OK
INFO: 192.168.5.2:58490 - "PUT /v1/settings/team/provider.max_retries HTTP/1.1" 200 OK
INFO: 192.168.5.2:58492 - "PUT /v1/settings/team/harness.plugin_channel HTTP/1.1" 200 OK
INFO: 192.168.5.2:58494 - "PUT /v1/settings/team/harness.auto_sync HTTP/1.1" 200 OK
INFO: 192.168.5.2:58496 - "PUT /v1/settings/team/harness.telemetry HTTP/1.1" 200 OK
INFO: 192.168.5.2:58498 - "PUT /v1/settings/team/ui.theme HTTP/1.1" 200 OK
INFO: 192.168.5.2:58501 - "PUT /v1/settings/team/ui.language HTTP/1.1" 200 OK
INFO: 192.168.5.2:58503 - "PUT /v1/settings/team/ui.show_token_usage HTTP/1.1" 200 OK
INFO: 192.168.5.2:58505 - "PUT /v1/settings/user/model.temperature HTTP/1.1" 200 OK
INFO: 192.168.5.2:58507 - "GET /v1/settings HTTP/1.1" 200 OK
INFO: 192.168.5.2:58509 - "PUT /v1/settings/user/model.temperature HTTP/1.1" 200 OK
INFO: 192.168.5.2:51336 - "GET /v1/settings HTTP/1.1" 200 OK
INFO: 192.168.5.2:51338 - "PUT /v1/settings/user/model.temperature HTTP/1.1" 200 OK
INFO: 192.168.5.2:51340 - "GET /v1/settings HTTP/1.1" 200 OK
INFO: 192.168.5.2:51342 - "PUT /v1/settings/team/model.temperature HTTP/1.1" 200 OK
INFO: 192.168.5.2:51039 - "GET /v1/settings HTTP/1.1" 200 OK
INFO: 192.168.5.2:61003 - "GET /v1/settings HTTP/1.1" 200 OK
INFO: 192.168.5.2:61435 - "GET /v1/settings HTTP/1.1" 200 OK
INFO: 192.168.5.2:61437 - "PUT /v1/settings/team/model.default HTTP/1.1" 200 OK
INFO: 192.168.5.2:61439 - "PUT /v1/settings/team/model.temperature HTTP/1.1" 200 OK
INFO: 192.168.5.2:61441 - "PUT /v1/settings/team/model.max_tokens HTTP/1.1" 200 OK
INFO: 192.168.5.2:61443 - "PUT /v1/settings/team/model.top_p HTTP/1.1" 200 OK
INFO: 192.168.5.2:61445 - "PUT /v1/settings/team/provider.base_url HTTP/1.1" 200 OK
INFO: 192.168.5.2:61447 - "PUT /v1/settings/team/provider.timeout_seconds HTTP/1.1" 200 OK
INFO: 192.168.5.2:61449 - "PUT /v1/settings/team/provider.max_retries HTTP/1.1" 200 OK
INFO: 192.168.5.2:61451 - "PUT /v1/settings/team/harness.plugin_channel HTTP/1.1" 200 OK
INFO: 192.168.5.2:61454 - "PUT /v1/settings/team/harness.auto_sync HTTP/1.1" 200 OK
INFO: 192.168.5.2:61456 - "PUT /v1/settings/team/harness.telemetry HTTP/1.1" 200 OK
INFO: 192.168.5.2:61458 - "PUT /v1/settings/team/ui.theme HTTP/1.1" 200 OK
INFO: 192.168.5.2:61460 - "PUT /v1/settings/team/ui.language HTTP/1.1" 200 OK
INFO: 192.168.5.2:61463 - "PUT /v1/settings/team/ui.show_token_usage HTTP/1.1" 200 OK
INFO: 192.168.5.2:61465 - "PUT /v1/settings/user/model.temperature HTTP/1.1" 200 OK
INFO: 192.168.5.2:61467 - "GET /v1/settings HTTP/1.1" 200 OK
INFO: 192.168.5.2:61470 - "PUT /v1/settings/team/model.default HTTP/1.1" 409 Conflict
INFO: 192.168.5.2:61472 - "PUT /v1/settings/team/model.temperature HTTP/1.1" 409 Conflict
INFO: 192.168.5.2:61474 - "PUT /v1/settings/team/model.max_tokens HTTP/1.1" 409 Conflict
INFO: 192.168.5.2:61476 - "PUT /v1/settings/team/model.top_p HTTP/1.1" 409 Conflict
INFO: 192.168.5.2:61478 - "PUT /v1/settings/team/provider.base_url HTTP/1.1" 409 Conflict
INFO: 192.168.5.2:61481 - "PUT /v1/settings/team/provider.timeout_seconds HTTP/1.1" 409 Conflict
INFO: 192.168.5.2:61483 - "PUT /v1/settings/team/provider.max_retries HTTP/1.1" 409 Conflict
INFO: 192.168.5.2:61485 - "PUT /v1/settings/team/harness.plugin_channel HTTP/1.1" 409 Conflict
INFO: 192.168.5.2:61487 - "PUT /v1/settings/team/harness.auto_sync HTTP/1.1" 409 Conflict
INFO: 192.168.5.2:61489 - "PUT /v1/settings/team/harness.telemetry HTTP/1.1" 409 Conflict
INFO: 192.168.5.2:61491 - "PUT /v1/settings/team/ui.theme HTTP/1.1" 409 Conflict
INFO: 192.168.5.2:61493 - "PUT /v1/settings/team/ui.language HTTP/1.1" 409 Conflict
INFO: 192.168.5.2:61495 - "PUT /v1/settings/team/ui.show_token_usage HTTP/1.1" 409 Conflict
INFO: 192.168.5.2:61497 - "PUT /v1/settings/user/model.temperature HTTP/1.1" 409 Conflict
INFO: 192.168.5.2:61499 - "GET /v1/settings HTTP/1.1" 200 OK
INFO: 192.168.5.2:61501 - "PUT /v1/settings/team/model.default HTTP/1.1" 409 Conflict
INFO: 192.168.5.2:61504 - "PUT /v1/settings/team/model.temperature HTTP/1.1" 409 Conflict
INFO: 192.168.5.2:61506 - "PUT /v1/settings/team/model.max_tokens HTTP/1.1" 409 Conflict
INFO: 192.168.5.2:61508 - "PUT /v1/settings/team/model.top_p HTTP/1.1" 409 Conflict
INFO: 192.168.5.2:61510 - "PUT /v1/settings/team/provider.base_url HTTP/1.1" 409 Conflict
INFO: 192.168.5.2:61512 - "PUT /v1/settings/team/provider.timeout_seconds HTTP/1.1" 409 Conflict
INFO: 192.168.5.2:61514 - "PUT /v1/settings/team/provider.max_retries HTTP/1.1" 409 Conflict
INFO: 192.168.5.2:61516 - "PUT /v1/settings/team/harness.plugin_channel HTTP/1.1" 409 Conflict
INFO: 192.168.5.2:61518 - "PUT /v1/settings/team/harness.auto_sync HTTP/1.1" 409 Conflict
INFO: 192.168.5.2:61520 - "PUT /v1/settings/team/harness.telemetry HTTP/1.1" 409 Conflict
INFO: 192.168.5.2:61522 - "PUT /v1/settings/team/ui.theme HTTP/1.1" 409 Conflict
INFO: 192.168.5.2:61524 - "PUT /v1/settings/team/ui.language HTTP/1.1" 409 Conflict
INFO: 192.168.5.2:61526 - "PUT /v1/settings/team/ui.show_token_usage HTTP/1.1" 409 Conflict
INFO: 192.168.5.2:61528 - "PUT /v1/settings/user/model.temperature HTTP/1.1" 409 Conflict
INFO: 192.168.5.2:49315 - "GET /v1/settings HTTP/1.1" 200 OK
INFO: 192.168.5.2:49317 - "POST /v1/tokens HTTP/1.1" 200 OK
INFO: 192.168.5.2:49319 - "GET /v1/settings HTTP/1.1" 200 OK
INFO: 192.168.5.2:57862 - "GET /healthz HTTP/1.1" 200 OK
INFO: 192.168.5.2:57863 - "GET /app HTTP/1.1" 200 OK
INFO: 192.168.5.2:57864 - "GET /apk HTTP/1.1" 200 OK
INFO: 192.168.5.2:57865 - "GET /icon.svg HTTP/1.1" 200 OK
INFO: 192.168.5.2:57866 - "GET /manifest.webmanifest HTTP/1.1" 200 OK
INFO: 192.168.5.2:57867 - "GET /favicon.ico HTTP/1.1" 307 Temporary Redirect
INFO: 192.168.5.2:57868 - "GET /icon.svg HTTP/1.1" 200 OK
INFO: 192.168.5.2:57869 - "GET /v1/me HTTP/1.1" 200 OK
INFO: 192.168.5.2:57872 - "GET /v1/settings HTTP/1.1" 200 OK
INFO: 192.168.5.2:57874 - "GET /v1/backup/info HTTP/1.1" 200 OK
INFO: 192.168.5.2:57876 - "GET /v1/plugins HTTP/1.1" 200 OK
INFO: 192.168.5.2:57878 - "GET /v1/plugins/demo/1.0.0/download HTTP/1.1" 200 OK
INFO: 192.168.5.2:63209 - "GET /healthz HTTP/1.1" 200 OK
INFO: 192.168.5.2:63210 - "GET /app HTTP/1.1" 200 OK
INFO: 192.168.5.2:63211 - "GET /apk HTTP/1.1" 200 OK
INFO: 192.168.5.2:63212 - "GET /icon.svg HTTP/1.1" 200 OK
INFO: 192.168.5.2:63213 - "GET /manifest.webmanifest HTTP/1.1" 200 OK
INFO: 192.168.5.2:63214 - "GET /favicon.ico HTTP/1.1" 307 Temporary Redirect
INFO: 192.168.5.2:63215 - "GET /v1/me HTTP/1.1" 200 OK
INFO: 192.168.5.2:63218 - "GET /v1/settings HTTP/1.1" 200 OK
INFO: 192.168.5.2:63220 - "GET /v1/backup/info HTTP/1.1" 200 OK
INFO: 192.168.5.2:63222 - "GET /v1/plugins HTTP/1.1" 200 OK
INFO: 192.168.5.2:63224 - "GET /v1/plugins/demo/1.0.0/download HTTP/1.1" 200 OK
INFO: 192.168.5.2:56387 - "GET /healthz HTTP/1.1" 200 OK
INFO: 192.168.5.2:56388 - "GET /app HTTP/1.1" 200 OK
INFO: 192.168.5.2:56389 - "GET /apk HTTP/1.1" 200 OK
INFO: 192.168.5.2:56390 - "GET /icon.svg HTTP/1.1" 200 OK
INFO: 192.168.5.2:56391 - "GET /manifest.webmanifest HTTP/1.1" 200 OK
INFO: 192.168.5.2:56392 - "GET /favicon.ico HTTP/1.1" 307 Temporary Redirect
INFO: 192.168.5.2:56393 - "GET /v1/me HTTP/1.1" 200 OK
INFO: 192.168.5.2:56395 - "GET /v1/settings HTTP/1.1" 200 OK
INFO: 192.168.5.2:56397 - "GET /v1/backup/info HTTP/1.1" 200 OK
INFO: 192.168.5.2:56399 - "GET /v1/plugins HTTP/1.1" 200 OK
INFO: 192.168.5.2:56401 - "GET /v1/plugins/demo/1.0.0/download HTTP/1.1" 404 Not Found
INFO: 192.168.5.2:56404 - "GET /healthz HTTP/1.1" 200 OK
INFO: 192.168.5.2:56405 - "GET /app HTTP/1.1" 200 OK
INFO: 192.168.5.2:56406 - "GET /apk HTTP/1.1" 200 OK
INFO: 192.168.5.2:56407 - "GET /icon.svg HTTP/1.1" 200 OK
INFO: 192.168.5.2:56408 - "GET /manifest.webmanifest HTTP/1.1" 200 OK
INFO: 192.168.5.2:56409 - "GET /favicon.ico HTTP/1.1" 307 Temporary Redirect
INFO: 192.168.5.2:56410 - "GET /v1/me HTTP/1.1" 200 OK
INFO: 192.168.5.2:56412 - "GET /v1/settings HTTP/1.1" 200 OK
INFO: 192.168.5.2:56414 - "GET /v1/backup/info HTTP/1.1" 200 OK
INFO: 192.168.5.2:56417 - "GET /v1/plugins HTTP/1.1" 200 OK
INFO: 192.168.5.2:56420 - "GET /v1/plugins/demo/1.0.0/download HTTP/1.1" 200 OK
INFO: 192.168.5.2:59955 - "GET /healthz HTTP/1.1" 200 OK
INFO: 192.168.5.2:59956 - "GET /app HTTP/1.1" 200 OK
INFO: 192.168.5.2:59957 - "GET /apk HTTP/1.1" 200 OK
INFO: 192.168.5.2:59958 - "GET /icon.svg HTTP/1.1" 200 OK
INFO: 192.168.5.2:59959 - "GET /manifest.webmanifest HTTP/1.1" 200 OK
INFO: 192.168.5.2:59960 - "GET /favicon.ico HTTP/1.1" 307 Temporary Redirect
INFO: 192.168.5.2:59961 - "GET /v1/me HTTP/1.1" 200 OK
INFO: 192.168.5.2:59963 - "GET /v1/settings HTTP/1.1" 200 OK
INFO: 192.168.5.2:59965 - "GET /v1/backup/info HTTP/1.1" 200 OK
INFO: 192.168.5.2:59967 - "GET /v1/plugins HTTP/1.1" 200 OK
INFO: 192.168.5.2:59969 - "GET /v1/plugins/demo/1.0.0/download HTTP/1.1" 200 OK
INFO: 192.168.5.2:59971 - "GET /healthz HTTP/1.1" 200 OK
INFO: 192.168.5.2:59972 - "GET /app HTTP/1.1" 200 OK
INFO: 192.168.5.2:59973 - "GET /apk HTTP/1.1" 200 OK
INFO: 192.168.5.2:59974 - "GET /icon.svg HTTP/1.1" 200 OK
INFO: 192.168.5.2:59975 - "GET /manifest.webmanifest HTTP/1.1" 200 OK
INFO: 192.168.5.2:59976 - "GET /favicon.ico HTTP/1.1" 307 Temporary Redirect
INFO: 192.168.5.2:59977 - "GET /v1/me HTTP/1.1" 200 OK
INFO: 192.168.5.2:59979 - "GET /v1/settings HTTP/1.1" 200 OK
INFO: 192.168.5.2:59981 - "GET /v1/backup/info HTTP/1.1" 200 OK
INFO: 192.168.5.2:59983 - "GET /v1/plugins HTTP/1.1" 200 OK
INFO: 192.168.5.2:59985 - "GET /v1/plugins/demo/1.0.0/download HTTP/1.1" 404 Not Found
INFO: 192.168.5.2:59987 - "GET /healthz HTTP/1.1" 200 OK
INFO: 192.168.5.2:59988 - "GET /app HTTP/1.1" 200 OK
INFO: 192.168.5.2:59989 - "GET /apk HTTP/1.1" 200 OK
INFO: 192.168.5.2:59990 - "GET /icon.svg HTTP/1.1" 200 OK
INFO: 192.168.5.2:59991 - "GET /manifest.webmanifest HTTP/1.1" 200 OK
INFO: 192.168.5.2:59992 - "GET /favicon.ico HTTP/1.1" 307 Temporary Redirect
INFO: 192.168.5.2:59993 - "GET /v1/me HTTP/1.1" 200 OK
INFO: 192.168.5.2:59995 - "GET /v1/settings HTTP/1.1" 200 OK
INFO: 192.168.5.2:59997 - "GET /v1/backup/info HTTP/1.1" 200 OK
INFO: 192.168.5.2:59999 - "GET /v1/plugins HTTP/1.1" 200 OK
INFO: 192.168.5.2:60001 - "GET /v1/plugins/demo/1.0.0/download HTTP/1.1" 200 OK
INFO: 192.168.5.2:49794 - "POST /healthz HTTP/1.1" 405 Method Not Allowed
INFO: 192.168.5.2:49798 - "POST /healthz HTTP/1.1" 405 Method Not Allowed
INFO: 192.168.5.2:57362 - "POST /v1/plugins/team-defaults HTTP/1.1" 400 Bad Request
INFO: Started server process [54788]
INFO: Waiting for application startup.
INFO: Application startup complete.
INFO: Uvicorn running on http://192.168.5.2:8020 (Press CTRL+C to quit)
INFO: 192.168.5.2:59116 - "GET /healthz HTTP/1.1" 200 OK
INFO: 192.168.5.2:59121 - "POST /v1/plugins/team-defaults HTTP/1.1" 400 Bad Request
INFO: 192.168.5.2:64604 - "POST /v1/plugins/team-defaults HTTP/1.1" 200 OK
INFO: 192.168.5.2:56956 - "POST /v1/tokens HTTP/1.1" 200 OK
INFO: 192.168.5.2:56958 - "GET /v1/plugins HTTP/1.1" 200 OK
INFO: 192.168.5.2:56960 - "GET /v1/plugins/team-defaults/1.0.0/download HTTP/1.1" 200 OK
INFO: 192.168.5.2:53151 - "GET /app HTTP/1.1" 200 OK
INFO: Started server process [55972]
INFO: Waiting for application startup.
INFO: Application startup complete.
INFO: Uvicorn running on http://192.168.5.2:8020 (Press CTRL+C to quit)
INFO: 192.168.5.2:64003 - "GET /healthz HTTP/1.1" 200 OK
INFO: 192.168.5.2:64004 - "GET /app HTTP/1.1" 200 OK
INFO: 192.168.5.2:53574 - "GET /v1/connect/info HTTP/1.1" 200 OK
INFO: 192.168.5.2:53576 - "GET /v1/connect/qr?host=192.168.5.2 HTTP/1.1" 200 OK
+409
View File
@@ -0,0 +1,409 @@
#!/usr/bin/env python3
"""I-01 模型能力自动探测:每晚自动 + 异常告警方向,最小闭环。
读 ~/.dsh/settings.yaml 枚举全部分组与模型,对每个模型跑三组探测:
- 基线:一次最小对话请求(200 + 非空回复)
- 思考:按模型的 thinkingFormat/reasoningEfforts 配置发带推理参数请求,验证 reasoning 字段
- 视觉:内置 64px 纯色 PNG(base64 内嵌)问主色
铁律:脚本只读 settings.yaml(open(mode='r'),代码层面无写入路径);
任何写入需求只进报告建议,由人执行。报告不打印 API key。
产出:tools/probe-reports/probe-YYYY-MM-DD.json(机读)+ 同名 .md(人读:表格 + 与上次 diff)。
用法:
python tools/probe_models.py --all # 全量探测 + 写报告
python tools/probe_models.py --all --compare # 全量探测 + 写报告 + 打印与上次对比
python tools/probe_models.py --provider b # 只跑一组
python tools/probe_models.py --check-readonly # 自证:grep 本源码无 settings 写入路径
Windows 计划任务(每晚 02:00)示例:
schtasks /create /tn "DSH模型探测" /tr "python C:\\Users\\12914\\Desktop\\dsh\\tools\\probe_models.py --all" /sc daily /st 02:00
"""
from __future__ import annotations
import argparse
import base64
import datetime as _dt
import io
import json
import os
import struct
import sys
import time
import urllib.request
import zlib
from pathlib import Path
HERE = Path(__file__).resolve().parent
REPORT_DIR = HERE / "probe-reports"
SETTINGS = Path(os.path.expanduser("~/.dsh/settings.yaml"))
TIMEOUT = 30 # 单请求超时(秒),失败隔离:单模型失败不中断全量
RED_PNG_B64: str | None = None
def red_png_b64() -> str:
"""64px 纯红 PNG,纯标准库生成(zlib+struct),无外部文件依赖。"""
global RED_PNG_B64
if RED_PNG_B64 is None:
w = h = 64
raw = b"".join(b"\x00" + b"\xff\x00\x00" * w for _ in range(h))
def chunk(typ: bytes, data: bytes) -> bytes:
c = typ + data
return struct.pack(">I", len(data)) + c + struct.pack(">I", zlib.crc32(c) & 0xFFFFFFFF)
png = (b"\x89PNG\r\n\x1a\n"
+ chunk(b"IHDR", struct.pack(">IIBBBBB", w, h, 8, 2, 0, 0, 0))
+ chunk(b"IDAT", zlib.compress(raw))
+ chunk(b"IEND", b""))
RED_PNG_B64 = base64.b64encode(png).decode()
return RED_PNG_B64
def load_settings() -> dict:
"""只读加载 settings.yaml(本函数是全脚本唯一的 settings 打开点,mode='r')。"""
try:
import yaml # type: ignore
with open(SETTINGS, "r", encoding="utf-8") as fh:
return yaml.safe_load(fh) or {}
except ImportError:
pass
# 无 PyYAML 时的最小缩进解析:只取 providers 下 api/apiKeyEnv/baseURL/models 必要字段
import re
text = None
with open(SETTINGS, "r", encoding="utf-8") as fh:
text = fh.read()
providers: dict = {}
cur_p = cur_m = None
cur: dict = {}
for line in text.splitlines():
m = re.match(r"^ ([a-z0-9-]+):\s*$", line)
if m:
if cur_p and cur:
providers[cur_p] = cur
cur_p, cur, cur_m = m.group(1), {"models": []}, None
continue
if cur_p is None:
continue
m2 = re.match(r"^ (displayName|apiKeyEnv|api|baseURL):\s*(.+?)\s*$", line)
if m2:
cur[m2.group(1)] = m2.group(2)
continue
m3 = re.match(r"^ - id:\s*(\S+)", line)
if m3:
cur_m = {"id": m3.group(1), "input": ["text"], "compat": {}, "reasoningEfforts": None}
cur["models"].append(cur_m)
continue
if cur_m is not None:
m4 = re.match(r"^ (thinkingFormat|supportsDeveloperRole):\s*(\S+)", line)
if m4:
cur_m["compat"][m4.group(1)] = m4.group(2)
continue
m5 = re.match(r"^ input:\s*\[(.+)\]", line)
if m5:
cur_m["input"] = [x.strip() for x in m5.group(1).split(",")]
continue
if re.match(r"^ reasoningEfforts:\s*$", line):
cur_m["reasoningEfforts"] = {}
continue
m6 = re.match(r"^ (off|low|medium|high):\s*(\S+)?", line)
if m6 and isinstance(cur_m.get("reasoningEfforts"), dict):
cur_m["reasoningEfforts"][m6.group(1)] = (m6.group(2) or "").lower() == "null" and None or m6.group(2)
if cur_p and cur:
providers[cur_p] = cur
return {"llm-pi-ai": {"providers": providers}}
def iter_models(cfg: dict):
provs = ((cfg.get("llm-pi-ai") or {}).get("providers") or {})
for pname, p in provs.items():
for m in p.get("models") or []:
yield pname, p, m
def _post(url: str, key: str, payload: dict, timeout: int = TIMEOUT) -> tuple[int, dict, str]:
body = json.dumps(payload, ensure_ascii=False).encode()
req = urllib.request.Request(
url, data=body,
headers={"Content-Type": "application/json", "Authorization": f"Bearer {key}"},
method="POST",
)
t0 = time.time()
try:
with urllib.request.urlopen(req, timeout=timeout) as resp:
raw = resp.read().decode("utf-8", "replace")
return resp.status, json.loads(raw), f"{(time.time() - t0) * 1000:.0f}ms"
except Exception as exc:
ms = f"{(time.time() - t0) * 1000:.0f}ms"
code = getattr(exc, "code", "")
return -1, {"_error": f"{type(exc).__name__}: {str(exc)[:200]}", "_code": code}, ms
def probe_baseline(base: str, key: str, model: str, api: str):
if api == "openai-responses":
payload = {"model": model, "input": "只回复:OK"}
st, data, ms = _post(base.rstrip("/") + "/responses", key, payload)
text = ""
try:
out = data.get("output") or []
for item in out:
for c in item.get("content") or []:
text += c.get("text", "")
except Exception:
pass
ok = st == 200 and bool(text.strip())
return {"pass": ok, "ms": ms, "status": st,
"err": "" if ok else str(data.get("_error", data))[:200]}
payload = {"model": model, "messages": [{"role": "user", "content": "只回复:OK"}],
"max_tokens": 16}
st, data, ms = _post(base.rstrip("/") + "/chat/completions", key, payload)
text = ""
try:
text = (data.get("choices") or [{}])[0].get("message", {}).get("content", "")
except Exception:
pass
ok = st == 200 and bool((text or "").strip())
return {"pass": ok, "ms": ms, "status": st,
"err": "" if ok else str(data.get("_error", data))[:200]}
def probe_thinking(base: str, key: str, model: str, api: str, compat: dict, efforts: dict | None):
"""按 thinkingFormat 发推理参数;验证 reasoning 字段返回。无声明 → skip。"""
fmt = (compat or {}).get("thinkingFormat")
if not fmt or not efforts:
return {"pass": None, "ms": "-", "status": "-", "err": "skip: 无 thinking 声明"}
hi = (efforts or {}).get("high")
extra: dict = {}
if fmt == "zai":
extra = {"enable_thinking": True, **({"reasoning_effort": hi} if hi else {})}
elif fmt == "qwen":
extra = {"enable_thinking": True, **({"reasoning_effort": hi or "xhigh"} if True else {})}
else:
extra = {"enable_thinking": True}
if api == "openai-responses":
payload = {"model": model, "input": "5-2=3,还剩3;2*3-3=3,新买3;3+3=? 只给出数字答案。", **extra}
st, data, ms = _post(base.rstrip("/") + "/responses", key, payload)
raw = json.dumps(data, ensure_ascii=False)
has_reason = "reasoning" in raw
text = ""
try:
for item in data.get("output") or []:
for c in item.get("content") or []:
text += c.get("text", "")
except Exception:
pass
ok = st == 200 and has_reason and ("6" in text)
return {"pass": ok, "ms": ms, "status": st,
"err": "" if ok else f"reasoning={'有' if has_reason else '无'} 答={'6' in text} " + str(data.get("_error", ""))[:120]}
payload = {"model": model,
"messages": [{"role": "user", "content": "5-2=3,还剩3;2*3-3=3,新买3;3+3=? 只给出数字答案。"}],
"max_tokens": 256, **extra}
st, data, ms = _post(base.rstrip("/") + "/chat/completions", key, payload)
raw = json.dumps(data, ensure_ascii=False)
has_reason = "reasoning" in raw
text = ""
try:
text = (data.get("choices") or [{}])[0].get("message", {}).get("content", "")
except Exception:
pass
ok = st == 200 and has_reason and ("6" in text)
return {"pass": ok, "ms": ms, "status": st,
"err": "" if ok else f"reasoning={'有' if has_reason else '无'} 答={'6' in text} " + str(data.get("_error", ""))[:120]}
def probe_vision(base: str, key: str, model: str, api: str, inputs: list):
"""视觉:内嵌 64px 纯红 PNG 问主色。未声明 image → skip。"""
if "image" not in (inputs or []):
return {"pass": None, "ms": "-", "status": "-", "err": "skip: 未声明 image 输入"}
img = {"type": "image_url", "image_url": {"url": "data:image/png;base64," + red_png_b64()}}
if api == "openai-responses":
payload = {"model": model, "input": [{"role": "user", "content": [
{"type": "input_text", "text": "这张纯色图的主色是什么?只用一个词回答。"},
{"type": "input_image", "image_url": "data:image/png;base64," + red_png_b64()},
]}]}
st, data, ms = _post(base.rstrip("/") + "/responses", key, payload)
text = json.dumps(data, ensure_ascii=False)
ok = st == 200 and ("红" in text or "red" in text.lower())
return {"pass": ok, "ms": ms, "status": st,
"err": "" if ok else str(data.get("_error", text[:200]))[:200]}
payload = {"model": model, "max_tokens": 32, "messages": [
{"role": "user", "content": [
{"type": "text", "text": "这张纯色图的主色是什么?只用一个词回答。"},
img,
]}]}
st, data, ms = _post(base.rstrip("/") + "/chat/completions", key, payload)
text = ""
try:
text = (data.get("choices") or [{}])[0].get("message", {}).get("content", "")
except Exception:
pass
ok = st == 200 and ("红" in (text or "") or "red" in (text or "").lower())
return {"pass": ok, "ms": ms, "status": st,
"err": "" if ok else (str(data.get("_error", text))[:200])}
def run(provider_filter: str | None = None):
cfg = load_settings()
rows = []
for pname, p, m in iter_models(cfg):
if provider_filter and pname != provider_filter:
continue
mid = m.get("id", "?")
api = p.get("api", "openai-completions")
base = p.get("baseURL", "")
key = os.environ.get(p.get("apiKeyEnv", ""), "")
if not key:
rows.append({"provider": pname, "model": mid, "api": api,
"baseline": {"pass": None, "ms": "-", "status": "-", "err": "skip: 无 key(未设 " + p.get("apiKeyEnv", "?") + ")"},
"thinking": {"pass": None, "ms": "-", "status": "-", "err": "skip: 无 key"},
"vision": {"pass": None, "ms": "-", "status": "-", "err": "skip: 无 key"}})
continue
compat = m.get("compat") or {}
efforts = m.get("reasoningEfforts")
inputs = m.get("input") or ["text"]
try:
b = probe_baseline(base, key, mid, api)
except Exception as exc:
b = {"pass": False, "ms": "-", "status": -1, "err": f"{type(exc).__name__}: {exc}"[:200]}
try:
t = probe_thinking(base, key, mid, api, compat, efforts)
except Exception as exc:
t = {"pass": False, "ms": "-", "status": -1, "err": f"{type(exc).__name__}: {exc}"[:200]}
try:
v = probe_vision(base, key, mid, api, inputs)
except Exception as exc:
v = {"pass": False, "ms": "-", "status": -1, "err": f"{type(exc).__name__}: {exc}"[:200]}
rows.append({"provider": pname, "model": mid, "api": api,
"baseline": b, "thinking": t, "vision": v})
return rows
def verdict(row: dict) -> str:
vals = [row[k].get("pass") for k in ("baseline", "thinking", "vision")]
if all(v is True or v is None for v in vals) and any(v is True for v in vals):
skips = sum(1 for v in vals if v is None)
return "pass" if skips == 0 else "pass-skip"
if any(v is False for v in vals):
return "fail"
return "skip"
def write_reports(rows: list) -> tuple[Path, Path]:
REPORT_DIR.mkdir(parents=True, exist_ok=True)
day = _dt.date.today().isoformat()
jpath = REPORT_DIR / f"probe-{day}.json"
mpath = REPORT_DIR / f"probe-{day}.md"
payload = {"date": day, "total": len(rows),
"pass": 0, "fail": 0, "skip": 0,
"rows": [{**r, "verdict": verdict(r)} for r in rows]}
for r in payload["rows"]:
if r["verdict"].startswith("pass"):
payload["pass"] += 1
elif r["verdict"] == "fail":
payload["fail"] += 1
else:
payload["skip"] += 1
# 与上次对比(能力变化告警方向)
prev = sorted(REPORT_DIR.glob("probe-*.json"))
prev = [p for p in prev if p.name != jpath.name]
diff_lines: list[str] = []
if prev:
last = json.loads(prev[-1].read_text(encoding="utf-8"))
by_key = {(r["provider"], r["model"]): r.get("verdict") for r in last.get("rows", [])}
for r in payload["rows"]:
old = by_key.get((r["provider"], r["model"]))
if old and old != r["verdict"]:
diff_lines.append(f'- {r["provider"]}/{r["model"]}: {old} → **{r["verdict"]}**')
payload["diff_from"] = prev[-1].name if prev else None
payload["diff"] = diff_lines
jpath.write_text(json.dumps(payload, ensure_ascii=False, indent=2), encoding="utf-8")
alert = "⚠️ 能力变化告警" if diff_lines else "无能力变化"
if payload["fail"]:
alert += f'|{payload["fail"]} 个模型失败'
lines = [f"# 模型探测报告 {day}", "", f"> {alert}", "",
f"总 {payload['total']} · 通过 {payload['pass']} · 失败 {payload['fail']} · 全跳过 {payload['skip']}",
"", "| 分组 | 模型 | 基线 | 思考 | 视觉 | 结论 |",
"|---|---|---|---|---|---|"]
sym = {True: "✅", False: "❌", None: "⏭️"}
for r in payload["rows"]:
b, t, v = r["baseline"], r["thinking"], r["vision"]
lines.append(f"| {r['provider']} | {r['model']} | {sym[b['pass']]}{b['ms']} "
f"| {sym[t['pass']]}{t['ms']} | {sym[v['pass']]}{v['ms']} | {r['verdict']} |")
fails = [r for r in payload["rows"] if r["verdict"] == "fail"]
if fails:
lines += ["", "## 失败明细(错误摘要,不含 key)"]
for r in fails:
for k in ("baseline", "thinking", "vision"):
if r[k].get("pass") is False:
lines.append(f"- {r['provider']}/{r['model']} {k}: [{r[k].get('status')}] {r[k].get('err', '')[:150]}")
if diff_lines:
lines += ["", f"## 与上次对比({payload['diff_from']})"] + diff_lines
else:
lines += ["", "## 与上次对比", "无变化" if prev else "(首次报告,无基线)"]
lines += ["", "## 配置建议(需人执行,本脚本不写配置)",
"失败/变化的模型请对照 2026-09-06 手动结论复核后再改 settings.yaml。"]
mpath.write_text("\n".join(lines) + "\n", encoding="utf-8")
return jpath, mpath
def check_readonly() -> int:
"""自证无 settings 写入路径:源码中不得出现写模式 open/settings 写入调用。"""
src = Path(__file__).read_text(encoding="utf-8")
bad = []
in_checker = False
for i, line in enumerate(src.splitlines(), 1):
s = line.strip()
if s.startswith("def check_readonly"):
in_checker = True
elif in_checker and s.startswith("def "):
in_checker = False
if in_checker:
continue # 跳过检查器自身的字面量(否则自指误报)
if s.startswith("#") or s.startswith('"""') or s.startswith("'''"):
continue
if "SETTINGS" in line and ("\"w\"" in line or "'w'" in line or "\"a\"" in line or "'a'" in line):
bad.append((i, line.strip()))
if "settings.yaml" in line.lower() and (".write" in line or "yaml.dump" in line or "safe_dump" in line):
bad.append((i, line.strip()))
if bad:
print("发现疑似写入路径:")
for i, l in bad:
print(f" L{i}: {l}")
return 1
opens = [l.strip() for l in src.splitlines() if "open(SETTINGS" in l]
print("settings 打开点(应仅 mode='r'):")
for l in opens:
print(f" {l}")
print("readonly-check: PASS(无写入路径)")
return 0
def main(argv: list[str]) -> int:
ap = argparse.ArgumentParser(description="DSH 模型能力自动探测(只报告不写配置)")
ap.add_argument("--all", action="store_true", help="全量探测")
ap.add_argument("--provider", default="", help="只跑指定分组")
ap.add_argument("--compare", action="store_true", help="打印与上次报告的对比")
ap.add_argument("--check-readonly", action="store_true", help="自证无 settings 写入路径")
args = ap.parse_args(argv)
if args.check_readonly:
return check_readonly()
if not args.all and not args.provider:
ap.print_help()
return 2
rows = run(args.provider or None)
jp, mp = write_reports(rows)
print(f"报告:{jp}\n报告:{mp}")
if args.compare:
data = json.loads(jp.read_text(encoding="utf-8"))
print(f"对比基线:{data.get('diff_from') or '无(首次)'}")
for d in data.get("diff", []) or ["无变化"]:
print(f" {d}")
fails = sum(1 for r in rows if verdict(r) == "fail")
return 0
if __name__ == "__main__":
raise SystemExit(main(sys.argv[1:]))